read-cli 0.2.0__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.
- read_cli-0.2.0/.gitignore +23 -0
- read_cli-0.2.0/ARCHITECTURE.md +574 -0
- read_cli-0.2.0/CLAUDE.md +280 -0
- read_cli-0.2.0/CONTRIBUTING.md +82 -0
- read_cli-0.2.0/LICENSE +21 -0
- read_cli-0.2.0/PKG-INFO +218 -0
- read_cli-0.2.0/README.md +193 -0
- read_cli-0.2.0/REVIEW_REPORT.md +335 -0
- read_cli-0.2.0/WHY.md +249 -0
- read_cli-0.2.0/agent/HEARTBEAT.md +1 -0
- read_cli-0.2.0/agent/IDENTITY.md +36 -0
- read_cli-0.2.0/agent/MEMORY.md.template +21 -0
- read_cli-0.2.0/agent/OVERVIEW.md +51 -0
- read_cli-0.2.0/agent/SOUL.md +164 -0
- read_cli-0.2.0/agent/TOOLS.md +96 -0
- read_cli-0.2.0/agent/USER.md +27 -0
- read_cli-0.2.0/docs/API_REFERENCE.md +349 -0
- read_cli-0.2.0/docs/GITHUB_RELEASE.md +85 -0
- read_cli-0.2.0/docs/OPENCLAW_TOOLS.md +126 -0
- read_cli-0.2.0/docs/PRODUCT_HUNT.md +52 -0
- read_cli-0.2.0/docs/TWITTER_POSTS.md +150 -0
- read_cli-0.2.0/docs/V2EX_POST.md +109 -0
- read_cli-0.2.0/pyproject.toml +54 -0
- read_cli-0.2.0/readme.md +193 -0
- read_cli-0.2.0/src/read/__init__.py +11 -0
- read_cli-0.2.0/src/read/cli.py +138 -0
- read_cli-0.2.0/src/read/commands/__init__.py +17 -0
- read_cli-0.2.0/src/read/commands/add.py +32 -0
- read_cli-0.2.0/src/read/commands/delete.py +49 -0
- read_cli-0.2.0/src/read/commands/get.py +32 -0
- read_cli-0.2.0/src/read/commands/init.py +12 -0
- read_cli-0.2.0/src/read/commands/ls.py +47 -0
- read_cli-0.2.0/src/read/commands/search.py +31 -0
- read_cli-0.2.0/src/read/config.py +36 -0
- read_cli-0.2.0/src/read/const.py +28 -0
- read_cli-0.2.0/src/read/core/__init__.py +9 -0
- read_cli-0.2.0/src/read/core/client.py +217 -0
- read_cli-0.2.0/src/read/core/models.py +80 -0
- read_cli-0.2.0/src/read/db/__init__.py +12 -0
- read_cli-0.2.0/src/read/db/connection.py +37 -0
- read_cli-0.2.0/src/read/db/schema.py +60 -0
- read_cli-0.2.0/src/read/db/utils.py +272 -0
- read_cli-0.2.0/src/read/mcp/__init__.py +7 -0
- read_cli-0.2.0/tests/__init__.py +1 -0
- read_cli-0.2.0/tests/conftest.py +39 -0
- read_cli-0.2.0/tests/integration/__init__.py +1 -0
- read_cli-0.2.0/tests/integration/test_commands.py +132 -0
- read_cli-0.2.0/tests/integration/test_workflow.py +95 -0
- read_cli-0.2.0/tests/unit/__init__.py +1 -0
- read_cli-0.2.0/tests/unit/core/__init__.py +1 -0
- read_cli-0.2.0/tests/unit/core/test_client.py +133 -0
- read_cli-0.2.0/tests/unit/core/test_models.py +88 -0
- read_cli-0.2.0/tests/unit/db/__init__.py +1 -0
- read_cli-0.2.0/tests/unit/db/test_connection.py +66 -0
- read_cli-0.2.0/tests/unit/db/test_utils.py +178 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
dist/
|
|
6
|
+
build/
|
|
7
|
+
.eggs/
|
|
8
|
+
|
|
9
|
+
# Coverage
|
|
10
|
+
.coverage
|
|
11
|
+
htmlcov/
|
|
12
|
+
.pytest_cache/
|
|
13
|
+
|
|
14
|
+
# Agent workspace (用户数据)
|
|
15
|
+
agent/MEMORY.md
|
|
16
|
+
*.db
|
|
17
|
+
|
|
18
|
+
# OS
|
|
19
|
+
.DS_Store
|
|
20
|
+
|
|
21
|
+
# IDE
|
|
22
|
+
.vscode/
|
|
23
|
+
.idea/
|
|
@@ -0,0 +1,574 @@
|
|
|
1
|
+
# 读咚咚 (Read) - 架构设计文档
|
|
2
|
+
|
|
3
|
+
> 设计日期:2026-03-15
|
|
4
|
+
> 版本:v1.0
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## 产品定位
|
|
9
|
+
|
|
10
|
+
**读咚咚** 是个人知识数据层,支持多种客户端访问。
|
|
11
|
+
|
|
12
|
+
本地、私有、可编程的个人知识基础设施。
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## 核心架构
|
|
17
|
+
|
|
18
|
+
### 四层架构
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
22
|
+
│ 客户端层 │
|
|
23
|
+
│ (浏览器插件、CLI、Alfred Workflow、快捷指令、GUI...) │
|
|
24
|
+
└────────────────────────────┬────────────────────────────────┘
|
|
25
|
+
│
|
|
26
|
+
┌────────▼────────┐
|
|
27
|
+
│ 接口层 │
|
|
28
|
+
│ (CLI/HTTP/MCP) │
|
|
29
|
+
└────────┬────────┘
|
|
30
|
+
│
|
|
31
|
+
┌────────▼────────┐
|
|
32
|
+
│ 核心层 │
|
|
33
|
+
│ (Core Library) │
|
|
34
|
+
└────────┬────────┘
|
|
35
|
+
│
|
|
36
|
+
┌────────▼────────┐
|
|
37
|
+
│ 数据层 │
|
|
38
|
+
│ (SQLite DB) │
|
|
39
|
+
└─────────────────┘
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
**核心原则**:
|
|
43
|
+
- **Core Library 是核心资产,不是 CLI**
|
|
44
|
+
- **CLI 是核心接口,不是唯一入口**
|
|
45
|
+
- **数据层只管存储和接口,不管用户交互**
|
|
46
|
+
- **客户端各自优化特定场景的 UX**
|
|
47
|
+
- **统一数据空间,人类和 Agent 可以协作**
|
|
48
|
+
|
|
49
|
+
### 为什么 Core Library 是核心
|
|
50
|
+
|
|
51
|
+
| 思维方式 | CLI 核心 | Core Library 核心 |
|
|
52
|
+
|----------|----------|-------------------|
|
|
53
|
+
| 产品性质 | 命令行工具 | Python 库 |
|
|
54
|
+
| 核心用户 | 终端用户 | 开发者/Agent |
|
|
55
|
+
| 扩展方式 | 加新命令 | 加新客户端 |
|
|
56
|
+
| 长期价值 | 功能完整性 | 接口稳定性 |
|
|
57
|
+
| 商业模式 | 卖便利性 | 开源核心 + 付费支持 |
|
|
58
|
+
|
|
59
|
+
**如果定位是"数据基础设施",那 Core Library 必须是核心。**
|
|
60
|
+
|
|
61
|
+
这就像 PostgreSQL:
|
|
62
|
+
- 核心开源(Core Library)
|
|
63
|
+
- 付费企业版(托管服务)
|
|
64
|
+
- 技术支持服务(开发者工具)
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## 1. 项目目录结构
|
|
69
|
+
|
|
70
|
+
```
|
|
71
|
+
ReadDongDong/
|
|
72
|
+
├── readme.md # 产品介绍
|
|
73
|
+
├── CLAUDE.md # Claude Code 指导文档
|
|
74
|
+
├── ARCHITECTURE.md # 本文档
|
|
75
|
+
├── pyproject.toml # 项目配置
|
|
76
|
+
│
|
|
77
|
+
├── src/
|
|
78
|
+
│ └── read/ # 主包
|
|
79
|
+
│ ├── __init__.py # version = "0.1.0"
|
|
80
|
+
│ ├── cli.py # Typer 主入口
|
|
81
|
+
│ ├── const.py # 常量定义
|
|
82
|
+
│ │
|
|
83
|
+
│ ├── core/ # 核心库(v0.1 抽离)
|
|
84
|
+
│ │ ├── __init__.py
|
|
85
|
+
│ │ ├── client.py # Python SDK (read.core.Client)
|
|
86
|
+
│ │ └── models.py # 数据模型
|
|
87
|
+
│ │
|
|
88
|
+
│ ├── mcp/ # MCP Server (v0.2,关键验证点)
|
|
89
|
+
│ │ └── server.py # MCP 服务
|
|
90
|
+
│ │
|
|
91
|
+
│ ├── db/ # 数据库层
|
|
92
|
+
│ │ ├── __init__.py
|
|
93
|
+
│ │ ├── connection.py # SQLite 连接管理
|
|
94
|
+
│ │ ├── schema.py # 表结构和初始化
|
|
95
|
+
│ │ └── utils.py # CRUD 操作封装
|
|
96
|
+
│ │
|
|
97
|
+
│ └── commands/ # 命令实现(调用 core)
|
|
98
|
+
│ ├── __init__.py
|
|
99
|
+
│ ├── init.py # read init
|
|
100
|
+
│ ├── add.py # read add
|
|
101
|
+
│ ├── ls.py # read ls
|
|
102
|
+
│ ├── get.py # read get
|
|
103
|
+
│ ├── delete.py # read delete
|
|
104
|
+
│ └── search.py # read search
|
|
105
|
+
│
|
|
106
|
+
├── tests/ # 测试
|
|
107
|
+
│ ├── __init__.py
|
|
108
|
+
│ ├── conftest.py
|
|
109
|
+
│ ├── unit/
|
|
110
|
+
│ └── integration/
|
|
111
|
+
│
|
|
112
|
+
└── .read/ # 数据目录(运行时生成)
|
|
113
|
+
└── read.db
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## 2. 数据库层设计
|
|
119
|
+
|
|
120
|
+
### 2.1 表结构
|
|
121
|
+
|
|
122
|
+
```sql
|
|
123
|
+
CREATE TABLE items (
|
|
124
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
125
|
+
content TEXT, -- 摘录内容(可选)
|
|
126
|
+
url TEXT, -- 链接(可选)
|
|
127
|
+
source TEXT, -- 来源备注(可选)
|
|
128
|
+
type TEXT DEFAULT 'quote', -- 数据类型:quote/article/code
|
|
129
|
+
metadata TEXT, -- JSON 扩展字段(v0.1 预留)
|
|
130
|
+
created_at TEXT DEFAULT CURRENT_TIMESTAMP,
|
|
131
|
+
updated_at TEXT DEFAULT CURRENT_TIMESTAMP, -- 同步预留(v0.1)
|
|
132
|
+
|
|
133
|
+
-- content 和 url 至少有一个不为空
|
|
134
|
+
CHECK(content IS NOT NULL OR url IS NOT NULL)
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
-- 索引:加速查询
|
|
138
|
+
CREATE INDEX idx_items_created_at ON items(created_at DESC);
|
|
139
|
+
CREATE INDEX idx_items_content ON items(content);
|
|
140
|
+
CREATE INDEX idx_items_url ON items(url);
|
|
141
|
+
CREATE INDEX idx_items_type ON items(type); -- 类型筛选(v0.1 预留)
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### 2.2 字段说明
|
|
145
|
+
|
|
146
|
+
| 字段 | 类型 | 必填 | 说明 |
|
|
147
|
+
|------|------|------|------|
|
|
148
|
+
| id | INTEGER | 是 | 主键,自增 |
|
|
149
|
+
| content | TEXT | 否* | 摘录内容,与 url 至少一个必填 |
|
|
150
|
+
| url | TEXT | 否* | 链接,与 content 至少一个必填 |
|
|
151
|
+
| source | TEXT | 否 | 来源备注(如:微信、书名) |
|
|
152
|
+
| type | TEXT | 否 | 数据类型:quote/article/code(v0.1 预留) |
|
|
153
|
+
| metadata | TEXT | 否 | JSON 扩展字段(v0.1 预留,为 Agent 补充信息) |
|
|
154
|
+
| created_at | TEXT | 是 | 创建时间,ISO 8601 格式 |
|
|
155
|
+
| updated_at | TEXT | 是 | 更新时间,同步预留(v0.1) |
|
|
156
|
+
|
|
157
|
+
### 2.3 连接管理 (connection.py)
|
|
158
|
+
|
|
159
|
+
```python
|
|
160
|
+
"""职责:
|
|
161
|
+
- 单例模式管理 SQLite 连接
|
|
162
|
+
- 确保数据库目录存在
|
|
163
|
+
- 提供线程安全的连接
|
|
164
|
+
- 提供上下文管理器
|
|
165
|
+
"""
|
|
166
|
+
|
|
167
|
+
import sqlite3
|
|
168
|
+
import threading
|
|
169
|
+
from contextlib import contextmanager
|
|
170
|
+
from functools import lru_cache
|
|
171
|
+
from pathlib import Path
|
|
172
|
+
from typing import Iterator
|
|
173
|
+
|
|
174
|
+
_connection: sqlite3.Connection | None = None
|
|
175
|
+
_lock = threading.Lock()
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
@lru_cache(maxsize=1)
|
|
179
|
+
def get_db_path() -> Path:
|
|
180
|
+
"""获取数据库路径"""
|
|
181
|
+
db_path = Path.home() / ".read" / "read.db"
|
|
182
|
+
db_path.parent.mkdir(parents=True, exist_ok=True)
|
|
183
|
+
return db_path
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def get_connection() -> sqlite3.Connection:
|
|
187
|
+
"""获取连接(单例)"""
|
|
188
|
+
# ...(实现)
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
@contextmanager
|
|
192
|
+
def get_cursor() -> Iterator[sqlite3.Cursor]:
|
|
193
|
+
"""获取游标(自动事务管理)"""
|
|
194
|
+
# ...(实现)
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### 2.4 CRUD 操作 (utils.py)
|
|
198
|
+
|
|
199
|
+
```python
|
|
200
|
+
"""职责:
|
|
201
|
+
- 封装所有数据库操作
|
|
202
|
+
- 提供类型友好的接口
|
|
203
|
+
- 处理业务逻辑验证
|
|
204
|
+
"""
|
|
205
|
+
|
|
206
|
+
def add_item(
|
|
207
|
+
content: Optional[str] = None,
|
|
208
|
+
url: Optional[str] = None,
|
|
209
|
+
source: Optional[str] = None,
|
|
210
|
+
) -> int:
|
|
211
|
+
"""添加摘录,返回新 ID"""
|
|
212
|
+
|
|
213
|
+
def list_items(
|
|
214
|
+
limit: int = 20,
|
|
215
|
+
offset: int = 0,
|
|
216
|
+
item_type: Optional[str] = None,
|
|
217
|
+
order: str = "desc",
|
|
218
|
+
) -> list[dict]:
|
|
219
|
+
"""列出摘录"""
|
|
220
|
+
|
|
221
|
+
def get_item(item_id: int) -> Optional[dict]:
|
|
222
|
+
"""获取单条"""
|
|
223
|
+
|
|
224
|
+
def delete_item(item_id: int) -> bool:
|
|
225
|
+
"""删除单条"""
|
|
226
|
+
|
|
227
|
+
def search_items(
|
|
228
|
+
query: str,
|
|
229
|
+
field: Optional[str] = None,
|
|
230
|
+
limit: int = 20,
|
|
231
|
+
) -> list[dict]:
|
|
232
|
+
"""搜索"""
|
|
233
|
+
|
|
234
|
+
def count_total() -> int:
|
|
235
|
+
"""总数"""
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
---
|
|
239
|
+
|
|
240
|
+
## 3. CLI 层设计
|
|
241
|
+
|
|
242
|
+
### 3.1 统一输出格式 (cli.py)
|
|
243
|
+
|
|
244
|
+
```python
|
|
245
|
+
"""职责:
|
|
246
|
+
- 统一的 JSON 输出
|
|
247
|
+
- 统一的错误处理
|
|
248
|
+
- Typer 应用配置
|
|
249
|
+
"""
|
|
250
|
+
|
|
251
|
+
import json
|
|
252
|
+
import typer
|
|
253
|
+
from typing import Any
|
|
254
|
+
|
|
255
|
+
app = typer.Typer(
|
|
256
|
+
name="read",
|
|
257
|
+
help="读咚咚 (Read) - 个人知识数据层的命令行接口",
|
|
258
|
+
no_args_is_help=True,
|
|
259
|
+
add_completion=False,
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
def output(data: Any, success: bool = True) -> None:
|
|
264
|
+
"""输出 JSON 格式"""
|
|
265
|
+
result: dict[str, Any] = {"success": success}
|
|
266
|
+
if success:
|
|
267
|
+
result["data"] = data
|
|
268
|
+
else:
|
|
269
|
+
result["error"] = data
|
|
270
|
+
typer.echo(json.dumps(result, ensure_ascii=False, indent=2))
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
def handle_error(e: Exception) -> None:
|
|
274
|
+
"""处理异常并输出结构化错误"""
|
|
275
|
+
error_info: dict[str, str] = {
|
|
276
|
+
"code": type(e).__name__,
|
|
277
|
+
"message": str(e)
|
|
278
|
+
}
|
|
279
|
+
output(error_info, success=False)
|
|
280
|
+
raise typer.Exit(code=1)
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
### 3.2 命令模板
|
|
284
|
+
|
|
285
|
+
每个命令文件遵循统一结构:
|
|
286
|
+
|
|
287
|
+
```python
|
|
288
|
+
"""命令说明"""
|
|
289
|
+
|
|
290
|
+
import typer
|
|
291
|
+
from read.cli import output, handle_error
|
|
292
|
+
from read.db.utils import add_item
|
|
293
|
+
|
|
294
|
+
def cmd_add(
|
|
295
|
+
content: str = typer.Argument(None, help="摘录内容"),
|
|
296
|
+
url: str = typer.Option(None, "--url", help="链接"),
|
|
297
|
+
source: str = typer.Option(None, "--source", help="来源备注"),
|
|
298
|
+
):
|
|
299
|
+
"""添加摘录或链接"""
|
|
300
|
+
try:
|
|
301
|
+
item_id = add_item(content=content, url=url, source=source)
|
|
302
|
+
item = get_item(item_id)
|
|
303
|
+
output(item)
|
|
304
|
+
except Exception as e:
|
|
305
|
+
handle_error(e)
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
---
|
|
309
|
+
|
|
310
|
+
## 4. 命令详细设计
|
|
311
|
+
|
|
312
|
+
### 4.1 init - 初始化
|
|
313
|
+
|
|
314
|
+
```bash
|
|
315
|
+
read init
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
```json
|
|
319
|
+
{
|
|
320
|
+
"success": true,
|
|
321
|
+
"data": {
|
|
322
|
+
"message": "Read database initialized",
|
|
323
|
+
"db_path": "/Users/xxx/.read/read.db",
|
|
324
|
+
"version": "0.1.0"
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
### 4.2 add - 添加
|
|
330
|
+
|
|
331
|
+
```bash
|
|
332
|
+
read add "一句话说得真好"
|
|
333
|
+
read add "一句话" --url "https://example.com"
|
|
334
|
+
read add --url "https://mp.weixin.qq.com/s/xxx"
|
|
335
|
+
read add "一句话" --url "..." --source "CLAUDE.md"
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
**验证规则**:
|
|
339
|
+
- content 和 url 至少一个不为空
|
|
340
|
+
- url 格式基本验证
|
|
341
|
+
|
|
342
|
+
### 4.3 ls - 列出
|
|
343
|
+
|
|
344
|
+
```bash
|
|
345
|
+
read ls # 默认 20 条
|
|
346
|
+
read ls --limit 50
|
|
347
|
+
read ls --type content # 只摘录
|
|
348
|
+
read ls --type link # 只链接
|
|
349
|
+
read ls --order asc # 正序
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
```json
|
|
353
|
+
{
|
|
354
|
+
"success": true,
|
|
355
|
+
"data": {
|
|
356
|
+
"total": 150,
|
|
357
|
+
"items": [...]
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
### 4.4 get - 获取
|
|
363
|
+
|
|
364
|
+
```bash
|
|
365
|
+
read get 123
|
|
366
|
+
read get 123 --field content
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
### 4.5 delete - 删除
|
|
370
|
+
|
|
371
|
+
```bash
|
|
372
|
+
read delete 123
|
|
373
|
+
read delete 123 124 125
|
|
374
|
+
read delete 123 --force
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
### 4.6 search - 搜索
|
|
378
|
+
|
|
379
|
+
```bash
|
|
380
|
+
read search "AI"
|
|
381
|
+
read search "微信" --field source
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
---
|
|
385
|
+
|
|
386
|
+
## 5. 配置文件
|
|
387
|
+
|
|
388
|
+
### 5.1 pyproject.toml
|
|
389
|
+
|
|
390
|
+
```toml
|
|
391
|
+
[project]
|
|
392
|
+
name = "read-cli"
|
|
393
|
+
version = "0.1.0"
|
|
394
|
+
description = "读咚咚 - 个人知识数据层的命令行接口"
|
|
395
|
+
readme = "readme.md"
|
|
396
|
+
requires-python = ">=3.11"
|
|
397
|
+
dependencies = [
|
|
398
|
+
"typer>=0.12.0",
|
|
399
|
+
]
|
|
400
|
+
|
|
401
|
+
[project.scripts]
|
|
402
|
+
read = "read.cli:app"
|
|
403
|
+
|
|
404
|
+
[build-system]
|
|
405
|
+
requires = ["hatchling"]
|
|
406
|
+
build-backend = "hatchling.build"
|
|
407
|
+
|
|
408
|
+
[tool.hatch.build.targets.wheel]
|
|
409
|
+
packages = ["src/read"]
|
|
410
|
+
|
|
411
|
+
[tool.pytest.ini_options]
|
|
412
|
+
minversion = "7.0"
|
|
413
|
+
testpaths = ["tests"]
|
|
414
|
+
pythonpath = ["src"]
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
---
|
|
418
|
+
|
|
419
|
+
## 6. 设计决策记录
|
|
420
|
+
|
|
421
|
+
### 6.1 为什么用 SQLite 单文件?
|
|
422
|
+
|
|
423
|
+
- 零配置
|
|
424
|
+
- 适合个人数据量
|
|
425
|
+
- 易于备份(复制一个文件即可)
|
|
426
|
+
- Python 内置支持
|
|
427
|
+
|
|
428
|
+
### 6.2 为什么不做 tags 表?
|
|
429
|
+
|
|
430
|
+
- 先验证核心需求
|
|
431
|
+
- 够用就行,不过度设计
|
|
432
|
+
- 以后可以通过 source 字段简单实现
|
|
433
|
+
|
|
434
|
+
### 6.3 为什么不做 status 字段?
|
|
435
|
+
|
|
436
|
+
- 只管收集,不管管理
|
|
437
|
+
- 减少心智负担
|
|
438
|
+
- 如果需要,可以通过 created_at 推断
|
|
439
|
+
|
|
440
|
+
### 6.4 为什么用 Typer?
|
|
441
|
+
|
|
442
|
+
- 类型提示友好
|
|
443
|
+
- 自动生成帮助信息
|
|
444
|
+
- Click 的现代替代品
|
|
445
|
+
|
|
446
|
+
---
|
|
447
|
+
|
|
448
|
+
## 7. 实现顺序
|
|
449
|
+
|
|
450
|
+
1. **基础设施**
|
|
451
|
+
- [ ] 项目目录结构
|
|
452
|
+
- [ ] pyproject.toml
|
|
453
|
+
- [ ] db/connection.py
|
|
454
|
+
- [ ] db/schema.py
|
|
455
|
+
- [ ] db/utils.py
|
|
456
|
+
|
|
457
|
+
2. **CLI 入口**
|
|
458
|
+
- [ ] cli.py(统一输出、错误处理)
|
|
459
|
+
- [ ] const.py
|
|
460
|
+
|
|
461
|
+
3. **核心命令**
|
|
462
|
+
- [ ] init.py
|
|
463
|
+
- [ ] add.py
|
|
464
|
+
- [ ] ls.py
|
|
465
|
+
- [ ] get.py
|
|
466
|
+
- [ ] delete.py
|
|
467
|
+
- [ ] search.py
|
|
468
|
+
|
|
469
|
+
4. **测试**
|
|
470
|
+
- [ ] 单元测试
|
|
471
|
+
- [ ] 集成测试
|
|
472
|
+
|
|
473
|
+
---
|
|
474
|
+
|
|
475
|
+
## 8. 版本规划
|
|
476
|
+
|
|
477
|
+
| 版本 | 核心资产 | 客户端 | 验证目标 | 工期 |
|
|
478
|
+
|------|----------|--------|----------|------|
|
|
479
|
+
| v0.1 | Core Library v0.1 | CLI | 数据模型和 API 稳定性 | 2天 |
|
|
480
|
+
| v0.2 | Core Library v0.1 | **MCP Server** | **Agent 集成价值(关键验证点)** | 1天 |
|
|
481
|
+
| v0.3 | Core Library v0.1 | Python SDK | 开发者体验 | 0.5天 |
|
|
482
|
+
| v0.4 | Core Library v0.1 | Browser Extension | 用户体验 | 1-2天 |
|
|
483
|
+
|
|
484
|
+
**核心原则**:
|
|
485
|
+
1. **Core Library 的版本号才是产品的主版本号**
|
|
486
|
+
2. 所有客户端都是 Core Library 的薄封装
|
|
487
|
+
3. 每一步都是"加一层",不是重构
|
|
488
|
+
|
|
489
|
+
**验证策略**:v0.2 MCP Server 是验证"数据基础设施"定位的关键。如果验证成功,继续推进;如果验证失败,重新定位或暂停。
|
|
490
|
+
|
|
491
|
+
---
|
|
492
|
+
|
|
493
|
+
## 9. v0.1 架构约束
|
|
494
|
+
|
|
495
|
+
为未来版本预留空间,v0.1 开发时必须遵守:
|
|
496
|
+
|
|
497
|
+
### 9.0 Core Library 优先设计原则
|
|
498
|
+
|
|
499
|
+
**Core Library 必须满足**:
|
|
500
|
+
|
|
501
|
+
| 原则 | 要求 | 验证方式 |
|
|
502
|
+
|------|------|----------|
|
|
503
|
+
| **独立性** | 不依赖任何客户端(CLI/MCP/HTTP) | 可单独 import 使用 |
|
|
504
|
+
| **完整性** | 包含所有数据操作逻辑 | 客户端无需直接访问数据库 |
|
|
505
|
+
| **稳定性** | API 设计考虑向后兼容 | 命名和参数慎重设计 |
|
|
506
|
+
| **可测试性** | 所有逻辑可独立单元测试 | 不依赖 CLI 运行 |
|
|
507
|
+
|
|
508
|
+
**实现约束**:
|
|
509
|
+
- Core Library 代码位于 `src/read/core/`
|
|
510
|
+
- 不得在 Core Library 中 import CLI 相关模块
|
|
511
|
+
- 所有数据库操作必须通过 Core Library 暴露的方法
|
|
512
|
+
|
|
513
|
+
### 9.1 Core Library 与 CLI 解耦
|
|
514
|
+
|
|
515
|
+
- CLI 不直接操作 SQLite
|
|
516
|
+
- CLI 调用 Core Library 的方法
|
|
517
|
+
- 未来 MCP/SDK 可以复用同一个 Core Library
|
|
518
|
+
- **设计目标**:v0.2 加入 MCP Server 时,只需"加一层",不需重构
|
|
519
|
+
|
|
520
|
+
### 9.2 错误码统一
|
|
521
|
+
|
|
522
|
+
- 设计一套错误码体系
|
|
523
|
+
- CLI 和未来 HTTP API/MCP 共用
|
|
524
|
+
- 方便客户端统一处理
|
|
525
|
+
- **错误信息人类化**:对开发者友好的错误提示
|
|
526
|
+
|
|
527
|
+
### 9.3 数据模型稳定
|
|
528
|
+
|
|
529
|
+
- 尽量减少 schema 变更
|
|
530
|
+
- 为扩展字段预留空间
|
|
531
|
+
- 向后兼容
|
|
532
|
+
- **新增字段考虑**:
|
|
533
|
+
- `metadata JSON` — Agent 补充信息
|
|
534
|
+
- `updated_at TEXT` — 同步预留
|
|
535
|
+
- `type TEXT` — 支持多种数据类型
|
|
536
|
+
|
|
537
|
+
---
|
|
538
|
+
|
|
539
|
+
## 10. 设计决策记录
|
|
540
|
+
|
|
541
|
+
### 10.1 为什么用 SQLite 单文件?
|
|
542
|
+
|
|
543
|
+
- 零配置
|
|
544
|
+
- 适合个人数据量
|
|
545
|
+
- 易于备份(复制一个文件即可)
|
|
546
|
+
- Python 内置支持
|
|
547
|
+
|
|
548
|
+
### 10.2 为什么 CLI 是核心接口而非唯一入口?
|
|
549
|
+
|
|
550
|
+
- CLI 是数据层的命令行接口
|
|
551
|
+
- 人类用户通过浏览器插件等客户端访问
|
|
552
|
+
- Agent 通过 MCP/SDK 访问
|
|
553
|
+
- CLI 主要用于开发和调试
|
|
554
|
+
|
|
555
|
+
### 10.3 为什么不做 tags 表?
|
|
556
|
+
|
|
557
|
+
- 先验证核心需求
|
|
558
|
+
- 够用就行,不过度设计
|
|
559
|
+
- 以后可以通过 source 字段简单实现
|
|
560
|
+
- 客户端层可以实现标签功能
|
|
561
|
+
|
|
562
|
+
### 10.4 为什么用 Typer?
|
|
563
|
+
|
|
564
|
+
- 类型提示友好
|
|
565
|
+
- 自动生成帮助信息
|
|
566
|
+
- Click 的现代替代品
|
|
567
|
+
|
|
568
|
+
---
|
|
569
|
+
|
|
570
|
+
## 11. 版本记录
|
|
571
|
+
|
|
572
|
+
| 版本 | 日期 | 变更 |
|
|
573
|
+
|------|------|------|
|
|
574
|
+
| v1.0 | 2026-03-15 | 初始版本 |
|