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.
Files changed (28) hide show
  1. {getnotes_cli-0.1.3 → getnotes_cli-0.1.5}/CHANGELOG.md +6 -0
  2. {getnotes_cli-0.1.3 → getnotes_cli-0.1.5}/PKG-INFO +57 -41
  3. {getnotes_cli-0.1.3 → getnotes_cli-0.1.5}/README.md +56 -40
  4. {getnotes_cli-0.1.3 → getnotes_cli-0.1.5}/pyproject.toml +1 -1
  5. {getnotes_cli-0.1.3 → getnotes_cli-0.1.5}/src/getnotes_cli/cli.py +90 -0
  6. {getnotes_cli-0.1.3 → getnotes_cli-0.1.5}/src/getnotes_cli/config.py +3 -0
  7. {getnotes_cli-0.1.3 → getnotes_cli-0.1.5}/src/getnotes_cli/mcp/server.py +1 -0
  8. {getnotes_cli-0.1.3 → getnotes_cli-0.1.5}/src/getnotes_cli/mcp/tools/notes.py +70 -1
  9. getnotes_cli-0.1.5/src/getnotes_cli/searcher.py +70 -0
  10. {getnotes_cli-0.1.3 → getnotes_cli-0.1.5}/uv.lock +1 -1
  11. {getnotes_cli-0.1.3 → getnotes_cli-0.1.5}/.agent/workflows/release.md +0 -0
  12. {getnotes_cli-0.1.3 → getnotes_cli-0.1.5}/.github/workflows/publish.yml +0 -0
  13. {getnotes_cli-0.1.3 → getnotes_cli-0.1.5}/.gitignore +0 -0
  14. {getnotes_cli-0.1.3 → getnotes_cli-0.1.5}/LICENSE +0 -0
  15. {getnotes_cli-0.1.3 → getnotes_cli-0.1.5}/src/getnotes_cli/__init__.py +0 -0
  16. {getnotes_cli-0.1.3 → getnotes_cli-0.1.5}/src/getnotes_cli/auth.py +0 -0
  17. {getnotes_cli-0.1.3 → getnotes_cli-0.1.5}/src/getnotes_cli/cache.py +0 -0
  18. {getnotes_cli-0.1.3 → getnotes_cli-0.1.5}/src/getnotes_cli/cdp.py +0 -0
  19. {getnotes_cli-0.1.3 → getnotes_cli-0.1.5}/src/getnotes_cli/creator.py +0 -0
  20. {getnotes_cli-0.1.3 → getnotes_cli-0.1.5}/src/getnotes_cli/downloader.py +0 -0
  21. {getnotes_cli-0.1.3 → getnotes_cli-0.1.5}/src/getnotes_cli/markdown.py +0 -0
  22. {getnotes_cli-0.1.3 → getnotes_cli-0.1.5}/src/getnotes_cli/mcp/__init__.py +0 -0
  23. {getnotes_cli-0.1.3 → getnotes_cli-0.1.5}/src/getnotes_cli/mcp/tools/__init__.py +0 -0
  24. {getnotes_cli-0.1.3 → getnotes_cli-0.1.5}/src/getnotes_cli/mcp/tools/_utils.py +0 -0
  25. {getnotes_cli-0.1.3 → getnotes_cli-0.1.5}/src/getnotes_cli/mcp/tools/notebooks.py +0 -0
  26. {getnotes_cli-0.1.3 → getnotes_cli-0.1.5}/src/getnotes_cli/notebook.py +0 -0
  27. {getnotes_cli-0.1.3 → getnotes_cli-0.1.5}/src/getnotes_cli/notebook_downloader.py +0 -0
  28. {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
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
- > Get笔记 CLI 下载工具 自动登录、批量下载、知识库管理、Markdown 导出、录音图片等附件下载
22
- >
21
+ Get笔记 Cli 下载工具和 MCP 集成,支持自动登录、批量下载、知识库管理、笔记搜索、Markdown 导出、录音图片等附件下载。
22
+
23
23
  > **初衷与设计理念:**
24
+ > - 🤖 **Agent 工作流**:提供标准化的 CLI 和 MCP 接入,便于无缝嵌入到各类大模型 Agent 或自动化流程中,充当高质量的个人知识上下文。
24
25
  > - 📦 **数据自有化**:将你在平台积攒的笔记、知识库等数字资产完整下载到本地,实现数据的真正自有化与安全备份。
25
- > - 🤖 **Agent 工作流**:提供标准化的 CLI 和本地文件系统接口,便于无缝嵌入到各类大模型 Agent 或自动化流程中,充当高质量的个人知识上下文。
26
26
 
27
27
  [![Python](https://img.shields.io/badge/Python-3.10+-blue.svg)](https://python.org)
28
28
  [![License](https://img.shields.io/badge/License-MIT-green.svg)](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
- > Get笔记 CLI 下载工具 自动登录、批量下载、知识库管理、Markdown 导出、录音图片等附件下载
4
- >
3
+ Get笔记 Cli 下载工具和 MCP 集成,支持自动登录、批量下载、知识库管理、笔记搜索、Markdown 导出、录音图片等附件下载。
4
+
5
5
  > **初衷与设计理念:**
6
+ > - 🤖 **Agent 工作流**:提供标准化的 CLI 和 MCP 接入,便于无缝嵌入到各类大模型 Agent 或自动化流程中,充当高质量的个人知识上下文。
6
7
  > - 📦 **数据自有化**:将你在平台积攒的笔记、知识库等数字资产完整下载到本地,实现数据的真正自有化与安全备份。
7
- > - 🤖 **Agent 工作流**:提供标准化的 CLI 和本地文件系统接口,便于无缝嵌入到各类大模型 Agent 或自动化流程中,充当高质量的个人知识上下文。
8
8
 
9
9
  [![Python](https://img.shields.io/badge/Python-3.10+-blue.svg)](https://python.org)
10
10
  [![License](https://img.shields.io/badge/License-MIT-green.svg)](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/`:
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "getnotes-cli"
7
- version = "0.1.3"
7
+ version = "0.1.5"
8
8
  description = "Get 笔记 CLI 下载工具 — 自动登录、批量下载、Markdown 导出"
9
9
  readme = "README.md"
10
10
  license = {text = "MIT"}
@@ -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
@@ -476,7 +476,7 @@ wheels = [
476
476
 
477
477
  [[package]]
478
478
  name = "getnotes-cli"
479
- version = "0.1.2"
479
+ version = "0.1.4"
480
480
  source = { editable = "." }
481
481
  dependencies = [
482
482
  { name = "fastmcp" },
File without changes
File without changes