@booklib/skills 1.9.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.
- package/AGENTS.md +111 -53
- package/CONTRIBUTING.md +147 -0
- package/PLAN.md +20 -92
- package/bin/skills.js +105 -14
- package/package.json +1 -1
- package/rules/common/clean-code.md +42 -0
- package/rules/java/effective-java.md +42 -0
- package/rules/kotlin/effective-kotlin.md +37 -0
- package/rules/python/effective-python.md +38 -0
- package/rules/rust/rust.md +37 -0
- package/rules/typescript/effective-typescript.md +42 -0
package/AGENTS.md
CHANGED
|
@@ -1,108 +1,166 @@
|
|
|
1
1
|
# Agent Integration
|
|
2
2
|
|
|
3
|
-
How to install and use booklib
|
|
3
|
+
How to install and use `@booklib/skills` with different AI coding assistants.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## What gets installed
|
|
6
|
+
|
|
7
|
+
Each install creates up to three things in your project (or globally with `--global`):
|
|
8
|
+
|
|
9
|
+
| Directory | Contents | Purpose |
|
|
10
|
+
|-----------|----------|---------|
|
|
11
|
+
| `.claude/skills/` | 22 book-grounded skills | Auto-triggered by context |
|
|
12
|
+
| `.claude/commands/` | 22 slash commands | Explicit invocation (`/effective-python`) |
|
|
13
|
+
| `.claude/agents/` | 8 reviewer agents | Autonomous end-to-end reviews |
|
|
6
14
|
|
|
7
|
-
|
|
15
|
+
The fastest way to install is by **profile** — one command installs the right skills, commands, and agent for your language or domain:
|
|
8
16
|
|
|
9
17
|
```bash
|
|
10
|
-
npx skills add
|
|
18
|
+
npx @booklib/skills add --profile=python # Python
|
|
19
|
+
npx @booklib/skills add --profile=ts # TypeScript / JavaScript
|
|
20
|
+
npx @booklib/skills add --profile=jvm # Java + Kotlin + Spring Boot
|
|
21
|
+
npx @booklib/skills add --profile=rust # Rust
|
|
22
|
+
npx @booklib/skills add --profile=architecture # DDD, microservices, system design
|
|
23
|
+
npx @booklib/skills add --profile=data # Pipelines, ETL, storage
|
|
24
|
+
npx @booklib/skills add --profile=ui # UI design, charts, animations
|
|
25
|
+
npx @booklib/skills add --profile=lean # Lean Startup practices
|
|
26
|
+
npx @booklib/skills add --profile=core # Routing + general quality (good default)
|
|
27
|
+
|
|
28
|
+
# Or install everything
|
|
29
|
+
npx @booklib/skills add --all
|
|
11
30
|
```
|
|
12
31
|
|
|
13
|
-
|
|
32
|
+
Add `--global` to any command to install to `~/.claude/` instead of the project directory.
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Claude Code
|
|
37
|
+
|
|
38
|
+
### Install
|
|
14
39
|
|
|
15
40
|
```bash
|
|
16
|
-
|
|
41
|
+
# Recommended — install by profile
|
|
42
|
+
npx @booklib/skills add --profile=ts --global
|
|
43
|
+
|
|
44
|
+
# Everything
|
|
45
|
+
npx @booklib/skills add --all --global
|
|
46
|
+
|
|
47
|
+
# Single skill
|
|
48
|
+
npx @booklib/skills add effective-typescript
|
|
17
49
|
```
|
|
18
50
|
|
|
19
|
-
|
|
51
|
+
### How skills trigger
|
|
52
|
+
|
|
53
|
+
Claude Code reads skills from `.claude/skills/` (project) or `~/.claude/skills/` (global) and loads them based on the `description` field in each `SKILL.md`. Skills activate automatically when the context matches.
|
|
54
|
+
|
|
55
|
+
### Slash commands
|
|
20
56
|
|
|
21
|
-
|
|
57
|
+
Each profile also installs companion slash commands:
|
|
22
58
|
|
|
23
59
|
```
|
|
24
|
-
/
|
|
60
|
+
/effective-typescript # runs the effective-typescript skill explicitly
|
|
61
|
+
/clean-code-reviewer # reviews against Clean Code principles
|
|
62
|
+
/skill-router # routes automatically to the best skill
|
|
25
63
|
```
|
|
26
64
|
|
|
27
|
-
|
|
65
|
+
### Agents
|
|
28
66
|
|
|
29
|
-
|
|
67
|
+
Agents are autonomous reviewers installed to `.claude/agents/`. Invoke with `@`:
|
|
30
68
|
|
|
31
|
-
```
|
|
32
|
-
|
|
69
|
+
```
|
|
70
|
+
@booklib-reviewer # auto-routes to the right skill
|
|
71
|
+
@python-reviewer # Python: effective-python + asyncio + scraping
|
|
72
|
+
@ts-reviewer # TypeScript: effective-typescript + clean-code
|
|
73
|
+
@jvm-reviewer # Java/Kotlin: effective-java + kotlin + spring-boot
|
|
74
|
+
@rust-reviewer # Rust: programming-with-rust + rust-in-action
|
|
75
|
+
@architecture-reviewer # DDD + microservices + system-design + DDIA
|
|
76
|
+
@data-reviewer # data-intensive-patterns + data-pipelines
|
|
77
|
+
@ui-reviewer # refactoring-ui + storytelling + animation
|
|
33
78
|
```
|
|
34
79
|
|
|
35
|
-
|
|
80
|
+
### Skill suggestion hook
|
|
36
81
|
|
|
37
|
-
|
|
82
|
+
`add --all` also installs a `UserPromptSubmit` hook (`booklib-suggest.js`) that detects when you're asking to review code and suggests the relevant skill — without firing on every message.
|
|
38
83
|
|
|
39
|
-
|
|
40
|
-
|
|
84
|
+
To activate it, add the hook config to your Claude Code settings:
|
|
85
|
+
```json
|
|
86
|
+
{
|
|
87
|
+
"UserPromptSubmit": [{
|
|
88
|
+
"hooks": [{ "type": "command", "command": "node \"$HOME/.claude/booklib-suggest.js\"" }]
|
|
89
|
+
}]
|
|
90
|
+
}
|
|
41
91
|
```
|
|
42
92
|
|
|
43
|
-
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## Cursor
|
|
44
96
|
|
|
45
|
-
|
|
97
|
+
Cursor reads rules from `.cursor/rules/`. Use `--target=cursor` to install there:
|
|
46
98
|
|
|
47
99
|
```bash
|
|
48
|
-
|
|
49
|
-
|
|
100
|
+
# Install to Cursor only
|
|
101
|
+
npx @booklib/skills add --profile=ts --target=cursor
|
|
50
102
|
|
|
51
|
-
|
|
103
|
+
# Install to both Claude Code and Cursor
|
|
104
|
+
npx @booklib/skills add --profile=ts --target=all
|
|
52
105
|
|
|
53
|
-
|
|
54
|
-
|
|
106
|
+
# Single skill to Cursor
|
|
107
|
+
npx @booklib/skills add effective-typescript --target=cursor
|
|
55
108
|
```
|
|
56
109
|
|
|
57
|
-
|
|
110
|
+
Skills are written as `.cursor/rules/<skill-name>.md`. Cursor loads them in Agent mode. Agents are not applicable to Cursor (no native agent system).
|
|
58
111
|
|
|
59
|
-
|
|
112
|
+
---
|
|
60
113
|
|
|
61
|
-
|
|
62
|
-
|
|
114
|
+
## GitHub Copilot (VS Code)
|
|
115
|
+
|
|
116
|
+
Copilot Chat doesn't load `.claude/skills/` natively. Reference skills explicitly in chat:
|
|
117
|
+
|
|
118
|
+
```
|
|
119
|
+
Using the effective-typescript skill, review this file for type safety issues.
|
|
120
|
+
Apply the clean-code-reviewer skill to the current diff.
|
|
63
121
|
```
|
|
64
122
|
|
|
65
|
-
|
|
123
|
+
Or install globally and reference by name — some Copilot extensions pick up `.claude/skills/` as context.
|
|
66
124
|
|
|
67
|
-
|
|
125
|
+
---
|
|
68
126
|
|
|
69
|
-
|
|
127
|
+
## Windsurf
|
|
128
|
+
|
|
129
|
+
Install into your project:
|
|
70
130
|
|
|
71
131
|
```bash
|
|
72
|
-
npx skills add
|
|
132
|
+
npx @booklib/skills add --profile=ts
|
|
73
133
|
```
|
|
74
134
|
|
|
75
|
-
|
|
135
|
+
Skills go to `.claude/skills/`. In Windsurf's Cascade mode, reference a skill by name or use `@booklib-reviewer` if agents are supported. The `skill-router` skill selects the right skill automatically when you describe your task.
|
|
76
136
|
|
|
77
|
-
|
|
78
|
-
Route this task to the right skill, then apply it: [your request]
|
|
79
|
-
```
|
|
137
|
+
---
|
|
80
138
|
|
|
81
|
-
|
|
139
|
+
## Supported platforms
|
|
82
140
|
|
|
83
|
-
|
|
141
|
+
| Platform | Skills | Commands | Agents | Auto-trigger |
|
|
142
|
+
|----------|--------|----------|--------|--------------|
|
|
143
|
+
| Claude Code | `.claude/skills/` | `.claude/commands/` | `.claude/agents/` | Yes |
|
|
144
|
+
| Cursor | `.cursor/rules/` (`--target=cursor`) | — | — | Partial |
|
|
145
|
+
| GitHub Copilot | Manual reference | — | — | No |
|
|
146
|
+
| Windsurf | `.claude/skills/` | — | Partial | Partial |
|
|
84
147
|
|
|
85
|
-
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
## Manual installation
|
|
86
151
|
|
|
87
152
|
```bash
|
|
88
|
-
# Single skill
|
|
89
|
-
cp -r skills/effective-kotlin /path/to
|
|
153
|
+
# Single skill to any path
|
|
154
|
+
cp -r skills/effective-kotlin /path/to/.claude/skills/
|
|
90
155
|
|
|
91
156
|
# All skills
|
|
92
|
-
cp -r skills/* /path/to
|
|
157
|
+
cp -r skills/* /path/to/.claude/skills/
|
|
93
158
|
```
|
|
94
159
|
|
|
95
|
-
Any agent that reads `.claude/skills/`
|
|
96
|
-
|
|
97
|
-
## Supported agents
|
|
160
|
+
Any agent that reads `.claude/skills/` picks them up automatically.
|
|
98
161
|
|
|
99
|
-
|
|
100
|
-
|-------|-------------|--------------|----------------|
|
|
101
|
-
| Claude Code | `~/.claude/skills/` or `.claude/skills/` | Yes | `/skill-name` |
|
|
102
|
-
| Cursor | `.claude/skills/` | Partial | Reference by name |
|
|
103
|
-
| GitHub Copilot | `~/.claude/skills/` | No | Reference by name |
|
|
104
|
-
| Windsurf | `.claude/skills/` | Partial | Reference by name |
|
|
162
|
+
---
|
|
105
163
|
|
|
106
|
-
## Requesting support for a new
|
|
164
|
+
## Requesting support for a new platform
|
|
107
165
|
|
|
108
|
-
Open an issue titled **"
|
|
166
|
+
Open an issue titled **"Platform Support: [Name]"** and describe how the platform loads context files. We'll add installation instructions here.
|
package/CONTRIBUTING.md
CHANGED
|
@@ -135,6 +135,153 @@ PR checklist:
|
|
|
135
135
|
- [ ] Pass rate ≥ 80% and delta ≥ 20pp in results.json
|
|
136
136
|
- [ ] README.md skills table updated
|
|
137
137
|
|
|
138
|
+
## Adding an Agent
|
|
139
|
+
|
|
140
|
+
An agent is a multi-step autonomous reviewer that orchestrates one or more skills. If you are packaging a single book's principles, write a skill. If you need to combine multiple skills, detect code patterns to route between them, or run a full review pipeline across a whole codebase, write an agent.
|
|
141
|
+
|
|
142
|
+
| Write a skill when... | Write an agent when... |
|
|
143
|
+
|-----------------------|------------------------|
|
|
144
|
+
| You are packaging one book's principles | You need two or more skills applied together |
|
|
145
|
+
| The logic is a single lens on code | You need routing logic (detect language → pick skill) |
|
|
146
|
+
| Instructions fit in one SKILL.md | You need a multi-step process (diff → detect → review → output) |
|
|
147
|
+
|
|
148
|
+
### 1. Create the file
|
|
149
|
+
|
|
150
|
+
Agents live in a flat directory at the repo root:
|
|
151
|
+
|
|
152
|
+
```
|
|
153
|
+
agents/<agent-name>.md
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
The filename must be lowercase and hyphen-separated. It does not need a matching folder — unlike skills, agents have no `examples/` or `evals/` subdirectories.
|
|
157
|
+
|
|
158
|
+
### 2. Write the frontmatter
|
|
159
|
+
|
|
160
|
+
Every agent file starts with YAML frontmatter:
|
|
161
|
+
|
|
162
|
+
```markdown
|
|
163
|
+
---
|
|
164
|
+
name: agent-name
|
|
165
|
+
description: >
|
|
166
|
+
When to invoke this agent and what it does. Include language names,
|
|
167
|
+
domain terms, and trigger conditions. Claude Code uses this field
|
|
168
|
+
for auto-invocation, so make it specific. Max 1024 characters.
|
|
169
|
+
tools: ["Read", "Grep", "Glob", "Bash"]
|
|
170
|
+
model: sonnet
|
|
171
|
+
---
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
**Required fields:**
|
|
175
|
+
|
|
176
|
+
- `name` — lowercase, hyphens only, matches filename exactly (without `.md`)
|
|
177
|
+
- `description` — used by Claude Code to decide when to invoke the agent automatically; include what it does, which skills it applies, and when to use it over alternatives
|
|
178
|
+
- `tools` — list of Claude Code tools the agent may call; `["Read", "Grep", "Glob", "Bash"]` covers most reviewers
|
|
179
|
+
- `model` — controls cost and capability (see model selection below)
|
|
180
|
+
|
|
181
|
+
### 3. Write the body
|
|
182
|
+
|
|
183
|
+
A good agent body has five parts:
|
|
184
|
+
|
|
185
|
+
**Opening sentence** — one sentence identifying what the agent is, which books it draws from, and its scope.
|
|
186
|
+
|
|
187
|
+
**Process** — numbered steps the agent follows every time it runs:
|
|
188
|
+
|
|
189
|
+
1. **Get the scope** — how to determine what to review (e.g., `git diff HEAD`, specific files passed by the user, or a directory scan)
|
|
190
|
+
2. **Detect signals** — bash commands or grep patterns that route to the right skill(s)
|
|
191
|
+
3. **Apply skill(s)** — one `### Step N` section per skill, each with `HIGH`/`MEDIUM`/`LOW` focus areas
|
|
192
|
+
4. **Output** — the standard output format
|
|
193
|
+
|
|
194
|
+
**Detection table** — a Markdown table mapping code signals to skills:
|
|
195
|
+
|
|
196
|
+
```markdown
|
|
197
|
+
| Code contains | Apply |
|
|
198
|
+
|---------------|-------|
|
|
199
|
+
| `async def`, `await`, `asyncio` | `using-asyncio-python` |
|
|
200
|
+
| `BeautifulSoup`, `scrapy` | `web-scraping-python` |
|
|
201
|
+
| General Python | `effective-python` |
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
**Per-skill focus areas** — for each skill applied, list what to look for under `HIGH`, `MEDIUM`, and `LOW` headings. Pull these from the skills' own SKILL.md files, but trim to what is relevant for this agent's scope.
|
|
205
|
+
|
|
206
|
+
**Output format** — end the body with the standard output block:
|
|
207
|
+
|
|
208
|
+
```markdown
|
|
209
|
+
### Step N — Output format
|
|
210
|
+
|
|
211
|
+
```
|
|
212
|
+
**Skills applied:** `skill-name(s)`
|
|
213
|
+
**Scope:** [files reviewed]
|
|
214
|
+
|
|
215
|
+
### HIGH
|
|
216
|
+
- `file:line` — finding
|
|
217
|
+
|
|
218
|
+
### MEDIUM
|
|
219
|
+
- `file:line` — finding
|
|
220
|
+
|
|
221
|
+
### LOW
|
|
222
|
+
- `file:line` — finding
|
|
223
|
+
|
|
224
|
+
**Summary:** X HIGH, Y MEDIUM, Z LOW findings.
|
|
225
|
+
```
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
### 4. Choose the right model
|
|
229
|
+
|
|
230
|
+
| Model | When to use |
|
|
231
|
+
|-------|-------------|
|
|
232
|
+
| `haiku` | Fast, cheap; use for simple or narrow tasks with a single skill and little routing logic |
|
|
233
|
+
| `sonnet` | Default for most reviewers; handles multi-skill routing and structured output well |
|
|
234
|
+
| `opus` | Only for architecture or reasoning-heavy agents where depth matters more than cost (e.g., `architecture-reviewer`) |
|
|
235
|
+
|
|
236
|
+
When in doubt, use `sonnet`.
|
|
237
|
+
|
|
238
|
+
### 5. Follow naming conventions
|
|
239
|
+
|
|
240
|
+
| Pattern | Examples | Use for |
|
|
241
|
+
|---------|----------|---------|
|
|
242
|
+
| `<language>-reviewer` | `python-reviewer`, `jvm-reviewer`, `ts-reviewer` | Language-cluster agents combining all relevant skills for a language |
|
|
243
|
+
| `<domain>-reviewer` | `architecture-reviewer`, `data-reviewer`, `ui-reviewer` | Domain-cluster agents cutting across languages |
|
|
244
|
+
| Descriptive name | `booklib-reviewer` | Meta or router agents that don't fit a single language or domain |
|
|
245
|
+
|
|
246
|
+
### 6. Installation
|
|
247
|
+
|
|
248
|
+
Agents install to `.claude/agents/` alongside skills:
|
|
249
|
+
|
|
250
|
+
```bash
|
|
251
|
+
# Install one agent
|
|
252
|
+
npx skills add booklib-ai/skills --agent=python-reviewer
|
|
253
|
+
|
|
254
|
+
# Install everything (skills + agents)
|
|
255
|
+
npx skills add booklib-ai/skills --all
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
Once installed, Claude Code reads the agent's `description` field and auto-invokes it when a matching request arrives — no slash command needed.
|
|
259
|
+
|
|
260
|
+
### 7. No eval system (yet)
|
|
261
|
+
|
|
262
|
+
There is no `evals/` system for agents. Instead:
|
|
263
|
+
|
|
264
|
+
- Make the `description` accurate — it controls when the agent auto-invokes
|
|
265
|
+
- Check that every `### Step N` section has a clear, testable action
|
|
266
|
+
- Test manually: install the agent locally and run it against a real codebase
|
|
267
|
+
|
|
268
|
+
### 8. Submit a PR
|
|
269
|
+
|
|
270
|
+
```bash
|
|
271
|
+
git checkout -b agent/agent-name
|
|
272
|
+
git add agents/agent-name.md
|
|
273
|
+
git commit -m "feat: add agent-name agent"
|
|
274
|
+
gh pr create --title "feat: add agent-name agent" --body "..."
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
PR checklist:
|
|
278
|
+
- [ ] Filename matches `name` in frontmatter
|
|
279
|
+
- [ ] `description` is under 1024 characters and describes when to invoke it
|
|
280
|
+
- [ ] `model` is appropriate for the agent's complexity
|
|
281
|
+
- [ ] Process steps are numbered and each has a clear action
|
|
282
|
+
- [ ] Detection table covers the signals the agent handles
|
|
283
|
+
- [ ] Output format section matches the standard `HIGH`/`MEDIUM`/`LOW` format
|
|
284
|
+
|
|
138
285
|
## Requesting a skill
|
|
139
286
|
|
|
140
287
|
Open an issue titled **"Skill Request: [Book Name]"** and describe why the book would make a good skill. Community members can then pick it up.
|
package/PLAN.md
CHANGED
|
@@ -1,100 +1,28 @@
|
|
|
1
1
|
# Implementation Plan
|
|
2
2
|
|
|
3
|
-
Current version: **1.
|
|
4
|
-
Next release: **1.9.0**
|
|
3
|
+
Current version: **1.10.0**
|
|
5
4
|
|
|
6
|
-
##
|
|
5
|
+
## Completed in 1.9.0
|
|
7
6
|
|
|
8
|
-
|
|
7
|
+
- `skills agents` list command
|
|
8
|
+
- Cursor support (`--target=cursor` / `--target=all`)
|
|
9
|
+
- Hooks: `hooks/suggest.js` + `hooks/hooks.json`
|
|
10
|
+
- README overhaul (three-tier architecture)
|
|
11
|
+
- AGENTS.md rewrite
|
|
9
12
|
|
|
10
|
-
|
|
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
|
-
```
|
|
13
|
+
## Completed in 1.10.0
|
|
18
14
|
|
|
19
|
-
|
|
15
|
+
- Rules system: `rules/{language}/*.md` always-on standards files
|
|
16
|
+
- `skills add --rules` / `skills add --rules=<language>` installs to `.claude/rules/`
|
|
17
|
+
- `skills rules` list command
|
|
18
|
+
- `skills add --hooks` standalone flag (previously hooks only installed via `--all`)
|
|
19
|
+
- CONTRIBUTING.md: "Adding an Agent" section
|
|
20
|
+
- `--all` now also installs all rules
|
|
20
21
|
|
|
21
|
-
|
|
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
|
|
22
|
+
## Possible next steps
|
|
34
23
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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
|
|
24
|
+
- `skills add --profile=<name> --rules` to install profile + relevant rules together
|
|
25
|
+
- Profile-aware rules: each profile installs matching rules automatically
|
|
26
|
+
- `skills rules --info=<language>` for detailed view
|
|
27
|
+
- Rules for more languages (Go, Swift, C++)
|
|
28
|
+
- Agent evals system
|
package/bin/skills.js
CHANGED
|
@@ -12,6 +12,7 @@ const command = args[0];
|
|
|
12
12
|
const skillsRoot = path.join(__dirname, '..', 'skills');
|
|
13
13
|
const commandsRoot = path.join(__dirname, '..', 'commands');
|
|
14
14
|
const agentsRoot = path.join(__dirname, '..', 'agents');
|
|
15
|
+
const rulesRoot = path.join(__dirname, '..', 'rules');
|
|
15
16
|
|
|
16
17
|
// ─── Installation profiles ────────────────────────────────────────────────────
|
|
17
18
|
const PROFILES = {
|
|
@@ -149,6 +150,9 @@ const commandsTargetDir = isGlobal
|
|
|
149
150
|
const agentsTargetDir = isGlobal
|
|
150
151
|
? path.join(os.homedir(), '.claude', 'agents')
|
|
151
152
|
: path.join(process.cwd(), '.claude', 'agents');
|
|
153
|
+
const rulesTargetDir = isGlobal
|
|
154
|
+
? path.join(os.homedir(), '.claude', 'rules')
|
|
155
|
+
: path.join(process.cwd(), '.claude', 'rules');
|
|
152
156
|
|
|
153
157
|
function copyCommand(skillName) {
|
|
154
158
|
const src = path.join(commandsRoot, `${skillName}.md`);
|
|
@@ -221,6 +225,42 @@ function copyHooks() {
|
|
|
221
225
|
}
|
|
222
226
|
}
|
|
223
227
|
|
|
228
|
+
function getAvailableRules() {
|
|
229
|
+
// Returns [{language, name, file}] for each rule file found under rules/
|
|
230
|
+
if (!fs.existsSync(rulesRoot)) return [];
|
|
231
|
+
const result = [];
|
|
232
|
+
for (const lang of fs.readdirSync(rulesRoot).sort()) {
|
|
233
|
+
const langDir = path.join(rulesRoot, lang);
|
|
234
|
+
if (!fs.statSync(langDir).isDirectory()) continue;
|
|
235
|
+
for (const file of fs.readdirSync(langDir).filter(f => f.endsWith('.md')).sort()) {
|
|
236
|
+
result.push({ language: lang, name: file.replace(/\.md$/, ''), file: path.join(langDir, file) });
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
return result;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
function copyRules(language) {
|
|
243
|
+
// Copies all rule files for a given language (or 'common') to rulesTargetDir
|
|
244
|
+
const langDir = path.join(rulesRoot, language);
|
|
245
|
+
if (!fs.existsSync(langDir)) {
|
|
246
|
+
console.error(c.red(`✗ No rules for language "${language}".`) + ' Run ' + c.cyan('skills rules') + ' to see available rules.');
|
|
247
|
+
process.exit(1);
|
|
248
|
+
}
|
|
249
|
+
const destDir = path.join(rulesTargetDir, language);
|
|
250
|
+
fs.mkdirSync(destDir, { recursive: true });
|
|
251
|
+
for (const file of fs.readdirSync(langDir).filter(f => f.endsWith('.md'))) {
|
|
252
|
+
const dest = path.join(destDir, file);
|
|
253
|
+
fs.copyFileSync(path.join(langDir, file), dest);
|
|
254
|
+
console.log(c.green('✓') + ` ${c.bold(language + '/' + file.replace(/\.md$/, ''))} rule → ${c.dim(dest)}`);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function copyAllRules() {
|
|
259
|
+
const rules = getAvailableRules();
|
|
260
|
+
const languages = [...new Set(rules.map(r => r.language))];
|
|
261
|
+
for (const lang of languages) copyRules(lang);
|
|
262
|
+
}
|
|
263
|
+
|
|
224
264
|
function copySkillToCursor(skillName) {
|
|
225
265
|
const src = path.join(skillsRoot, skillName, 'SKILL.md');
|
|
226
266
|
if (!fs.existsSync(src)) return;
|
|
@@ -825,15 +865,18 @@ async function main() {
|
|
|
825
865
|
}
|
|
826
866
|
|
|
827
867
|
case 'add': {
|
|
828
|
-
const addAll
|
|
829
|
-
const
|
|
830
|
-
const
|
|
831
|
-
const
|
|
832
|
-
const
|
|
833
|
-
const
|
|
834
|
-
const
|
|
835
|
-
const
|
|
836
|
-
const
|
|
868
|
+
const addAll = args.includes('--all');
|
|
869
|
+
const addHooks = args.includes('--hooks');
|
|
870
|
+
const noCommands = args.includes('--no-commands');
|
|
871
|
+
const noAgents = args.includes('--no-agents');
|
|
872
|
+
const agentArg = args.find(a => a.startsWith('--agent='))?.split('=')[1];
|
|
873
|
+
const profileArg = args.find(a => a.startsWith('--profile='))?.split('=')[1];
|
|
874
|
+
const rulesArg = args.find(a => a === '--rules' || a.startsWith('--rules='));
|
|
875
|
+
const rulesLang = rulesArg?.includes('=') ? rulesArg.split('=')[1] : null;
|
|
876
|
+
const targetArg = (args.find(a => a.startsWith('--target='))?.split('=')[1] ?? 'claude').toLowerCase();
|
|
877
|
+
const toClaude = targetArg === 'claude' || targetArg === 'all';
|
|
878
|
+
const toCursor = targetArg === 'cursor' || targetArg === 'all';
|
|
879
|
+
const skillName = args.find(a => !a.startsWith('--') && a !== 'add');
|
|
837
880
|
|
|
838
881
|
const installSkills = (list) => {
|
|
839
882
|
if (toClaude) list.forEach(s => copySkill(s, targetDir));
|
|
@@ -868,12 +911,25 @@ async function main() {
|
|
|
868
911
|
console.log(c.dim(`\nInstalled to ${agentsTargetDir}`));
|
|
869
912
|
} else if (addAll) {
|
|
870
913
|
const skills = getAvailableSkills();
|
|
914
|
+
const agents = getAvailableAgents();
|
|
871
915
|
installSkills(skills);
|
|
872
|
-
installAgents(
|
|
873
|
-
if (toClaude) copyHooks();
|
|
874
|
-
const agentCount = (!noAgents && toClaude) ?
|
|
916
|
+
installAgents(agents);
|
|
917
|
+
if (toClaude) { copyHooks(); copyAllRules(); }
|
|
918
|
+
const agentCount = (!noAgents && toClaude) ? agents.length : 0;
|
|
875
919
|
const targets = [toClaude && '.claude', toCursor && '.cursor/rules'].filter(Boolean).join(' + ');
|
|
876
|
-
console.log(c.dim(`\nInstalled ${skills.length} skills, ${agentCount} agents → ${targets}`));
|
|
920
|
+
console.log(c.dim(`\nInstalled ${skills.length} skills, ${agentCount} agents, ${getAvailableRules().length} rules → ${targets}`));
|
|
921
|
+
} else if (addHooks) {
|
|
922
|
+
if (toClaude) copyHooks();
|
|
923
|
+
else console.log(c.yellow(' --hooks only applies to Claude targets. Use without --target=cursor.'));
|
|
924
|
+
break;
|
|
925
|
+
} else if (rulesArg) {
|
|
926
|
+
if (!toClaude) {
|
|
927
|
+
console.log(c.yellow(' --rules only applies to Claude targets (.claude/rules/).'));
|
|
928
|
+
break;
|
|
929
|
+
}
|
|
930
|
+
if (rulesLang) copyRules(rulesLang);
|
|
931
|
+
else copyAllRules();
|
|
932
|
+
console.log(c.dim(`\nInstalled rules → ${rulesTargetDir}`));
|
|
877
933
|
} else if (skillName) {
|
|
878
934
|
installSkills([skillName]);
|
|
879
935
|
console.log(c.dim(`\nInstalled to ${targetDir}`));
|
|
@@ -1019,6 +1075,37 @@ async function main() {
|
|
|
1019
1075
|
break;
|
|
1020
1076
|
}
|
|
1021
1077
|
|
|
1078
|
+
case 'rules': {
|
|
1079
|
+
const available = getAvailableRules();
|
|
1080
|
+
if (!available.length) {
|
|
1081
|
+
console.log(c.yellow(' No rules found.'));
|
|
1082
|
+
break;
|
|
1083
|
+
}
|
|
1084
|
+
// Group by language
|
|
1085
|
+
const byLang = {};
|
|
1086
|
+
for (const r of available) {
|
|
1087
|
+
if (!byLang[r.language]) byLang[r.language] = [];
|
|
1088
|
+
byLang[r.language].push(r);
|
|
1089
|
+
}
|
|
1090
|
+
console.log('');
|
|
1091
|
+
console.log(c.bold(' @booklib/skills — rules') + c.dim(` (${available.length} always-on)`));
|
|
1092
|
+
console.log(' ' + c.line(60));
|
|
1093
|
+
for (const [lang, rules] of Object.entries(byLang)) {
|
|
1094
|
+
console.log(` ${c.bold(lang)}`);
|
|
1095
|
+
for (const r of rules) {
|
|
1096
|
+
const content = fs.readFileSync(r.file, 'utf8');
|
|
1097
|
+
const descMatch = content.match(/^description:\s*(.+)$/m);
|
|
1098
|
+
const desc = descMatch ? descMatch[1].trim().replace(/^>$/, '') : '';
|
|
1099
|
+
console.log(` ${c.cyan(r.name.padEnd(28))}${c.dim(firstSentence(desc, 55))}`);
|
|
1100
|
+
}
|
|
1101
|
+
console.log('');
|
|
1102
|
+
}
|
|
1103
|
+
console.log(c.dim(` skills add --rules install all rules → .claude/rules/`));
|
|
1104
|
+
console.log(c.dim(` skills add --rules=<language> install rules for one language`));
|
|
1105
|
+
console.log('');
|
|
1106
|
+
break;
|
|
1107
|
+
}
|
|
1108
|
+
|
|
1022
1109
|
case 'profiles': {
|
|
1023
1110
|
const nameW = Math.max(...Object.keys(PROFILES).map(k => k.length)) + 2;
|
|
1024
1111
|
console.log('');
|
|
@@ -1045,13 +1132,17 @@ ${c.bold(' Usage:')}
|
|
|
1045
1132
|
${c.cyan('skills agents')} list all available agents
|
|
1046
1133
|
${c.cyan('skills agents')} ${c.dim('--info=<name>')} full description of an agent
|
|
1047
1134
|
${c.cyan('skills profiles')} list available profiles
|
|
1135
|
+
${c.cyan('skills rules')} list always-on rule files
|
|
1048
1136
|
${c.cyan('skills info')} ${c.dim('<name>')} full description of a skill
|
|
1049
1137
|
${c.cyan('skills demo')} ${c.dim('<name>')} before/after example
|
|
1050
1138
|
${c.cyan('skills add')} ${c.dim('--profile=<name>')} install a profile (skills + commands + agent)
|
|
1051
1139
|
${c.cyan('skills add')} ${c.dim('<name>')} install a single skill + /command
|
|
1052
|
-
${c.cyan('skills add --all')} install everything (skills +
|
|
1140
|
+
${c.cyan('skills add --all')} install everything (skills + agents + rules + hooks)
|
|
1053
1141
|
${c.cyan('skills add')} ${c.dim('<name> --global')} install globally (~/.claude/)
|
|
1054
1142
|
${c.cyan('skills add')} ${c.dim('--agent=<name>')} install a single agent to .claude/agents/
|
|
1143
|
+
${c.cyan('skills add --rules')} install always-on rules to .claude/rules/
|
|
1144
|
+
${c.cyan('skills add')} ${c.dim('--rules=<language>')} install rules for one language
|
|
1145
|
+
${c.cyan('skills add --hooks')} install the UserPromptSubmit suggestion hook
|
|
1055
1146
|
${c.cyan('skills add')} ${c.dim('--target=cursor')} install to .cursor/rules/ (Cursor IDE)
|
|
1056
1147
|
${c.cyan('skills add')} ${c.dim('--target=all')} install to both .claude/ and .cursor/
|
|
1057
1148
|
${c.cyan('skills add')} ${c.dim('--no-commands')} skip /command installation
|
package/package.json
CHANGED
|
@@ -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
|