getnotes-cli 0.1.3__tar.gz → 0.1.5__tar.gz
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.
- {getnotes_cli-0.1.3 → getnotes_cli-0.1.5}/CHANGELOG.md +6 -0
- {getnotes_cli-0.1.3 → getnotes_cli-0.1.5}/PKG-INFO +57 -41
- {getnotes_cli-0.1.3 → getnotes_cli-0.1.5}/README.md +56 -40
- {getnotes_cli-0.1.3 → getnotes_cli-0.1.5}/pyproject.toml +1 -1
- {getnotes_cli-0.1.3 → getnotes_cli-0.1.5}/src/getnotes_cli/cli.py +90 -0
- {getnotes_cli-0.1.3 → getnotes_cli-0.1.5}/src/getnotes_cli/config.py +3 -0
- {getnotes_cli-0.1.3 → getnotes_cli-0.1.5}/src/getnotes_cli/mcp/server.py +1 -0
- {getnotes_cli-0.1.3 → getnotes_cli-0.1.5}/src/getnotes_cli/mcp/tools/notes.py +70 -1
- getnotes_cli-0.1.5/src/getnotes_cli/searcher.py +70 -0
- {getnotes_cli-0.1.3 → getnotes_cli-0.1.5}/uv.lock +1 -1
- {getnotes_cli-0.1.3 → getnotes_cli-0.1.5}/.agent/workflows/release.md +0 -0
- {getnotes_cli-0.1.3 → getnotes_cli-0.1.5}/.github/workflows/publish.yml +0 -0
- {getnotes_cli-0.1.3 → getnotes_cli-0.1.5}/.gitignore +0 -0
- {getnotes_cli-0.1.3 → getnotes_cli-0.1.5}/LICENSE +0 -0
- {getnotes_cli-0.1.3 → getnotes_cli-0.1.5}/src/getnotes_cli/__init__.py +0 -0
- {getnotes_cli-0.1.3 → getnotes_cli-0.1.5}/src/getnotes_cli/auth.py +0 -0
- {getnotes_cli-0.1.3 → getnotes_cli-0.1.5}/src/getnotes_cli/cache.py +0 -0
- {getnotes_cli-0.1.3 → getnotes_cli-0.1.5}/src/getnotes_cli/cdp.py +0 -0
- {getnotes_cli-0.1.3 → getnotes_cli-0.1.5}/src/getnotes_cli/creator.py +0 -0
- {getnotes_cli-0.1.3 → getnotes_cli-0.1.5}/src/getnotes_cli/downloader.py +0 -0
- {getnotes_cli-0.1.3 → getnotes_cli-0.1.5}/src/getnotes_cli/markdown.py +0 -0
- {getnotes_cli-0.1.3 → getnotes_cli-0.1.5}/src/getnotes_cli/mcp/__init__.py +0 -0
- {getnotes_cli-0.1.3 → getnotes_cli-0.1.5}/src/getnotes_cli/mcp/tools/__init__.py +0 -0
- {getnotes_cli-0.1.3 → getnotes_cli-0.1.5}/src/getnotes_cli/mcp/tools/_utils.py +0 -0
- {getnotes_cli-0.1.3 → getnotes_cli-0.1.5}/src/getnotes_cli/mcp/tools/notebooks.py +0 -0
- {getnotes_cli-0.1.3 → getnotes_cli-0.1.5}/src/getnotes_cli/notebook.py +0 -0
- {getnotes_cli-0.1.3 → getnotes_cli-0.1.5}/src/getnotes_cli/notebook_downloader.py +0 -0
- {getnotes_cli-0.1.3 → getnotes_cli-0.1.5}/src/getnotes_cli/settings.py +0 -0
|
@@ -5,6 +5,12 @@
|
|
|
5
5
|
此文件的格式基于 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/),
|
|
6
6
|
并且本项目遵循 [语义化版本规范 (Semantic Versioning)](https://semver.org/spec/v2.0.0.html)。
|
|
7
7
|
|
|
8
|
+
## [0.1.5] - 2026-02-28
|
|
9
|
+
|
|
10
|
+
### 新增 (Added)
|
|
11
|
+
- **笔记搜索功能**:新增 `search` CLI 命令,支持根据关键词搜索笔记,结果以 Rich 表格展示,支持分页浏览。
|
|
12
|
+
- **MCP 搜索工具**:MCP 服务器新增 `search_notes(query)` 工具,允许 LLM 客户端直接搜索用户笔记并返回结构化结果。
|
|
13
|
+
|
|
8
14
|
## [0.1.3] - 2026-02-27
|
|
9
15
|
|
|
10
16
|
### 新增 (Added)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: getnotes-cli
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.5
|
|
4
4
|
Summary: Get 笔记 CLI 下载工具 — 自动登录、批量下载、Markdown 导出
|
|
5
5
|
Author: Han Zhang
|
|
6
6
|
License: MIT
|
|
@@ -18,11 +18,11 @@ Description-Content-Type: text/markdown
|
|
|
18
18
|
|
|
19
19
|
# getnotes-cli 🗂️
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
Get笔记 Cli 下载工具和 MCP 集成,支持自动登录、批量下载、知识库管理、笔记搜索、Markdown 导出、录音图片等附件下载。
|
|
22
|
+
|
|
23
23
|
> **初衷与设计理念:**
|
|
24
|
+
> - 🤖 **Agent 工作流**:提供标准化的 CLI 和 MCP 接入,便于无缝嵌入到各类大模型 Agent 或自动化流程中,充当高质量的个人知识上下文。
|
|
24
25
|
> - 📦 **数据自有化**:将你在平台积攒的笔记、知识库等数字资产完整下载到本地,实现数据的真正自有化与安全备份。
|
|
25
|
-
> - 🤖 **Agent 工作流**:提供标准化的 CLI 和本地文件系统接口,便于无缝嵌入到各类大模型 Agent 或自动化流程中,充当高质量的个人知识上下文。
|
|
26
26
|
|
|
27
27
|
[](https://python.org)
|
|
28
28
|
[](LICENSE)
|
|
@@ -32,7 +32,8 @@ Description-Content-Type: text/markdown
|
|
|
32
32
|
- 🔐 **自动登录** — 通过 Chrome DevTools Protocol 自动获取 Bearer token,无需手动抓包
|
|
33
33
|
- 📥 **批量下载** — 分页拉取全部笔记,支持指定数量
|
|
34
34
|
- 📤 **新建笔记** — 支持通过本地 Markdown 或文本文件创建笔记,并支持自动上传内嵌图片
|
|
35
|
-
-
|
|
35
|
+
- � **笔记搜索** — 根据关键词搜索笔记,支持分页浏览
|
|
36
|
+
- �📚 **知识库管理** — 查看、下载我的知识库与订阅知识库
|
|
36
37
|
- 📝 **Markdown 导出** — 每条笔记保存为 Markdown,包含元信息、标签、正文、引用内容
|
|
37
38
|
- 🔊 **附件下载** — 自动下载音频、图片附件,并在 Markdown 中内嵌链接
|
|
38
39
|
- 💾 **缓存管理** — 自动跳过已下载且未变化的笔记,支持增量更新
|
|
@@ -41,7 +42,43 @@ Description-Content-Type: text/markdown
|
|
|
41
42
|
- ⏱️ **可配置间隔** — 自定义请求间隔,避免频率限制
|
|
42
43
|
- 📊 **自动索引** — 自动生成笔记索引文件 `INDEX.md`
|
|
43
44
|
|
|
44
|
-
##
|
|
45
|
+
## 🤖 MCP 服务器
|
|
46
|
+
|
|
47
|
+
Get笔记 CLI 提供原生的 [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) 服务器支持,允许集成 [Claude Desktop](https://claude.ai/download) 等 AI 客户端直接为你管理笔记和知识库。
|
|
48
|
+
|
|
49
|
+
### 配置 Claude Desktop
|
|
50
|
+
|
|
51
|
+
编辑 Claude Desktop 配置文件 `claude_desktop_config.json`(通常在 `~/Library/Application Support/Claude/`):
|
|
52
|
+
|
|
53
|
+
```json
|
|
54
|
+
{
|
|
55
|
+
"mcpServers": {
|
|
56
|
+
"getnotes": {
|
|
57
|
+
"command": "uvx",
|
|
58
|
+
"args": [
|
|
59
|
+
"--from",
|
|
60
|
+
"getnotes-cli",
|
|
61
|
+
"getnotes-mcp"
|
|
62
|
+
]
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
> **注意**:在使用 MCP 服务器前,确保你在终端执行过 `getnotes login` 获取了 Token。
|
|
69
|
+
|
|
70
|
+
### 可用 MCP Tools
|
|
71
|
+
|
|
72
|
+
- `download_notes(limit=10)`: 下载近期笔记为 Markdown 文件。
|
|
73
|
+
- `create_note(content)`: 直接提交文本建立新笔记。
|
|
74
|
+
- `create_link_note(url)`: 通过 AI 解析链接创建深度笔记。
|
|
75
|
+
- `search_notes(query)`: 根据关键词搜索笔记并返回匹配结果。
|
|
76
|
+
- `list_notebooks()`: 获取你创建的知识库列表及对应 ID。
|
|
77
|
+
- `download_notebook(notebook_id)`: 下载指定的知识库内容。
|
|
78
|
+
- `list_subscribed_notebooks()`: 获取订阅知识库列表。
|
|
79
|
+
- `download_subscribed_notebook(notebook_id)`: 下载指定的订阅知识库。
|
|
80
|
+
|
|
81
|
+
## 📦 Cli 安装
|
|
45
82
|
|
|
46
83
|
### 使用 uv 安装(推荐)
|
|
47
84
|
|
|
@@ -93,6 +130,19 @@ getnotes create -f my_note.md -i img1.png -i img2.jpg
|
|
|
93
130
|
getnotes create-link <url>
|
|
94
131
|
```
|
|
95
132
|
|
|
133
|
+
### 搜索笔记
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
# 根据关键词搜索笔记
|
|
137
|
+
getnotes search "AI 提效"
|
|
138
|
+
|
|
139
|
+
# 查看第 2 页结果
|
|
140
|
+
getnotes search "AI 提效" --page 2
|
|
141
|
+
|
|
142
|
+
# 自定义每页数量
|
|
143
|
+
getnotes search "AI 提效" --page-size 20
|
|
144
|
+
```
|
|
145
|
+
|
|
96
146
|
### 下载笔记
|
|
97
147
|
|
|
98
148
|
```bash
|
|
@@ -217,46 +267,12 @@ getnotes --version
|
|
|
217
267
|
getnotes --help
|
|
218
268
|
getnotes create --help
|
|
219
269
|
getnotes download --help
|
|
270
|
+
getnotes search --help
|
|
220
271
|
getnotes notebook --help
|
|
221
272
|
getnotes subscribe --help
|
|
222
273
|
getnotes config --help
|
|
223
274
|
```
|
|
224
275
|
|
|
225
|
-
## 🤖 MCP 服务器
|
|
226
|
-
|
|
227
|
-
Get笔记 CLI 提供原生的 [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) 服务器支持,允许集成 [Claude Desktop](https://claude.ai/download) 等 AI 客户端直接为你管理笔记和知识库。
|
|
228
|
-
|
|
229
|
-
### 配置 Claude Desktop
|
|
230
|
-
|
|
231
|
-
编辑 Claude Desktop 配置文件 `claude_desktop_config.json`(通常在 `~/Library/Application Support/Claude/`):
|
|
232
|
-
|
|
233
|
-
```json
|
|
234
|
-
{
|
|
235
|
-
"mcpServers": {
|
|
236
|
-
"getnotes": {
|
|
237
|
-
"command": "uv",
|
|
238
|
-
"args": [
|
|
239
|
-
"tool",
|
|
240
|
-
"run",
|
|
241
|
-
"getnotes-mcp"
|
|
242
|
-
]
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
```
|
|
247
|
-
|
|
248
|
-
> **注意**:在使用 MCP 服务器前,确保你在终端执行过 `getnotes login` 获取了 Token。
|
|
249
|
-
|
|
250
|
-
### 可用 MCP Tools
|
|
251
|
-
|
|
252
|
-
- `download_notes(limit=10)`: 下载近期笔记为 Markdown 文件。
|
|
253
|
-
- `create_note(content)`: 直接提交文本建立新笔记。
|
|
254
|
-
- `create_link_note(url)`: 通过 AI 解析链接创建深度笔记。
|
|
255
|
-
- `list_notebooks()`: 获取你创建的知识库列表及对应 ID。
|
|
256
|
-
- `download_notebook(notebook_id)`: 下载指定的知识库内容。
|
|
257
|
-
- `list_subscribed_notebooks()`: 获取订阅知识库列表。
|
|
258
|
-
- `download_subscribed_notebook(notebook_id)`: 下载指定的订阅知识库。
|
|
259
|
-
|
|
260
276
|
## 📁 输出目录结构
|
|
261
277
|
|
|
262
278
|
默认输出到 `~/Downloads/getnotes_export/`:
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# getnotes-cli 🗂️
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
Get笔记 Cli 下载工具和 MCP 集成,支持自动登录、批量下载、知识库管理、笔记搜索、Markdown 导出、录音图片等附件下载。
|
|
4
|
+
|
|
5
5
|
> **初衷与设计理念:**
|
|
6
|
+
> - 🤖 **Agent 工作流**:提供标准化的 CLI 和 MCP 接入,便于无缝嵌入到各类大模型 Agent 或自动化流程中,充当高质量的个人知识上下文。
|
|
6
7
|
> - 📦 **数据自有化**:将你在平台积攒的笔记、知识库等数字资产完整下载到本地,实现数据的真正自有化与安全备份。
|
|
7
|
-
> - 🤖 **Agent 工作流**:提供标准化的 CLI 和本地文件系统接口,便于无缝嵌入到各类大模型 Agent 或自动化流程中,充当高质量的个人知识上下文。
|
|
8
8
|
|
|
9
9
|
[](https://python.org)
|
|
10
10
|
[](LICENSE)
|
|
@@ -14,7 +14,8 @@
|
|
|
14
14
|
- 🔐 **自动登录** — 通过 Chrome DevTools Protocol 自动获取 Bearer token,无需手动抓包
|
|
15
15
|
- 📥 **批量下载** — 分页拉取全部笔记,支持指定数量
|
|
16
16
|
- 📤 **新建笔记** — 支持通过本地 Markdown 或文本文件创建笔记,并支持自动上传内嵌图片
|
|
17
|
-
-
|
|
17
|
+
- � **笔记搜索** — 根据关键词搜索笔记,支持分页浏览
|
|
18
|
+
- �📚 **知识库管理** — 查看、下载我的知识库与订阅知识库
|
|
18
19
|
- 📝 **Markdown 导出** — 每条笔记保存为 Markdown,包含元信息、标签、正文、引用内容
|
|
19
20
|
- 🔊 **附件下载** — 自动下载音频、图片附件,并在 Markdown 中内嵌链接
|
|
20
21
|
- 💾 **缓存管理** — 自动跳过已下载且未变化的笔记,支持增量更新
|
|
@@ -23,7 +24,43 @@
|
|
|
23
24
|
- ⏱️ **可配置间隔** — 自定义请求间隔,避免频率限制
|
|
24
25
|
- 📊 **自动索引** — 自动生成笔记索引文件 `INDEX.md`
|
|
25
26
|
|
|
26
|
-
##
|
|
27
|
+
## 🤖 MCP 服务器
|
|
28
|
+
|
|
29
|
+
Get笔记 CLI 提供原生的 [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) 服务器支持,允许集成 [Claude Desktop](https://claude.ai/download) 等 AI 客户端直接为你管理笔记和知识库。
|
|
30
|
+
|
|
31
|
+
### 配置 Claude Desktop
|
|
32
|
+
|
|
33
|
+
编辑 Claude Desktop 配置文件 `claude_desktop_config.json`(通常在 `~/Library/Application Support/Claude/`):
|
|
34
|
+
|
|
35
|
+
```json
|
|
36
|
+
{
|
|
37
|
+
"mcpServers": {
|
|
38
|
+
"getnotes": {
|
|
39
|
+
"command": "uvx",
|
|
40
|
+
"args": [
|
|
41
|
+
"--from",
|
|
42
|
+
"getnotes-cli",
|
|
43
|
+
"getnotes-mcp"
|
|
44
|
+
]
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
> **注意**:在使用 MCP 服务器前,确保你在终端执行过 `getnotes login` 获取了 Token。
|
|
51
|
+
|
|
52
|
+
### 可用 MCP Tools
|
|
53
|
+
|
|
54
|
+
- `download_notes(limit=10)`: 下载近期笔记为 Markdown 文件。
|
|
55
|
+
- `create_note(content)`: 直接提交文本建立新笔记。
|
|
56
|
+
- `create_link_note(url)`: 通过 AI 解析链接创建深度笔记。
|
|
57
|
+
- `search_notes(query)`: 根据关键词搜索笔记并返回匹配结果。
|
|
58
|
+
- `list_notebooks()`: 获取你创建的知识库列表及对应 ID。
|
|
59
|
+
- `download_notebook(notebook_id)`: 下载指定的知识库内容。
|
|
60
|
+
- `list_subscribed_notebooks()`: 获取订阅知识库列表。
|
|
61
|
+
- `download_subscribed_notebook(notebook_id)`: 下载指定的订阅知识库。
|
|
62
|
+
|
|
63
|
+
## 📦 Cli 安装
|
|
27
64
|
|
|
28
65
|
### 使用 uv 安装(推荐)
|
|
29
66
|
|
|
@@ -75,6 +112,19 @@ getnotes create -f my_note.md -i img1.png -i img2.jpg
|
|
|
75
112
|
getnotes create-link <url>
|
|
76
113
|
```
|
|
77
114
|
|
|
115
|
+
### 搜索笔记
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
# 根据关键词搜索笔记
|
|
119
|
+
getnotes search "AI 提效"
|
|
120
|
+
|
|
121
|
+
# 查看第 2 页结果
|
|
122
|
+
getnotes search "AI 提效" --page 2
|
|
123
|
+
|
|
124
|
+
# 自定义每页数量
|
|
125
|
+
getnotes search "AI 提效" --page-size 20
|
|
126
|
+
```
|
|
127
|
+
|
|
78
128
|
### 下载笔记
|
|
79
129
|
|
|
80
130
|
```bash
|
|
@@ -199,46 +249,12 @@ getnotes --version
|
|
|
199
249
|
getnotes --help
|
|
200
250
|
getnotes create --help
|
|
201
251
|
getnotes download --help
|
|
252
|
+
getnotes search --help
|
|
202
253
|
getnotes notebook --help
|
|
203
254
|
getnotes subscribe --help
|
|
204
255
|
getnotes config --help
|
|
205
256
|
```
|
|
206
257
|
|
|
207
|
-
## 🤖 MCP 服务器
|
|
208
|
-
|
|
209
|
-
Get笔记 CLI 提供原生的 [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) 服务器支持,允许集成 [Claude Desktop](https://claude.ai/download) 等 AI 客户端直接为你管理笔记和知识库。
|
|
210
|
-
|
|
211
|
-
### 配置 Claude Desktop
|
|
212
|
-
|
|
213
|
-
编辑 Claude Desktop 配置文件 `claude_desktop_config.json`(通常在 `~/Library/Application Support/Claude/`):
|
|
214
|
-
|
|
215
|
-
```json
|
|
216
|
-
{
|
|
217
|
-
"mcpServers": {
|
|
218
|
-
"getnotes": {
|
|
219
|
-
"command": "uv",
|
|
220
|
-
"args": [
|
|
221
|
-
"tool",
|
|
222
|
-
"run",
|
|
223
|
-
"getnotes-mcp"
|
|
224
|
-
]
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
```
|
|
229
|
-
|
|
230
|
-
> **注意**:在使用 MCP 服务器前,确保你在终端执行过 `getnotes login` 获取了 Token。
|
|
231
|
-
|
|
232
|
-
### 可用 MCP Tools
|
|
233
|
-
|
|
234
|
-
- `download_notes(limit=10)`: 下载近期笔记为 Markdown 文件。
|
|
235
|
-
- `create_note(content)`: 直接提交文本建立新笔记。
|
|
236
|
-
- `create_link_note(url)`: 通过 AI 解析链接创建深度笔记。
|
|
237
|
-
- `list_notebooks()`: 获取你创建的知识库列表及对应 ID。
|
|
238
|
-
- `download_notebook(notebook_id)`: 下载指定的知识库内容。
|
|
239
|
-
- `list_subscribed_notebooks()`: 获取订阅知识库列表。
|
|
240
|
-
- `download_subscribed_notebook(notebook_id)`: 下载指定的订阅知识库。
|
|
241
|
-
|
|
242
258
|
## 📁 输出目录结构
|
|
243
259
|
|
|
244
260
|
默认输出到 `~/Downloads/getnotes_export/`:
|
|
@@ -175,6 +175,96 @@ def create_link(
|
|
|
175
175
|
raise typer.Exit(1)
|
|
176
176
|
|
|
177
177
|
|
|
178
|
+
# ========================================================================
|
|
179
|
+
# search 命令
|
|
180
|
+
# ========================================================================
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
@app.command()
|
|
184
|
+
def search(
|
|
185
|
+
query: str = typer.Argument(
|
|
186
|
+
...,
|
|
187
|
+
help="搜索关键词",
|
|
188
|
+
),
|
|
189
|
+
page: int = typer.Option(
|
|
190
|
+
1, "--page", "-p",
|
|
191
|
+
help="页码(从 1 开始)",
|
|
192
|
+
),
|
|
193
|
+
page_size: int = typer.Option(
|
|
194
|
+
10, "--page-size", "-s",
|
|
195
|
+
help="每页数量(默认 10)",
|
|
196
|
+
),
|
|
197
|
+
token: Optional[str] = typer.Option(
|
|
198
|
+
None, "--token", "-t",
|
|
199
|
+
help="直接传入 Bearer token(跳过缓存检查)",
|
|
200
|
+
),
|
|
201
|
+
) -> None:
|
|
202
|
+
"""🔍 搜索笔记 — 根据关键词搜索相关笔记"""
|
|
203
|
+
from getnotes_cli.searcher import NoteSearcher
|
|
204
|
+
|
|
205
|
+
auth = _get_auth(token)
|
|
206
|
+
|
|
207
|
+
console.print(f"\n[bold]🔍 正在搜索: {query}[/bold]\n")
|
|
208
|
+
|
|
209
|
+
try:
|
|
210
|
+
searcher = NoteSearcher(auth)
|
|
211
|
+
result = searcher.search(query, page=page, page_size=page_size)
|
|
212
|
+
except Exception as e:
|
|
213
|
+
console.print(f"[red]✗[/red] 搜索失败: {e}")
|
|
214
|
+
raise typer.Exit(1)
|
|
215
|
+
|
|
216
|
+
items = result["items"]
|
|
217
|
+
total = result["total"]
|
|
218
|
+
has_more = result["has_more"]
|
|
219
|
+
|
|
220
|
+
if not items:
|
|
221
|
+
console.print("[dim]未找到相关笔记。[/dim]")
|
|
222
|
+
return
|
|
223
|
+
|
|
224
|
+
table = Table(title=f"搜索结果 — '{query}' (共 {total} 条,第 {page} 页)")
|
|
225
|
+
table.add_column("#", style="dim", width=4)
|
|
226
|
+
table.add_column("标题 / 摘要", style="cyan", max_width=50)
|
|
227
|
+
table.add_column("类型", style="yellow", width=8)
|
|
228
|
+
table.add_column("标签", style="green", max_width=20)
|
|
229
|
+
table.add_column("创建时间", style="dim", width=12)
|
|
230
|
+
|
|
231
|
+
for i, item in enumerate(items, (page - 1) * page_size + 1):
|
|
232
|
+
title = NoteSearcher.strip_highlight(item.get("title", "").strip())
|
|
233
|
+
note_type = item.get("note_type", "")
|
|
234
|
+
|
|
235
|
+
# 获取高亮摘要
|
|
236
|
+
highlight = item.get("highlight_info", {})
|
|
237
|
+
snippet = ""
|
|
238
|
+
for key in ("title", "content", "ref_content"):
|
|
239
|
+
parts = highlight.get(key, [])
|
|
240
|
+
text = next((p for p in parts if p.strip()), "")
|
|
241
|
+
if text:
|
|
242
|
+
snippet = NoteSearcher.extract_highlight(text)
|
|
243
|
+
break
|
|
244
|
+
|
|
245
|
+
display = title if title else (snippet[:40] + "..." if len(snippet) > 40 else snippet)
|
|
246
|
+
if not display:
|
|
247
|
+
display = f"(ID: {item.get('note_id', '?')[-8:]})"
|
|
248
|
+
|
|
249
|
+
tags = ", ".join(
|
|
250
|
+
NoteSearcher.strip_highlight(t.get("name", "")) for t in item.get("tags", [])
|
|
251
|
+
if t.get("type") != "system"
|
|
252
|
+
)
|
|
253
|
+
if len(tags) > 20:
|
|
254
|
+
tags = tags[:18] + "…"
|
|
255
|
+
|
|
256
|
+
created = item.get("created_at", "")[:10]
|
|
257
|
+
|
|
258
|
+
table.add_row(str(i), display, note_type, tags, created)
|
|
259
|
+
|
|
260
|
+
console.print(table)
|
|
261
|
+
|
|
262
|
+
if has_more:
|
|
263
|
+
next_page = page + 1
|
|
264
|
+
console.print(f"\n[dim]还有更多结果,使用 `getnotes search \"{query}\" --page {next_page}` 查看下一页[/dim]")
|
|
265
|
+
console.print(f"[dim]共 {total} 条匹配结果[/dim]")
|
|
266
|
+
|
|
267
|
+
|
|
178
268
|
|
|
179
269
|
# ========================================================================
|
|
180
270
|
# download 命令
|
|
@@ -18,6 +18,9 @@ NOTE_CREATE_API_URL = "https://get-notes.luojilab.com/voicenotes/web/notes"
|
|
|
18
18
|
# 通过链接创建笔记流式 API
|
|
19
19
|
LINK_NOTE_CREATE_API_URL = "https://get-notes.luojilab.com/voicenotes/web/notes/stream"
|
|
20
20
|
|
|
21
|
+
# 搜索笔记 API
|
|
22
|
+
SEARCH_API_URL = "https://get-notes.luojilab.com/voicenotes/web/notes/search"
|
|
23
|
+
|
|
21
24
|
# 获取图片上传 Token API
|
|
22
25
|
IMAGE_TOKEN_API_URL = "https://get-notes.luojilab.com/voicenotes/web/token/image"
|
|
23
26
|
|
|
@@ -20,6 +20,7 @@ Tools available:
|
|
|
20
20
|
- download_notes(limit=10): Download recent notes as Markdown
|
|
21
21
|
- create_note(content): Create a new note from text content
|
|
22
22
|
- create_link_note(url): Use AI to create a deep note from a link URL
|
|
23
|
+
- search_notes(query): Search notes by keyword and return matching results
|
|
23
24
|
- list_notebooks(): Get a list of the user's notebooks
|
|
24
25
|
- download_notebook(notebook_id): Download a specific notebook
|
|
25
26
|
|
|
@@ -7,9 +7,10 @@ from getnotes_cli.auth import get_or_refresh_token
|
|
|
7
7
|
from getnotes_cli.config import DEFAULT_OUTPUT_DIR
|
|
8
8
|
from getnotes_cli.creator import NoteCreator
|
|
9
9
|
from getnotes_cli.downloader import NoteDownloader
|
|
10
|
+
from getnotes_cli.searcher import NoteSearcher
|
|
10
11
|
|
|
11
12
|
# We will export the functions that should be registered
|
|
12
|
-
__all__ = ["download_notes", "create_note", "create_link_note"]
|
|
13
|
+
__all__ = ["download_notes", "create_note", "create_link_note", "search_notes"]
|
|
13
14
|
|
|
14
15
|
def download_notes(limit: int = 10, force: bool = False) -> str:
|
|
15
16
|
"""Download the most recent notes to the default output directory.
|
|
@@ -107,3 +108,71 @@ def create_link_note(url: str) -> str:
|
|
|
107
108
|
|
|
108
109
|
except Exception as e:
|
|
109
110
|
return f"Error creating note from link: {e}"
|
|
111
|
+
|
|
112
|
+
def search_notes(query: str, page: int = 1, page_size: int = 10) -> str:
|
|
113
|
+
"""Search for notes by keyword and return matching results.
|
|
114
|
+
|
|
115
|
+
Args:
|
|
116
|
+
query: The search keyword or phrase to find relevant notes.
|
|
117
|
+
page: Page number (starting from 1, default: 1).
|
|
118
|
+
page_size: Number of results per page (default: 10).
|
|
119
|
+
|
|
120
|
+
Returns:
|
|
121
|
+
A formatted string with matching notes including titles, types, tags, and snippets.
|
|
122
|
+
"""
|
|
123
|
+
try:
|
|
124
|
+
auth = get_or_refresh_token()
|
|
125
|
+
except Exception as e:
|
|
126
|
+
return f"Error: Authentication failed. Please run 'getnotes login' in your terminal. ({e})"
|
|
127
|
+
|
|
128
|
+
searcher = NoteSearcher(auth)
|
|
129
|
+
|
|
130
|
+
try:
|
|
131
|
+
result = searcher.search(query, page=page, page_size=page_size)
|
|
132
|
+
except Exception as e:
|
|
133
|
+
return f"Error searching notes: {e}"
|
|
134
|
+
|
|
135
|
+
items = result["items"]
|
|
136
|
+
total = result["total"]
|
|
137
|
+
has_more = result["has_more"]
|
|
138
|
+
|
|
139
|
+
if not items:
|
|
140
|
+
return f"No notes found matching '{query}'."
|
|
141
|
+
|
|
142
|
+
lines = [f"Found {total} notes matching '{query}' (page {page}):"]
|
|
143
|
+
lines.append("")
|
|
144
|
+
|
|
145
|
+
for i, item in enumerate(items, (page - 1) * page_size + 1):
|
|
146
|
+
title = NoteSearcher.strip_highlight(item.get("title", "").strip())
|
|
147
|
+
note_type = item.get("note_type", "")
|
|
148
|
+
note_id = item.get("note_id", "")
|
|
149
|
+
created = item.get("created_at", "")
|
|
150
|
+
|
|
151
|
+
# Get highlight snippet
|
|
152
|
+
highlight = item.get("highlight_info", {})
|
|
153
|
+
snippet = ""
|
|
154
|
+
for key in ("title", "content", "ref_content"):
|
|
155
|
+
parts = highlight.get(key, [])
|
|
156
|
+
text = next((p for p in parts if p.strip()), "")
|
|
157
|
+
if text:
|
|
158
|
+
snippet = NoteSearcher.extract_highlight(text)
|
|
159
|
+
break
|
|
160
|
+
|
|
161
|
+
display = title or snippet[:60] or f"(Note {note_id[-8:]})"
|
|
162
|
+
tags = ", ".join(
|
|
163
|
+
NoteSearcher.strip_highlight(t.get("name", "")) for t in item.get("tags", [])
|
|
164
|
+
if t.get("type") != "system"
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
lines.append(f"{i}. [{note_type}] {display}")
|
|
168
|
+
if tags:
|
|
169
|
+
lines.append(f" Tags: {tags}")
|
|
170
|
+
if snippet and title:
|
|
171
|
+
lines.append(f" Snippet: {snippet}")
|
|
172
|
+
lines.append(f" ID: {note_id} | Created: {created}")
|
|
173
|
+
lines.append("")
|
|
174
|
+
|
|
175
|
+
if has_more:
|
|
176
|
+
lines.append(f"(More results available — use page={page + 1} to see next page)")
|
|
177
|
+
|
|
178
|
+
return "\n".join(lines)
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"""笔记搜索 — 根据关键词搜索笔记"""
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
|
|
5
|
+
import httpx
|
|
6
|
+
|
|
7
|
+
from getnotes_cli.auth import AuthToken
|
|
8
|
+
from getnotes_cli.config import SEARCH_API_URL
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class NoteSearcher:
|
|
12
|
+
"""笔记搜索器"""
|
|
13
|
+
|
|
14
|
+
def __init__(self, token: AuthToken):
|
|
15
|
+
self.token = token
|
|
16
|
+
self.client = httpx.Client(timeout=30)
|
|
17
|
+
|
|
18
|
+
def search(
|
|
19
|
+
self,
|
|
20
|
+
query: str,
|
|
21
|
+
page: int = 1,
|
|
22
|
+
page_size: int = 10,
|
|
23
|
+
) -> dict:
|
|
24
|
+
"""搜索笔记
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
query: 搜索关键词
|
|
28
|
+
page: 页码(从 1 开始)
|
|
29
|
+
page_size: 每页数量
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
包含 items, total, has_more 的字典
|
|
33
|
+
"""
|
|
34
|
+
params = {
|
|
35
|
+
"page": page,
|
|
36
|
+
"page_size": page_size,
|
|
37
|
+
"query": query,
|
|
38
|
+
}
|
|
39
|
+
headers = self.token.get_headers()
|
|
40
|
+
resp = self.client.get(
|
|
41
|
+
SEARCH_API_URL,
|
|
42
|
+
headers=headers,
|
|
43
|
+
params=params,
|
|
44
|
+
timeout=30,
|
|
45
|
+
)
|
|
46
|
+
resp.raise_for_status()
|
|
47
|
+
data = resp.json()
|
|
48
|
+
|
|
49
|
+
content = data.get("c", {})
|
|
50
|
+
return {
|
|
51
|
+
"items": content.get("items", []),
|
|
52
|
+
"total": content.get("total", 0),
|
|
53
|
+
"has_more": content.get("has_more", False),
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
@staticmethod
|
|
57
|
+
def strip_highlight(text: str) -> str:
|
|
58
|
+
"""移除高亮标签 <hl>...</hl>,保留内部文本"""
|
|
59
|
+
return re.sub(r"</?hl>", "", text)
|
|
60
|
+
|
|
61
|
+
@staticmethod
|
|
62
|
+
def extract_highlight(text: str) -> str:
|
|
63
|
+
"""提取高亮文本片段,用于展示"""
|
|
64
|
+
text = text.replace("\\n", "\n").strip()
|
|
65
|
+
# 移除 hl 标签
|
|
66
|
+
clean = re.sub(r"</?hl>", "", text)
|
|
67
|
+
# 截断过长内容
|
|
68
|
+
if len(clean) > 120:
|
|
69
|
+
clean = clean[:120] + "..."
|
|
70
|
+
return clean
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|