Skip to content

Commit 90eaa47

Browse files
Merge pull request #47 from modelscope/feat/cli
[Docs] Update readme
2 parents a42af88 + bb2b7db commit 90eaa47

14 files changed

Lines changed: 213 additions & 48 deletions

File tree

README.md

Lines changed: 56 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
[![ripgrep-all](https://img.shields.io/badge/ripgrep--all-Search-E67E22?style=flat-square&logo=rust&logoColor=white)](https://github.com/phiresky/ripgrep-all)
1414
[![OpenAI](https://img.shields.io/badge/OpenAI-API-412991?style=flat-square&logo=openai&logoColor=white)](https://github.com/openai/openai-python)
1515
[![Kreuzberg](https://img.shields.io/badge/Kreuzberg-Text_Extraction-4CAF50?style=flat-square)](https://github.com/kreuzberg-dev/kreuzberg)
16+
[![MCP](https://img.shields.io/badge/MCP-Python_SDK-8B5CF6?style=flat-square&logo=python&logoColor=white)](https://github.com/modelcontextprotocol/python-sdk)
1617

1718

1819
[**Quick Start**](#-quick-start) · [**Key Features**](#-key-features) · [**Web UI**](#-web-ui) · [**How it Works**](#-how-it-works) · [**FAQ**](#-faq)
@@ -138,6 +139,12 @@ It serves as a unified intelligent hub for AI agents, delivering deep insights a
138139

139140
## 🎉 News
140141

142+
* 🚀 **Feb 5, 2026**: Release **v0.0.2** — MCP Support, CLI Commands & Knowledge Persistence!
143+
- **MCP Integration**: Full [Model Context Protocol](https://modelcontextprotocol.io) support, works seamlessly with Claude Desktop and Cursor IDE.
144+
- **CLI Commands**: New `sirchmunk` CLI with `init`, `config`, `serve`, and `search` commands.
145+
- **KnowledgeCluster Persistence**: DuckDB-powered storage with Parquet export for efficient knowledge management.
146+
- **Knowledge Reuse**: Semantic similarity-based cluster retrieval for faster searches via embedding vectors.
147+
141148
* 🎉🎉 Jan 22, 2026: Introducing **Sirchmunk**: Initial Release v0.0.1 Now Available!
142149

143150

@@ -183,9 +190,9 @@ llm = OpenAIChat(
183190

184191
async def main():
185192

186-
agent_search = AgenticSearch(llm=llm)
193+
searcher = AgenticSearch(llm=llm)
187194

188-
result: str = await agent_search.search(
195+
result: str = await searcher.search(
189196
query="How does transformer attention work?",
190197
search_paths=["/path/to/documents"],
191198
)
@@ -205,24 +212,34 @@ asyncio.run(main())
205212

206213
Sirchmunk provides a powerful CLI for server management and search operations.
207214

215+
#### Installation
216+
217+
```bash
218+
pip install "sirchmunk[web]"
219+
220+
# or install via UV
221+
uv pip install "sirchmunk[web]"
222+
```
223+
224+
208225
#### Initialize
209226

210227
```bash
211-
# Initialize Sirchmunk with default settings
228+
# Initialize Sirchmunk with default settings (Default work path: `~/.sirchmunk/`)
212229
sirchmunk init
213230

214-
# Initialize with custom work path
231+
# Alternatively, initialize with custom work path
215232
sirchmunk init --work-path /path/to/workspace
216233
```
217234

218235
#### Configure
219236

220237
```bash
221-
# Generate configuration file (~/.sirchmunk/.env)
222-
sirchmunk config --generate
223-
224238
# Show current configuration
225239
sirchmunk config
240+
241+
# Regenerate configuration file if needed (Default config file: ~/.sirchmunk/.env)
242+
sirchmunk config --generate
226243
```
227244

228245
#### Start API Server
@@ -269,6 +286,36 @@ sirchmunk search "query" --api --api-url http://localhost:8584
269286

270287
---
271288

289+
## 🔌 MCP Server
290+
291+
Sirchmunk provides a [Model Context Protocol (MCP)](https://modelcontextprotocol.io) server that exposes its intelligent search capabilities as MCP tools. This enables seamless integration with AI assistants like **Claude Desktop** and **Cursor IDE**.
292+
293+
### Quick Start
294+
295+
```bash
296+
# Install MCP package
297+
pip install sirchmunk-mcp
298+
299+
# Initialize and configure
300+
sirchmunk-mcp init
301+
sirchmunk-mcp config --generate
302+
303+
# Edit ~/.sirchmunk/.mcp_env with your LLM API key
304+
305+
# Test with MCP Inspector
306+
npx @modelcontextprotocol/inspector sirchmunk-mcp serve
307+
```
308+
309+
### Features
310+
311+
- **Multi-Mode Search**: DEEP mode for comprehensive analysis, FILENAME_ONLY for fast file discovery
312+
- **Knowledge Cluster Management**: Automatic extraction, storage, and reuse of knowledge
313+
- **Standard MCP Protocol**: Works with stdio and Streamable HTTP transports
314+
315+
📖 **For detailed documentation, see [Sirchmunk MCP README](src/sirchmunk_mcp/README.md)**.
316+
317+
---
318+
272319
## 🖥️ Web UI
273320

274321
The web UI is built for fast, transparent workflows: chat, knowledge analytics, and system monitoring in one place.
@@ -386,7 +433,7 @@ Any OpenAI-compatible API endpoint, including (but not limited too):
386433
Simply specify the path in your search query:
387434

388435
```python
389-
result = await search.search(
436+
result = await searcher.search(
390437
query="Your question",
391438
search_paths=["/path/to/folder", "/path/to/file.pdf"]
392439
)
@@ -413,7 +460,7 @@ You can query them using DuckDB or the `KnowledgeManager` API.
413460

414461
1. **Web Dashboard**: Visit the Monitor page for real-time statistics
415462
2. **API**: `GET /api/v1/monitor/llm` returns usage metrics
416-
3. **Code**: Access `search.llm_usages` after search completion
463+
3. **Code**: Access `searcher.llm_usages` after search completion
417464

418465
</details>
419466

README_zh.md

Lines changed: 53 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
[![ripgrep-all](https://img.shields.io/badge/ripgrep--all-Search-E67E22?style=flat-square&logo=rust&logoColor=white)](https://github.com/phiresky/ripgrep-all)
1414
[![OpenAI](https://img.shields.io/badge/OpenAI-API-412991?style=flat-square&logo=openai&logoColor=white)](https://github.com/openai/openai-python)
1515
[![Kreuzberg](https://img.shields.io/badge/Kreuzberg-Text_Extraction-4CAF50?style=flat-square)](https://github.com/kreuzberg-dev/kreuzberg)
16+
[![MCP](https://img.shields.io/badge/MCP-Python_SDK-8B5CF6?style=flat-square&logo=python&logoColor=white)](https://github.com/modelcontextprotocol/python-sdk)
1617

1718

1819
[**快速开始**](#-快速开始) · [**核心特性**](#-核心特性) · [**Web UI**](#-web-ui) · [**工作原理**](#-工作原理) · [**FAQ**](#-faq)
@@ -135,6 +136,12 @@
135136

136137
## 🎉 News
137138

139+
* 🚀 **2026.2.5**: 发布 **v0.0.2** — MCP 支持、CLI 命令行 & 知识持久化!
140+
- **MCP 集成**:完整支持 [Model Context Protocol](https://modelcontextprotocol.io),与 Claude Desktop 和 Cursor IDE 无缝协作。
141+
- **CLI 命令行**:全新 `sirchmunk` 命令行工具,支持 `init``config``serve``search` 命令。
142+
- **KnowledgeCluster 持久化**:基于 DuckDB 存储,支持 Parquet 导出,高效管理知识聚类。
143+
- **知识复用**:基于语义相似度的知识聚类检索,通过 embedding 向量加速搜索。
144+
138145
* 🎉🎉 2026.1.22: **Sirchmunk** 初始版本 v0.0.1 现已发布!
139146

140147
---
@@ -202,24 +209,35 @@ asyncio.run(main())
202209

203210
Sirchmunk 提供强大的 CLI,用于服务器管理和搜索操作。
204211

212+
213+
#### 安装
214+
215+
```bash
216+
pip install "sirchmunk[web]"
217+
218+
# 或使用UV安装
219+
uv pip install "sirchmunk[web]"
220+
```
221+
222+
205223
#### 初始化
206224

207225
```bash
208-
# 使用默认设置初始化 Sirchmunk
226+
# 使用默认设置初始化 Sirchmunk,默认工作路径为 `~/.sirchmunk/`
209227
sirchmunk init
210228

211-
# 使用自定义工作路径初始化
229+
# 或者,也可以使用自定义工作路径初始化
212230
sirchmunk init --work-path /path/to/workspace
213231
```
214232

215233
#### 配置
216234

217235
```bash
218-
# 生成配置文件 (~/.sirchmunk/.env)
219-
sirchmunk config --generate
220-
221236
# 显示当前配置
222237
sirchmunk config
238+
239+
# 如需要,可重新生成配置文件 (默认配置文件路径,~/.sirchmunk/.env)
240+
sirchmunk config --generate
223241
```
224242

225243
#### 启动 API 服务器
@@ -266,6 +284,36 @@ sirchmunk search "查询" --api --api-url http://localhost:8584
266284

267285
---
268286

287+
## 🔌 MCP 服务器
288+
289+
Sirchmunk 提供 [Model Context Protocol (MCP)](https://modelcontextprotocol.io) 服务器,将其智能搜索能力作为 MCP 工具暴露。可与 **Claude Desktop****Cursor IDE** 等 AI 助手无缝集成。
290+
291+
### 快速开始
292+
293+
```bash
294+
# 安装 MCP 包
295+
pip install sirchmunk-mcp
296+
297+
# 初始化和配置
298+
sirchmunk-mcp init
299+
sirchmunk-mcp config --generate
300+
301+
# 编辑 ~/.sirchmunk/.mcp_env 配置 LLM API Key
302+
303+
# 使用 MCP Inspector 测试
304+
npx @modelcontextprotocol/inspector sirchmunk-mcp serve
305+
```
306+
307+
### 特性
308+
309+
- **多模式搜索**:DEEP 模式进行全面分析,FILENAME_ONLY 模式快速发现文件
310+
- **知识聚类管理**:自动提取、存储和复用知识
311+
- **标准 MCP 协议**:支持 stdio 和 Streamable HTTP 传输
312+
313+
📖 **详细文档请参阅 [Sirchmunk MCP README](src/sirchmunk_mcp/README.md)**
314+
315+
---
316+
269317
## 🖥️ Web UI
270318

271319
Web UI 专为快速、透明的工作流设计:对话、知识分析、系统监控一体化。

requirements/core.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,4 @@ rapidfuzz
1414
duckdb
1515
kreuzberg>=4.0.0rc1
1616
sentence-transformers
17+
modelscope

src/sirchmunk/api/components/history_storage.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,12 @@ def __init__(self, work_path: Optional[str] = None):
3333
Args:
3434
work_path: Base work path. If None, uses SIRCHMUNK_WORK_PATH env variable
3535
"""
36-
# Get work path from env if not provided
36+
# Get work path from env if not provided, and expand ~ in path
3737
if work_path is None:
3838
work_path = os.getenv("SIRCHMUNK_WORK_PATH", DEFAULT_SIRCHMUNK_WORK_PATH)
3939

40-
# Create history storage path
41-
self.history_path = Path(work_path) / ".cache" / "history"
40+
# Create history storage path (expand ~ and resolve to absolute path)
41+
self.history_path = Path(work_path).expanduser().resolve() / ".cache" / "history"
4242
self.history_path.mkdir(parents=True, exist_ok=True)
4343

4444
# Initialize DuckDB

src/sirchmunk/api/components/monitor_tracker.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -353,7 +353,7 @@ def get_storage_info(self) -> Dict[str, Any]:
353353
Storage information
354354
"""
355355
try:
356-
work_path = Path(os.getenv("SIRCHMUNK_WORK_PATH", DEFAULT_SIRCHMUNK_WORK_PATH))
356+
work_path = Path(os.getenv("SIRCHMUNK_WORK_PATH", DEFAULT_SIRCHMUNK_WORK_PATH)).expanduser().resolve()
357357
cache_path = work_path / ".cache"
358358

359359
storage_info = {

src/sirchmunk/api/components/settings_storage.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,12 @@ def __init__(self, work_path: Optional[str] = None):
3232
Args:
3333
work_path: Base work path. If None, uses SIRCHMUNK_WORK_PATH env variable
3434
"""
35-
# Get work path from env if not provided
35+
# Get work path from env if not provided, and expand ~ in path
3636
if work_path is None:
3737
work_path = os.getenv("SIRCHMUNK_WORK_PATH", DEFAULT_SIRCHMUNK_WORK_PATH)
3838

39-
# Create settings storage path
40-
self.settings_path = Path(work_path) / ".cache" / "settings"
39+
# Create settings storage path (expand ~ and resolve to absolute path)
40+
self.settings_path = Path(work_path).expanduser().resolve() / ".cache" / "settings"
4141
self.settings_path.mkdir(parents=True, exist_ok=True)
4242

4343
# Initialize DuckDB

src/sirchmunk/cli/cli.py

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,22 @@ def cmd_init(args: argparse.Namespace) -> int:
184184
llm_base_url = os.getenv("LLM_BASE_URL", "https://api.openai.com/v1")
185185
print(f" • LLM_BASE_URL: {llm_base_url}")
186186

187+
# Pre-download embedding model
188+
print()
189+
print("Downloading embedding model...")
190+
print(" (This may take a few minutes on first run)")
191+
try:
192+
from sirchmunk.utils.embedding_util import EmbeddingUtil
193+
194+
model_cache_dir = str(work_path / ".cache" / "models")
195+
model_dir = EmbeddingUtil.preload_model(
196+
cache_dir=model_cache_dir,
197+
)
198+
print(f" ✓ Embedding model downloaded: {model_dir}")
199+
except Exception as e:
200+
print(f" ✗ Failed to download embedding model: {e}")
201+
print(" Model will be downloaded on first search.")
202+
187203
print()
188204
print("=" * 60)
189205
print("✅ Initialization complete!")
@@ -393,19 +409,24 @@ async def _search_local(
393409
"""
394410
from sirchmunk.search import AgenticSearch
395411
from sirchmunk.llm.openai_chat import OpenAIChat
396-
from sirchmunk.utils.constants import LLM_BASE_URL, LLM_API_KEY, LLM_MODEL_NAME
412+
413+
# Read LLM config from environment at runtime (after .env is loaded)
414+
# Don't use constants module values as they are loaded at import time
415+
llm_base_url = os.getenv("LLM_BASE_URL", "https://api.openai.com/v1")
416+
llm_api_key = os.getenv("LLM_API_KEY", "")
417+
llm_model_name = os.getenv("LLM_MODEL_NAME", "gpt-5.2")
397418

398419
# Validate API key
399-
if not LLM_API_KEY:
420+
if not llm_api_key:
400421
print("❌ LLM_API_KEY is not set.")
401422
print(" Configure it in ~/.sirchmunk/.env or set the environment variable.")
402423
return 1
403424

404425
# Create LLM client
405426
llm = OpenAIChat(
406-
base_url=LLM_BASE_URL,
407-
api_key=LLM_API_KEY,
408-
model=LLM_MODEL_NAME,
427+
base_url=llm_base_url,
428+
api_key=llm_api_key,
429+
model=llm_model_name,
409430
)
410431

411432
# Create search instance

src/sirchmunk/learnings/knowledge_base.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,9 @@ def __init__(
5151
self.llm = llm
5252
self.metadata_map = metadata_map
5353
self.work_path: Path = (
54-
DEFAULT_SIRCHMUNK_WORK_PATH if work_path is None else Path(work_path).resolve()
54+
Path(DEFAULT_SIRCHMUNK_WORK_PATH).expanduser().resolve()
55+
if work_path is None
56+
else Path(work_path).expanduser().resolve()
5557
)
5658
self.metadata_path: Path = (
5759
self.work_path / StorageStructure.CACHE_DIR / StorageStructure.METADATA_DIR

src/sirchmunk/retrieve/text_retriever.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ class GrepRetriever(BaseRetriever):
2929
def __init__(self, work_path: Union[str, Path] = None, **kwargs):
3030
super().__init__()
3131

32-
self.work_path: Path = Path(work_path or DEFAULT_SIRCHMUNK_WORK_PATH)
32+
self.work_path: Path = Path(work_path or DEFAULT_SIRCHMUNK_WORK_PATH).expanduser().resolve()
3333
self.rga_cache: Path = (
3434
self.work_path / StorageStructure.CACHE_DIR / StorageStructure.GREP_DIR
3535
)

src/sirchmunk/scan/file_scanner.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,9 @@ def __init__(
5858
corpus_path = [corpus_path]
5959
self.corpus_paths: List[Path] = [Path(p).resolve() for p in corpus_path]
6060

61-
# Set work and metadata paths
61+
# Set work and metadata paths (expand ~ and resolve to absolute path)
6262
self.work_path: Path = (
63-
Path.cwd() if work_path is None else Path(work_path).resolve()
63+
Path.cwd() if work_path is None else Path(work_path).expanduser().resolve()
6464
)
6565
self.metadata_path: Path = (
6666
self.work_path / StorageStructure.CACHE_DIR / StorageStructure.METADATA_DIR

0 commit comments

Comments
 (0)