@booklib/skills 1.8.0 → 1.10.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.
@@ -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.8.0",
3
+ "version": "1.10.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"
@@ -0,0 +1,42 @@
1
+ ---
2
+ description: Always-on Clean Code standards from Robert C. Martin. Apply to all code regardless of language.
3
+ ---
4
+
5
+ # Clean Code Standards
6
+
7
+ Apply these principles from *Clean Code* (Robert C. Martin) to all code you write or review.
8
+
9
+ ## Names
10
+
11
+ - Use intention-revealing names — if the name needs a comment to explain it, rename it
12
+ - Avoid abbreviations unless universally understood (`url`, `id`, `ctx` are fine; `mgr`, `proc` are not)
13
+ - Classes and types are nouns; methods and functions are verb phrases
14
+ - Avoid noise words that add no meaning: `Manager`, `Data`, `Info`, `Handler` in type names usually signal a missing concept
15
+ - Boolean variables and functions read as assertions: `isEnabled`, `hasPermission`, `canRetry`
16
+
17
+ ## Functions
18
+
19
+ - Functions do one thing; if you can extract a meaningful sub-function with a non-trivial name, the function does too much
20
+ - Keep functions short — aim for under 20 lines; over 40 is a smell
21
+ - Max 3 parameters; group related parameters into a value object when you need more
22
+ - Avoid boolean flag parameters — they signal the function does two things; split it
23
+ - No side effects in functions that return values
24
+
25
+ ## Comments
26
+
27
+ - Comments compensate for failure to express intent in code — prefer renaming over commenting
28
+ - Never commit commented-out code; use version control
29
+ - `// TODO:` is acceptable only when tracked in an issue; delete stale TODOs
30
+ - Document *why*, not *what* — the code shows what; the comment explains a non-obvious reason
31
+
32
+ ## Structure
33
+
34
+ - Group related code together; put high-level concepts at the top, details below
35
+ - Functions in a file should be ordered so callers appear before callees
36
+ - Avoid deep nesting — if `if`/`else` chains exceed 3 levels, extract or invert conditions
37
+
38
+ ## Error handling
39
+
40
+ - Prefer exceptions over error codes for exceptional conditions
41
+ - Handle errors at the appropriate abstraction level — don't catch and re-throw unless you add context
42
+ - Never swallow exceptions silently; at minimum log before ignoring
@@ -0,0 +1,42 @@
1
+ ---
2
+ description: Always-on Effective Java standards from Joshua Bloch. Apply when writing or reviewing Java code.
3
+ ---
4
+
5
+ # Effective Java Standards
6
+
7
+ Apply these principles from *Effective Java* (Joshua Bloch, 3rd edition) to all Java code.
8
+
9
+ ## Object creation
10
+
11
+ - Prefer static factory methods over constructors — they have names, can return subtypes, and can cache instances
12
+ - Use a builder when a constructor or factory would have more than 3 parameters
13
+ - Never create unnecessary objects; reuse `String` literals, prefer `Boolean.valueOf(x)` over `new Boolean(x)`
14
+
15
+ ## Classes and mutability
16
+
17
+ - Minimize mutability — all fields `private final` by default; add setters only when needed
18
+ - Favor composition over inheritance; explicitly document classes designed for extension or mark them `final`
19
+ - Override `@Override` on every method that overrides or implements; the annotation catches typos at compile time
20
+
21
+ ## Methods
22
+
23
+ - Validate parameters at entry; throw `IllegalArgumentException`, `NullPointerException`, or `IndexOutOfBoundsException` with a message
24
+ - Return empty collections or `Optional`, never `null`, from methods with a non-primitive return type
25
+ - Use `Optional` for return values that may be absent; don't use it for fields or parameters
26
+
27
+ ## Exceptions
28
+
29
+ - Use checked exceptions for recoverable conditions; unchecked (`RuntimeException`) for programming errors
30
+ - Prefer standard exceptions: `IllegalArgumentException`, `IllegalStateException`, `UnsupportedOperationException`, `NullPointerException`
31
+ - Don't swallow exceptions — at minimum log with context before ignoring; never `catch (Exception e) {}`
32
+
33
+ ## Generics and collections
34
+
35
+ - Use generic types and methods; avoid raw types (`List` → `List<E>`)
36
+ - Use bounded wildcards (`? extends T` for producers, `? super T` for consumers — PECS)
37
+ - Prefer `List` over arrays for type safety; use arrays only for performance-sensitive low-level code
38
+
39
+ ## Concurrency
40
+
41
+ - Synchronize all accesses to shared mutable state; prefer `java.util.concurrent` utilities over `synchronized`
42
+ - Prefer immutable objects and thread confinement over shared mutable state
@@ -0,0 +1,37 @@
1
+ ---
2
+ description: Always-on Effective Kotlin standards from Marcin Moskała. Apply when writing or reviewing Kotlin code.
3
+ ---
4
+
5
+ # Effective Kotlin Standards
6
+
7
+ Apply these principles from *Effective Kotlin* (Marcin Moskała, 2nd edition) to all Kotlin code.
8
+
9
+ ## Safety
10
+
11
+ - Prefer `val` over `var`; use `var` only when mutation is genuinely required
12
+ - Use nullable types explicitly (`T?`); avoid `!!` — narrow with `?.`, `?:`, `let`, or `checkNotNull()`
13
+ - Use `require()` for argument preconditions and `check()` for state preconditions at function entry
14
+
15
+ ## Functions
16
+
17
+ - Use named arguments when passing more than 2 parameters, especially when they share the same type
18
+ - Use default arguments instead of overloads for optional behavior
19
+ - Prefer extension functions over utility classes for domain operations on a type you own
20
+
21
+ ## Classes and design
22
+
23
+ - Use data classes for value objects — they get `equals`, `hashCode`, `copy`, and `toString` for free
24
+ - Prefer sealed classes over open hierarchies when the set of subtypes is finite and known
25
+ - Use `object` for singletons, `companion object` for factory methods and class-level constants
26
+
27
+ ## Collections
28
+
29
+ - Use functional operators (`map`, `filter`, `fold`, `groupBy`) over manual loops
30
+ - Prefer `Sequence` for large collections or multi-step pipelines — avoids intermediate lists
31
+ - Use `buildList { }` / `buildMap { }` instead of a mutable variable followed by `.toList()`
32
+
33
+ ## Coroutines
34
+
35
+ - Launch coroutines in a structured `CoroutineScope`; never use `GlobalScope` in production
36
+ - Use `withContext(Dispatchers.IO)` for blocking I/O; never block the main/UI thread
37
+ - Prefer `Flow` over callbacks for asynchronous streams; use `StateFlow` for observable state
@@ -0,0 +1,38 @@
1
+ ---
2
+ description: Always-on Effective Python standards from Brett Slatkin. Apply when writing or reviewing Python code.
3
+ ---
4
+
5
+ # Effective Python Standards
6
+
7
+ Apply these principles from *Effective Python* (Brett Slatkin, 3rd edition) to all Python code.
8
+
9
+ ## Pythonic style
10
+
11
+ - Use `enumerate()` over `range(len(...))` for indexed iteration
12
+ - Use f-strings for interpolation; avoid `%` formatting and `.format()`
13
+ - Prefer unpacking over indexing: `first, *rest = items` instead of `items[0]` and `items[1:]`
14
+ - Use `zip()` to iterate two sequences together; use `zip(strict=True)` when lengths must match
15
+
16
+ ## Data structures
17
+
18
+ - Use `list` for ordered mutable sequences, `tuple` for immutable positional data, `set` for membership tests
19
+ - Use `collections.defaultdict` or `Counter` instead of manual dict initialization
20
+ - Prefer `dataclasses` over plain dicts or namedtuples for structured data with methods
21
+
22
+ ## Functions
23
+
24
+ - Use keyword-only arguments (`def f(a, *, b)`) for optional parameters that benefit from names at the call site
25
+ - Never use mutable default arguments — use `None` and assign inside the function body
26
+ - Prefer generator expressions `(x for x in ...)` over list comprehensions when you don't need the full list in memory
27
+
28
+ ## Type annotations
29
+
30
+ - Annotate all public functions and class attributes
31
+ - Use `X | None` (Python 3.10+) or `Optional[X]` for nullable types; never return `None` silently from a typed function
32
+ - Avoid `Any` except at system boundaries (external APIs, deserialized JSON)
33
+
34
+ ## Error handling
35
+
36
+ - Catch specific exception types; never use bare `except:`
37
+ - Use `contextlib.suppress(ExceptionType)` for intentionally ignored exceptions — makes the intent explicit
38
+ - Use `__all__` in every module to declare its public API
@@ -0,0 +1,37 @@
1
+ ---
2
+ description: Always-on Rust standards from Programming with Rust and Rust in Action. Apply when writing or reviewing Rust code.
3
+ ---
4
+
5
+ # Rust Standards
6
+
7
+ Apply these principles from *Programming with Rust* (Donis Marshall) and *Rust in Action* (Tim McNamara) to all Rust code.
8
+
9
+ ## Ownership and borrowing
10
+
11
+ - Use owned values (`String`, `Vec<T>`) for data you own; borrow (`&str`, `&[T]`) when you only need to read
12
+ - Prefer passing `&T` or `&mut T` over cloning; clone only when ownership transfer is required
13
+ - Use `Rc<T>` for single-threaded shared ownership, `Arc<T>` for multi-threaded; use `RefCell<T>` / `Mutex<T>` for interior mutability
14
+
15
+ ## Error handling
16
+
17
+ - Return `Result<T, E>` from all fallible functions; propagate with `?`
18
+ - Use `thiserror` to define library errors with `#[derive(Error)]`; use `anyhow` for application-level error context
19
+ - Avoid `.unwrap()` in library code; use `.expect("clear message")` in application code where panicking is intentional
20
+
21
+ ## Types and traits
22
+
23
+ - Use `struct` for data, `enum` for variants with payloads, `trait` for shared behaviour
24
+ - Implement standard traits where appropriate: `Debug` always, `Display` for user-facing types, `Clone`, `PartialEq`, `Hash` as needed
25
+ - Use `impl Trait` in argument position for static dispatch; `Box<dyn Trait>` only when you need runtime dispatch
26
+
27
+ ## Idiomatic patterns
28
+
29
+ - Use `Iterator` adapters (`map`, `filter`, `flat_map`, `collect`) over manual loops — the compiler optimizes them equally
30
+ - Use `Option` methods (`map`, `unwrap_or`, `and_then`, `ok_or`) over `match` for simple transformations
31
+ - Use `if let` for single-variant matching; use `match` for exhaustive handling
32
+
33
+ ## Naming and style
34
+
35
+ - Types: `PascalCase`; functions, variables, modules: `snake_case`; constants and statics: `SCREAMING_SNAKE_CASE`
36
+ - Lifetime names: `'a`, `'b` for simple cases; descriptive names (`'arena`, `'cx`) for complex lifetimes
37
+ - Mark all public items in a library crate with doc comments (`///`)
@@ -0,0 +1,42 @@
1
+ ---
2
+ description: Always-on Effective TypeScript standards from Dan Vanderkam. Apply when writing or reviewing TypeScript or JavaScript code.
3
+ ---
4
+
5
+ # Effective TypeScript Standards
6
+
7
+ Apply these principles from *Effective TypeScript* (Dan Vanderkam, 2nd edition) to all TypeScript code.
8
+
9
+ ## Types
10
+
11
+ - Prefer union types over enums for simple sets of values: `type Direction = 'N' | 'S' | 'E' | 'W'`
12
+ - Use `interface` for extensible object shapes that others may augment; use `type` for unions, intersections, and computed types
13
+ - Avoid `any`; use `unknown` when the type is genuinely unknown, then narrow with guards before use
14
+ - Avoid type assertions (`as T`) — prefer type narrowing, overloads, or generics
15
+
16
+ ## Type inference
17
+
18
+ - Let TypeScript infer return types on internal functions; explicitly annotate public API return types
19
+ - Annotate a variable at declaration if it cannot be initialized immediately
20
+ - Use `as const` to preserve literal types; don't use it just to silence widening errors
21
+
22
+ ## Null safety
23
+
24
+ - Enable `strict` mode (which includes `strictNullChecks`) — treat every `T | undefined` as requiring explicit handling
25
+ - Use optional chaining `?.` and nullish coalescing `??` over `&&` and `||` chains
26
+ - Never use non-null assertion (`!`) — narrow instead
27
+
28
+ ## Structural typing
29
+
30
+ - TypeScript checks shapes, not nominal types — understand that duck typing applies
31
+ - Use discriminated unions with a `kind` or `type` literal field for exhaustive `switch` / narrowing
32
+ - Avoid class hierarchies for data shapes — prefer interfaces and composition
33
+
34
+ ## Generics
35
+
36
+ - Constrain generics to the minimum required: `<T extends string>` not `<T>`
37
+ - Use descriptive generic names for complex types (`<TItem, TKey>`) and single letters for simple transforms (`<T>`, `<K, V>`)
38
+
39
+ ## Functions
40
+
41
+ - Prefer function overloads over union parameter types to express the relationship between input and output
42
+ - Keep functions pure where possible; extract side effects to the call site