@lucasygu/redbook 0.1.2 → 0.1.6
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/SKILL.md +224 -0
- package/package.json +8 -2
- package/scripts/postinstall.js +69 -0
- package/scripts/preuninstall.js +49 -0
package/SKILL.md
ADDED
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Search, read, and analyze Xiaohongshu (小红书) content via CLI
|
|
3
|
+
allowed-tools: Bash, Read, Write, Glob, Grep
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Redbook — Xiaohongshu CLI
|
|
7
|
+
|
|
8
|
+
Use the `redbook` CLI to search notes, read content, analyze creators, and research topics on Xiaohongshu (小红书/RED).
|
|
9
|
+
|
|
10
|
+
## Usage
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
/redbook search "AI编程" # Search notes
|
|
14
|
+
/redbook read <url> # Read a note
|
|
15
|
+
/redbook user <userId> # Creator profile
|
|
16
|
+
/redbook analyze <userId> # Full creator analysis (profile + posts)
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Instructions
|
|
20
|
+
|
|
21
|
+
When this command is invoked, determine the user's intent and run the appropriate CLI command(s).
|
|
22
|
+
|
|
23
|
+
### Quick Reference
|
|
24
|
+
|
|
25
|
+
| Intent | Command |
|
|
26
|
+
|--------|---------|
|
|
27
|
+
| Search notes | `redbook search "keyword" --json` |
|
|
28
|
+
| Read a note | `redbook read <url> --json` |
|
|
29
|
+
| Get comments | `redbook comments <url> --json --all` |
|
|
30
|
+
| Creator profile | `redbook user <userId> --json` |
|
|
31
|
+
| Creator's posts | `redbook user-posts <userId> --json` |
|
|
32
|
+
| Browse feed | `redbook feed --json` |
|
|
33
|
+
| Search hashtags | `redbook topics "keyword" --json` |
|
|
34
|
+
| Check connection | `redbook whoami` |
|
|
35
|
+
|
|
36
|
+
### Always Use `--json`
|
|
37
|
+
|
|
38
|
+
Always add `--json` to commands when you need to parse the output programmatically. The JSON output is structured and reliable. Without `--json`, output is human-formatted text.
|
|
39
|
+
|
|
40
|
+
### Command Details
|
|
41
|
+
|
|
42
|
+
#### `redbook search <keyword>`
|
|
43
|
+
|
|
44
|
+
Search for notes by keyword. Returns note titles, URLs, likes, author info.
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
redbook search "Claude Code教程" --json
|
|
48
|
+
redbook search "AI编程" --sort popular --json # Sort: general, popular, latest
|
|
49
|
+
redbook search "Cursor" --type image --json # Type: all, video, image
|
|
50
|
+
redbook search "MCP Server" --page 2 --json # Pagination
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
**Options:**
|
|
54
|
+
- `--sort <type>`: `general` (default), `popular`, `latest`
|
|
55
|
+
- `--type <type>`: `all` (default), `video`, `image`
|
|
56
|
+
- `--page <n>`: Page number (default: 1)
|
|
57
|
+
|
|
58
|
+
#### `redbook read <url>`
|
|
59
|
+
|
|
60
|
+
Read a note's full content — title, body text, images, likes, comments count.
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
redbook read "https://www.xiaohongshu.com/explore/abc123" --json
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Accepts full URLs or short note IDs. If the API returns a captcha, it falls back to HTML scraping automatically.
|
|
67
|
+
|
|
68
|
+
#### `redbook comments <url>`
|
|
69
|
+
|
|
70
|
+
Get comments on a note. Use `--all` to fetch all pages.
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
redbook comments "https://www.xiaohongshu.com/explore/abc123" --json
|
|
74
|
+
redbook comments "https://www.xiaohongshu.com/explore/abc123" --all --json
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
**Options:**
|
|
78
|
+
- `--all`: Fetch all comment pages (default: first page only)
|
|
79
|
+
|
|
80
|
+
#### `redbook user <userId>`
|
|
81
|
+
|
|
82
|
+
Get a creator's profile — nickname, bio, follower count, note count, likes received.
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
redbook user "5a1234567890abcdef012345" --json
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
The userId is the hex string from the creator's profile URL.
|
|
89
|
+
|
|
90
|
+
#### `redbook user-posts <userId>`
|
|
91
|
+
|
|
92
|
+
List all notes posted by a creator. Returns titles, URLs, likes, timestamps.
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
redbook user-posts "5a1234567890abcdef012345" --json
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
#### `redbook feed`
|
|
99
|
+
|
|
100
|
+
Browse the recommendation feed. Returns a batch of recommended notes.
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
redbook feed --json
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
#### `redbook topics <keyword>`
|
|
107
|
+
|
|
108
|
+
Search for topic hashtags. Useful for finding trending topics to attach to posts.
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
redbook topics "Claude Code" --json
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
#### `redbook whoami`
|
|
115
|
+
|
|
116
|
+
Check connection status. Verifies cookies are valid and shows the logged-in user.
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
redbook whoami
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
If this fails, the user needs to log into xiaohongshu.com in Chrome.
|
|
123
|
+
|
|
124
|
+
#### `redbook post` (Limited)
|
|
125
|
+
|
|
126
|
+
Publish an image note. **Note: This command frequently triggers captcha (type=124) on the creator API.** Image upload works, but the publish step is unreliable. For posting, use Chrome browser automation instead.
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
redbook post --title "标题" --body "正文" --images cover.png --json
|
|
130
|
+
redbook post --title "测试" --body "..." --images img.png --private --json
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
**Options:**
|
|
134
|
+
- `--title <title>`: Note title (required)
|
|
135
|
+
- `--body <body>`: Note body text (required)
|
|
136
|
+
- `--images <paths...>`: Image file paths (required, at least one)
|
|
137
|
+
- `--topic <keyword>`: Search and attach a topic hashtag
|
|
138
|
+
- `--private`: Publish as private note
|
|
139
|
+
|
|
140
|
+
### Global Options
|
|
141
|
+
|
|
142
|
+
All commands accept:
|
|
143
|
+
- `--cookie-source <browser>`: `chrome` (default), `safari`, `firefox`
|
|
144
|
+
- `--json`: Output as JSON
|
|
145
|
+
|
|
146
|
+
## Research Workflows
|
|
147
|
+
|
|
148
|
+
### Competitive Analysis
|
|
149
|
+
|
|
150
|
+
Research competitors in a niche:
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
# 1. Search for content in the niche
|
|
154
|
+
redbook search "Claude Code" --sort popular --json
|
|
155
|
+
|
|
156
|
+
# 2. Identify top creators from search results (extract user IDs)
|
|
157
|
+
|
|
158
|
+
# 3. Deep-dive on each creator
|
|
159
|
+
redbook user <userId> --json # Profile + follower stats
|
|
160
|
+
redbook user-posts <userId> --json # All their content + engagement
|
|
161
|
+
|
|
162
|
+
# 4. Read their top-performing notes
|
|
163
|
+
redbook read <noteUrl> --json # Full content
|
|
164
|
+
redbook comments <noteUrl> --all --json # What resonates with audience
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### Topic Research
|
|
168
|
+
|
|
169
|
+
Find trending topics and hashtags:
|
|
170
|
+
|
|
171
|
+
```bash
|
|
172
|
+
# Search for topics
|
|
173
|
+
redbook topics "AI编程" --json
|
|
174
|
+
|
|
175
|
+
# Search notes using different sort orders to understand the landscape
|
|
176
|
+
redbook search "keyword" --sort popular --json # What's proven
|
|
177
|
+
redbook search "keyword" --sort latest --json # What's new
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### Creator Deep-Dive
|
|
181
|
+
|
|
182
|
+
Analyze a specific creator's strategy:
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
# Get profile overview
|
|
186
|
+
redbook user <userId> --json
|
|
187
|
+
|
|
188
|
+
# Get all their posts to analyze content patterns
|
|
189
|
+
redbook user-posts <userId> --json
|
|
190
|
+
|
|
191
|
+
# Read their top posts for content structure analysis
|
|
192
|
+
redbook read <topPostUrl> --json
|
|
193
|
+
redbook comments <topPostUrl> --all --json
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
## Programmatic API
|
|
197
|
+
|
|
198
|
+
The package also exports a TypeScript client for scripting:
|
|
199
|
+
|
|
200
|
+
```typescript
|
|
201
|
+
import { XhsClient } from "@lucasygu/redbook";
|
|
202
|
+
import { loadCookies } from "@lucasygu/redbook/cookies";
|
|
203
|
+
|
|
204
|
+
const cookies = await loadCookies("chrome");
|
|
205
|
+
const client = new XhsClient(cookies);
|
|
206
|
+
|
|
207
|
+
const results = await client.searchNotes("AI编程", 1, 20, "popular");
|
|
208
|
+
const topics = await client.searchTopics("Claude Code");
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
## Error Handling
|
|
212
|
+
|
|
213
|
+
| Error | Meaning | Fix |
|
|
214
|
+
|-------|---------|-----|
|
|
215
|
+
| "No 'a1' cookie" | Not logged into XHS in browser | Log into xiaohongshu.com in Chrome |
|
|
216
|
+
| "Session expired" | Cookie too old | Re-login in Chrome |
|
|
217
|
+
| "NeedVerify" / captcha | Anti-bot triggered | Wait and retry, or reduce request frequency |
|
|
218
|
+
| "IP blocked" (300012) | Rate limited | Wait or switch network |
|
|
219
|
+
|
|
220
|
+
## Requirements
|
|
221
|
+
|
|
222
|
+
- Node.js >= 22
|
|
223
|
+
- Logged into xiaohongshu.com in Chrome (or Safari/Firefox with `--cookie-source`)
|
|
224
|
+
- macOS (cookie extraction uses native keychain access)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lucasygu/redbook",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"description": "CLI tool for Xiaohongshu (Red Note) - read and post via private API",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/lib/client.js",
|
|
@@ -14,6 +14,8 @@
|
|
|
14
14
|
},
|
|
15
15
|
"files": [
|
|
16
16
|
"dist/",
|
|
17
|
+
"scripts/",
|
|
18
|
+
"SKILL.md",
|
|
17
19
|
"LICENSE",
|
|
18
20
|
"README.md"
|
|
19
21
|
],
|
|
@@ -21,6 +23,8 @@
|
|
|
21
23
|
"build": "tsc && chmod +x dist/cli.js",
|
|
22
24
|
"dev": "tsc --watch",
|
|
23
25
|
"prepublishOnly": "npm run build",
|
|
26
|
+
"postinstall": "node scripts/postinstall.js",
|
|
27
|
+
"preuninstall": "node scripts/preuninstall.js",
|
|
24
28
|
"start": "node dist/cli.js"
|
|
25
29
|
},
|
|
26
30
|
"engines": {
|
|
@@ -31,7 +35,9 @@
|
|
|
31
35
|
"redbook",
|
|
32
36
|
"xhs",
|
|
33
37
|
"red-note",
|
|
34
|
-
"cli"
|
|
38
|
+
"cli",
|
|
39
|
+
"claude-code",
|
|
40
|
+
"claude-code-skill"
|
|
35
41
|
],
|
|
36
42
|
"author": "lucasygu",
|
|
37
43
|
"license": "MIT",
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Post-install script for redbook CLI.
|
|
4
|
+
*
|
|
5
|
+
* Sets up Claude Code skill by creating a symlink:
|
|
6
|
+
* ~/.claude/skills/redbook -> <npm-package-location>
|
|
7
|
+
*
|
|
8
|
+
* This gives Claude Code a /redbook slash command automatically.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { existsSync, mkdirSync, unlinkSync, symlinkSync, lstatSync, readlinkSync, rmSync } from 'fs';
|
|
12
|
+
import { join, dirname } from 'path';
|
|
13
|
+
import { fileURLToPath } from 'url';
|
|
14
|
+
import { homedir } from 'os';
|
|
15
|
+
|
|
16
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
17
|
+
const __dirname = dirname(__filename);
|
|
18
|
+
|
|
19
|
+
const PACKAGE_ROOT = join(__dirname, '..');
|
|
20
|
+
const SKILL_DIR = join(homedir(), '.claude', 'skills');
|
|
21
|
+
const SKILL_LINK = join(SKILL_DIR, 'redbook');
|
|
22
|
+
|
|
23
|
+
function setupClaudeSkill() {
|
|
24
|
+
try {
|
|
25
|
+
if (!existsSync(SKILL_DIR)) {
|
|
26
|
+
mkdirSync(SKILL_DIR, { recursive: true });
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (existsSync(SKILL_LINK)) {
|
|
30
|
+
try {
|
|
31
|
+
const stats = lstatSync(SKILL_LINK);
|
|
32
|
+
if (stats.isSymbolicLink()) {
|
|
33
|
+
const currentTarget = readlinkSync(SKILL_LINK);
|
|
34
|
+
if (currentTarget === PACKAGE_ROOT) {
|
|
35
|
+
console.log('[redbook] Claude Code skill already configured.');
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
unlinkSync(SKILL_LINK);
|
|
39
|
+
} else {
|
|
40
|
+
rmSync(SKILL_LINK, { recursive: true });
|
|
41
|
+
}
|
|
42
|
+
} catch (err) {
|
|
43
|
+
console.log(`[redbook] Warning: ${err.message}`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
symlinkSync(PACKAGE_ROOT, SKILL_LINK);
|
|
48
|
+
console.log('[redbook] Claude Code skill installed:');
|
|
49
|
+
console.log(`[redbook] ~/.claude/skills/redbook -> ${PACKAGE_ROOT}`);
|
|
50
|
+
return true;
|
|
51
|
+
} catch (error) {
|
|
52
|
+
console.error(`[redbook] Failed to set up skill: ${error.message}`);
|
|
53
|
+
console.log('[redbook] You can manually create the symlink:');
|
|
54
|
+
console.log(`[redbook] ln -s "${PACKAGE_ROOT}" "${SKILL_LINK}"`);
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function main() {
|
|
60
|
+
console.log('[redbook] Running post-install...');
|
|
61
|
+
const success = setupClaudeSkill();
|
|
62
|
+
console.log('');
|
|
63
|
+
console.log('[redbook] Installation complete!');
|
|
64
|
+
if (success) {
|
|
65
|
+
console.log('[redbook] Use /redbook in Claude Code, or run: redbook --help');
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
main();
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Pre-uninstall script for redbook CLI.
|
|
4
|
+
*
|
|
5
|
+
* Removes the Claude Code skill symlink at ~/.claude/skills/redbook
|
|
6
|
+
* (only if it points to this package).
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { existsSync, unlinkSync, lstatSync, readlinkSync } from 'fs';
|
|
10
|
+
import { join, dirname } from 'path';
|
|
11
|
+
import { fileURLToPath } from 'url';
|
|
12
|
+
import { homedir } from 'os';
|
|
13
|
+
|
|
14
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
15
|
+
const __dirname = dirname(__filename);
|
|
16
|
+
|
|
17
|
+
const PACKAGE_ROOT = join(__dirname, '..');
|
|
18
|
+
const SKILL_LINK = join(homedir(), '.claude', 'skills', 'redbook');
|
|
19
|
+
|
|
20
|
+
function main() {
|
|
21
|
+
console.log('[redbook] Running pre-uninstall...');
|
|
22
|
+
|
|
23
|
+
if (!existsSync(SKILL_LINK)) {
|
|
24
|
+
console.log('[redbook] No skill symlink found, nothing to clean up.');
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
try {
|
|
29
|
+
const stats = lstatSync(SKILL_LINK);
|
|
30
|
+
if (!stats.isSymbolicLink()) {
|
|
31
|
+
console.log('[redbook] Skill path is not a symlink, leaving it alone.');
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const target = readlinkSync(SKILL_LINK);
|
|
36
|
+
if (target === PACKAGE_ROOT || target.includes('node_modules/@lucasygu/redbook')) {
|
|
37
|
+
unlinkSync(SKILL_LINK);
|
|
38
|
+
console.log('[redbook] Removed Claude Code skill symlink.');
|
|
39
|
+
} else {
|
|
40
|
+
console.log('[redbook] Skill symlink points elsewhere, leaving it alone.');
|
|
41
|
+
}
|
|
42
|
+
} catch (error) {
|
|
43
|
+
console.error(`[redbook] Warning: Could not remove skill: ${error.message}`);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
console.log('[redbook] Uninstall cleanup complete.');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
main();
|