fr-cli 2.2.8__tar.gz → 2.3.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.
Files changed (94) hide show
  1. {fr_cli-2.2.8/fr_cli.egg-info → fr_cli-2.3.0}/PKG-INFO +70 -2
  2. {fr_cli-2.2.8 → fr_cli-2.3.0}/README.md +68 -1
  3. fr_cli-2.3.0/fr_cli/agent/acp.py +195 -0
  4. fr_cli-2.3.0/fr_cli/agent/context_files.py +159 -0
  5. fr_cli-2.3.0/fr_cli/agent/gateway.py +233 -0
  6. fr_cli-2.3.0/fr_cli/agent/hermes.py +331 -0
  7. {fr_cli-2.2.8 → fr_cli-2.3.0}/fr_cli/agent/master.py +9 -0
  8. fr_cli-2.3.0/fr_cli/agent/personality.py +236 -0
  9. fr_cli-2.3.0/fr_cli/agent/plugin_system.py +195 -0
  10. fr_cli-2.3.0/fr_cli/agent/shell_mode.py +181 -0
  11. fr_cli-2.3.0/fr_cli/agent/skills.py +226 -0
  12. fr_cli-2.3.0/fr_cli/core/llm.py +266 -0
  13. fr_cli-2.3.0/fr_cli/core/model_factory.py +173 -0
  14. fr_cli-2.3.0/fr_cli/weapon/mcp.py +186 -0
  15. {fr_cli-2.2.8 → fr_cli-2.3.0/fr_cli.egg-info}/PKG-INFO +70 -2
  16. {fr_cli-2.2.8 → fr_cli-2.3.0}/fr_cli.egg-info/SOURCES.txt +9 -1
  17. {fr_cli-2.2.8 → fr_cli-2.3.0}/fr_cli.egg-info/requires.txt +1 -0
  18. {fr_cli-2.2.8 → fr_cli-2.3.0}/pyproject.toml +2 -1
  19. fr_cli-2.2.8/fr_cli/core/llm.py +0 -401
  20. fr_cli-2.2.8/fr_cli/weapon/mcp.py +0 -202
  21. fr_cli-2.2.8/tests/test_coding_helper.py +0 -205
  22. {fr_cli-2.2.8 → fr_cli-2.3.0}/LICENSE +0 -0
  23. {fr_cli-2.2.8 → fr_cli-2.3.0}/MANIFEST.in +0 -0
  24. {fr_cli-2.2.8 → fr_cli-2.3.0}/fr_cli/README.md +0 -0
  25. {fr_cli-2.2.8 → fr_cli-2.3.0}/fr_cli/WEAPON.MD +0 -0
  26. {fr_cli-2.2.8 → fr_cli-2.3.0}/fr_cli/__init__.py +0 -0
  27. {fr_cli-2.2.8 → fr_cli-2.3.0}/fr_cli/addon/plugin.py +0 -0
  28. {fr_cli-2.2.8 → fr_cli-2.3.0}/fr_cli/agent/__init__.py +0 -0
  29. {fr_cli-2.2.8 → fr_cli-2.3.0}/fr_cli/agent/a2a.py +0 -0
  30. {fr_cli-2.2.8 → fr_cli-2.3.0}/fr_cli/agent/builtins/__init__.py +0 -0
  31. {fr_cli-2.2.8 → fr_cli-2.3.0}/fr_cli/agent/builtins/_utils.py +0 -0
  32. {fr_cli-2.2.8 → fr_cli-2.3.0}/fr_cli/agent/builtins/db.py +0 -0
  33. {fr_cli-2.2.8 → fr_cli-2.3.0}/fr_cli/agent/builtins/local.py +0 -0
  34. {fr_cli-2.2.8 → fr_cli-2.3.0}/fr_cli/agent/builtins/powerful_agent_template.py +0 -0
  35. {fr_cli-2.2.8 → fr_cli-2.3.0}/fr_cli/agent/builtins/rag.py +0 -0
  36. {fr_cli-2.2.8 → fr_cli-2.3.0}/fr_cli/agent/builtins/rag_watcher_daemon.py +0 -0
  37. {fr_cli-2.2.8 → fr_cli-2.3.0}/fr_cli/agent/builtins/remote.py +0 -0
  38. {fr_cli-2.2.8 → fr_cli-2.3.0}/fr_cli/agent/builtins/spider.py +0 -0
  39. {fr_cli-2.2.8 → fr_cli-2.3.0}/fr_cli/agent/client.py +0 -0
  40. {fr_cli-2.2.8 → fr_cli-2.3.0}/fr_cli/agent/coding_helper.py +0 -0
  41. {fr_cli-2.2.8 → fr_cli-2.3.0}/fr_cli/agent/executor.py +0 -0
  42. {fr_cli-2.2.8 → fr_cli-2.3.0}/fr_cli/agent/generator.py +0 -0
  43. {fr_cli-2.2.8 → fr_cli-2.3.0}/fr_cli/agent/image_and_parallel.py +0 -0
  44. {fr_cli-2.2.8 → fr_cli-2.3.0}/fr_cli/agent/manager.py +0 -0
  45. {fr_cli-2.2.8 → fr_cli-2.3.0}/fr_cli/agent/master_prompt.py +0 -0
  46. {fr_cli-2.2.8 → fr_cli-2.3.0}/fr_cli/agent/remote.py +0 -0
  47. {fr_cli-2.2.8 → fr_cli-2.3.0}/fr_cli/agent/server.py +0 -0
  48. {fr_cli-2.2.8 → fr_cli-2.3.0}/fr_cli/agent/workflow.py +0 -0
  49. {fr_cli-2.2.8 → fr_cli-2.3.0}/fr_cli/agent/workflow_system.py +0 -0
  50. {fr_cli-2.2.8 → fr_cli-2.3.0}/fr_cli/breakthrough/update.py +0 -0
  51. {fr_cli-2.2.8 → fr_cli-2.3.0}/fr_cli/command/__init__.py +0 -0
  52. {fr_cli-2.2.8 → fr_cli-2.3.0}/fr_cli/command/executor.py +0 -0
  53. {fr_cli-2.2.8 → fr_cli-2.3.0}/fr_cli/command/registry.py +0 -0
  54. {fr_cli-2.2.8 → fr_cli-2.3.0}/fr_cli/command/security.py +0 -0
  55. {fr_cli-2.2.8 → fr_cli-2.3.0}/fr_cli/conf/config.py +0 -0
  56. {fr_cli-2.2.8 → fr_cli-2.3.0}/fr_cli/conf/wizard.py +0 -0
  57. {fr_cli-2.2.8 → fr_cli-2.3.0}/fr_cli/core/chat.py +0 -0
  58. {fr_cli-2.2.8 → fr_cli-2.3.0}/fr_cli/core/core.py +0 -0
  59. {fr_cli-2.2.8 → fr_cli-2.3.0}/fr_cli/core/intent.py +0 -0
  60. {fr_cli-2.2.8 → fr_cli-2.3.0}/fr_cli/core/recommender.py +0 -0
  61. {fr_cli-2.2.8 → fr_cli-2.3.0}/fr_cli/core/stream.py +0 -0
  62. {fr_cli-2.2.8 → fr_cli-2.3.0}/fr_cli/core/sysmon.py +0 -0
  63. {fr_cli-2.2.8 → fr_cli-2.3.0}/fr_cli/core/thinking.py +0 -0
  64. {fr_cli-2.2.8 → fr_cli-2.3.0}/fr_cli/gatekeeper/__init__.py +0 -0
  65. {fr_cli-2.2.8 → fr_cli-2.3.0}/fr_cli/gatekeeper/daemon.py +0 -0
  66. {fr_cli-2.2.8 → fr_cli-2.3.0}/fr_cli/gatekeeper/manager.py +0 -0
  67. {fr_cli-2.2.8 → fr_cli-2.3.0}/fr_cli/lang/i18n.py +0 -0
  68. {fr_cli-2.2.8 → fr_cli-2.3.0}/fr_cli/main.py +0 -0
  69. {fr_cli-2.2.8 → fr_cli-2.3.0}/fr_cli/memory/context.py +0 -0
  70. {fr_cli-2.2.8 → fr_cli-2.3.0}/fr_cli/memory/history.py +0 -0
  71. {fr_cli-2.2.8 → fr_cli-2.3.0}/fr_cli/memory/session.py +0 -0
  72. {fr_cli-2.2.8 → fr_cli-2.3.0}/fr_cli/repl/__init__.py +0 -0
  73. {fr_cli-2.2.8 → fr_cli-2.3.0}/fr_cli/repl/commands.py +0 -0
  74. {fr_cli-2.2.8 → fr_cli-2.3.0}/fr_cli/security/security.py +0 -0
  75. {fr_cli-2.2.8 → fr_cli-2.3.0}/fr_cli/ui/ui.py +0 -0
  76. {fr_cli-2.2.8 → fr_cli-2.3.0}/fr_cli/weapon/cron.py +0 -0
  77. {fr_cli-2.2.8 → fr_cli-2.3.0}/fr_cli/weapon/dataframe.py +0 -0
  78. {fr_cli-2.2.8 → fr_cli-2.3.0}/fr_cli/weapon/disk.py +0 -0
  79. {fr_cli-2.2.8 → fr_cli-2.3.0}/fr_cli/weapon/fs.py +0 -0
  80. {fr_cli-2.2.8 → fr_cli-2.3.0}/fr_cli/weapon/launcher.py +0 -0
  81. {fr_cli-2.2.8 → fr_cli-2.3.0}/fr_cli/weapon/loader.py +0 -0
  82. {fr_cli-2.2.8 → fr_cli-2.3.0}/fr_cli/weapon/mail.py +0 -0
  83. {fr_cli-2.2.8 → fr_cli-2.3.0}/fr_cli/weapon/vision.py +0 -0
  84. {fr_cli-2.2.8 → fr_cli-2.3.0}/fr_cli/weapon/web.py +0 -0
  85. {fr_cli-2.2.8 → fr_cli-2.3.0}/fr_cli.egg-info/dependency_links.txt +0 -0
  86. {fr_cli-2.2.8 → fr_cli-2.3.0}/fr_cli.egg-info/entry_points.txt +0 -0
  87. {fr_cli-2.2.8 → fr_cli-2.3.0}/fr_cli.egg-info/top_level.txt +0 -0
  88. {fr_cli-2.2.8 → fr_cli-2.3.0}/setup.cfg +0 -0
  89. {fr_cli-2.2.8 → fr_cli-2.3.0}/tests/test_a2a_and_providers.py +0 -0
  90. {fr_cli-2.2.8 → fr_cli-2.3.0}/tests/test_integration_real.py +0 -0
  91. {fr_cli-2.2.8 → fr_cli-2.3.0}/tests/test_master_prompt_fix.py +0 -0
  92. {fr_cli-2.2.8 → fr_cli-2.3.0}/tests/test_model_config.py +0 -0
  93. {fr_cli-2.2.8 → fr_cli-2.3.0}/tests/test_new_features.py +0 -0
  94. {fr_cli-2.2.8 → fr_cli-2.3.0}/tests/test_new_providers.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fr-cli
3
- Version: 2.2.8
3
+ Version: 2.3.0
4
4
  Summary: 凡人打字机 - 支持多模型(Zhipu/DeepSeek/Kimi/Qwen/StepFun/MiniMax/Spark/Doubao/MiMo)的终极全能终端工具
5
5
  Author: FANREN CLI Author
6
6
  License-Expression: MIT
@@ -27,6 +27,7 @@ Requires-Dist: zhipuai>=2.0.0
27
27
  Requires-Dist: openai>=1.0.0
28
28
  Requires-Dist: requests>=2.28.0
29
29
  Requires-Dist: mcp>=1.6.0
30
+ Requires-Dist: pyyaml>=6.0.0
30
31
  Provides-Extra: data
31
32
  Requires-Dist: pandas>=1.5.0; extra == "data"
32
33
  Requires-Dist: openpyxl>=3.0.0; extra == "data"
@@ -73,6 +74,8 @@ Dynamic: license-file
73
74
 
74
75
  **支持 27+ 种 AI 模型(智谱/智谱Coding/DeepSeek/Kimi/Kimi K2/Kimi Code/StepFun/Step-3/MiniMax/M2.7/讯飞星火/豆包/MiMo/LongCat)的终极全能终端工具。**
75
76
 
77
+ **集成 Hermes Agent 核心功能:自我进化的 AI 助手。**
78
+
76
79
  ## ✨ 功能特性
77
80
 
78
81
  ### 🤖 多模型支持
@@ -234,7 +237,72 @@ python3 main.py
234
237
  /security 查看安全设置
235
238
  ```
236
239
 
237
- ## 📦 支持的模型提供商(25+)
240
+ ## Hermes 核心功能
241
+
242
+ ### 📋 任务管理
243
+ ```
244
+ from fr_cli.agent.hermes import get_task_manager, get_analytics
245
+
246
+ # 创建任务
247
+ tm = get_task_manager()
248
+ task = tm.create_task("完成代码审查")
249
+
250
+ # 记录分析
251
+ an = get_analytics()
252
+ an.record_request("glm-4-flash", 1000, 0.01)
253
+ ```
254
+
255
+ ### 🎯 目标追踪
256
+ ```
257
+ from fr_cli.agent.hermes import GoalTracker
258
+
259
+ gt = GoalTracker()
260
+ gt.set_goal("完成项目", ["阶段1", "阶段2", "阶段3"])
261
+ gt.update_progress("已完成阶段1", 0.33)
262
+ ```
263
+
264
+ ### ⏰ 定时任务
265
+ ```
266
+ from fr_cli.agent.hermes import get_cron_scheduler
267
+
268
+ cron = get_cron_scheduler()
269
+ cron.add_job("daily-report", "0 9 * * *", "生成日报")
270
+ ```
271
+
272
+ ### 🔌 插件系统
273
+ ```
274
+ from fr_cli.agent.plugin_system import get_plugin_registry
275
+
276
+ registry = get_plugin_registry()
277
+ plugins = registry.list_all()
278
+ ```
279
+
280
+ ### 🐚 Shell 模式 (Ctrl-X 切换)
281
+ ```
282
+ Agent 模式: 输入消息与 AI 对话
283
+ Shell 模式: 直接执行 shell 命令
284
+
285
+ 按 Ctrl-X 切换模式
286
+ ```
287
+
288
+ ### 🔗 ACP (Agent Client Protocol)
289
+ ```bash
290
+ # 启动 ACP 服务
291
+ fr acp
292
+
293
+ # 配置到 Zed
294
+ # ~/.config/zed/settings.json
295
+ {
296
+ "agent_servers": {
297
+ "fr-cli": {
298
+ "command": "fr",
299
+ "args": ["acp"]
300
+ }
301
+ }
302
+ }
303
+ ```
304
+
305
+ ## �📦 支持的模型提供商(25+)
238
306
 
239
307
  | 道统 | 默认模型 | API 地址 |
240
308
  |------|---------|----------|
@@ -2,6 +2,8 @@
2
2
 
3
3
  **支持 27+ 种 AI 模型(智谱/智谱Coding/DeepSeek/Kimi/Kimi K2/Kimi Code/StepFun/Step-3/MiniMax/M2.7/讯飞星火/豆包/MiMo/LongCat)的终极全能终端工具。**
4
4
 
5
+ **集成 Hermes Agent 核心功能:自我进化的 AI 助手。**
6
+
5
7
  ## ✨ 功能特性
6
8
 
7
9
  ### 🤖 多模型支持
@@ -163,7 +165,72 @@ python3 main.py
163
165
  /security 查看安全设置
164
166
  ```
165
167
 
166
- ## 📦 支持的模型提供商(25+)
168
+ ## Hermes 核心功能
169
+
170
+ ### 📋 任务管理
171
+ ```
172
+ from fr_cli.agent.hermes import get_task_manager, get_analytics
173
+
174
+ # 创建任务
175
+ tm = get_task_manager()
176
+ task = tm.create_task("完成代码审查")
177
+
178
+ # 记录分析
179
+ an = get_analytics()
180
+ an.record_request("glm-4-flash", 1000, 0.01)
181
+ ```
182
+
183
+ ### 🎯 目标追踪
184
+ ```
185
+ from fr_cli.agent.hermes import GoalTracker
186
+
187
+ gt = GoalTracker()
188
+ gt.set_goal("完成项目", ["阶段1", "阶段2", "阶段3"])
189
+ gt.update_progress("已完成阶段1", 0.33)
190
+ ```
191
+
192
+ ### ⏰ 定时任务
193
+ ```
194
+ from fr_cli.agent.hermes import get_cron_scheduler
195
+
196
+ cron = get_cron_scheduler()
197
+ cron.add_job("daily-report", "0 9 * * *", "生成日报")
198
+ ```
199
+
200
+ ### 🔌 插件系统
201
+ ```
202
+ from fr_cli.agent.plugin_system import get_plugin_registry
203
+
204
+ registry = get_plugin_registry()
205
+ plugins = registry.list_all()
206
+ ```
207
+
208
+ ### 🐚 Shell 模式 (Ctrl-X 切换)
209
+ ```
210
+ Agent 模式: 输入消息与 AI 对话
211
+ Shell 模式: 直接执行 shell 命令
212
+
213
+ 按 Ctrl-X 切换模式
214
+ ```
215
+
216
+ ### 🔗 ACP (Agent Client Protocol)
217
+ ```bash
218
+ # 启动 ACP 服务
219
+ fr acp
220
+
221
+ # 配置到 Zed
222
+ # ~/.config/zed/settings.json
223
+ {
224
+ "agent_servers": {
225
+ "fr-cli": {
226
+ "command": "fr",
227
+ "args": ["acp"]
228
+ }
229
+ }
230
+ }
231
+ ```
232
+
233
+ ## �📦 支持的模型提供商(25+)
167
234
 
168
235
  | 道统 | 默认模型 | API 地址 |
169
236
  |------|---------|----------|
@@ -0,0 +1,195 @@
1
+ """
2
+ ACP (Agent Client Protocol) 支持
3
+ 参考 kimi-cli 实现的 ACP 集成
4
+ """
5
+
6
+ import os
7
+ import json
8
+ import asyncio
9
+ import subprocess
10
+ from typing import Dict, Optional, Any
11
+ from dataclasses import dataclass
12
+ from enum import Enum
13
+
14
+
15
+ class ACPMessageType(Enum):
16
+ """ACP 消息类型"""
17
+ REQUEST = "request"
18
+ RESPONSE = "response"
19
+ ERROR = "error"
20
+ STREAM = "stream"
21
+
22
+
23
+ @dataclass
24
+ class ACPMessage:
25
+ """ACP 消息"""
26
+ id: str
27
+ type: ACPMessageType
28
+ method: str
29
+ params: Dict = None
30
+ result: Any = None
31
+ error: str = None
32
+
33
+
34
+ class ACPServer:
35
+ """ACP 服务器 - 作为 Agent 服务端"""
36
+
37
+ def __init__(self, name: str = "fr-cli"):
38
+ self.name = name
39
+ self.running = False
40
+ self.port = 8765
41
+
42
+ async def start(self, port: int = 8765):
43
+ """启动 ACP 服务器"""
44
+ self.port = port
45
+ self.running = True
46
+
47
+ try:
48
+ import uvicorn
49
+ from fastapi import FastAPI
50
+ from fastapi.websockets import WebSocket
51
+
52
+ app = FastAPI()
53
+
54
+ @app.websocket("/acp")
55
+ async def acp_endpoint(websocket: WebSocket):
56
+ await websocket.accept()
57
+ try:
58
+ while self.running:
59
+ data = await websocket.receive_text()
60
+ message = json.loads(data)
61
+
62
+ # 处理 ACP 消息
63
+ response = await self.handle_message(message)
64
+
65
+ if response:
66
+ await websocket.send_text(json.dumps(response))
67
+ except Exception as e:
68
+ print(f"ACP 连接错误: {e}")
69
+
70
+ config = uvicorn.Config(app, host="127.0.0.1", port=port, log_level="error")
71
+ server = uvicorn.Server(config)
72
+ await server.serve()
73
+
74
+ except ImportError:
75
+ print("警告: 需要安装 uvicorn 和 fastapi 来启用 ACP 服务")
76
+ except Exception as e:
77
+ print(f"ACP 服务器错误: {e}")
78
+
79
+ async def handle_message(self, message: Dict) -> Optional[Dict]:
80
+ """处理 ACP 消息"""
81
+ msg_type = message.get("type")
82
+
83
+ if msg_type == "request":
84
+ method = message.get("method")
85
+ params = message.get("params", {})
86
+
87
+ if method == "chat":
88
+ result = await self.chat(params)
89
+ return {"id": message.get("id"), "type": "response", "result": result}
90
+ elif method == "tools":
91
+ return {"id": message.get("id"), "type": "response", "result": self.get_tools()}
92
+
93
+ return None
94
+
95
+ async def chat(self, params: Dict) -> str:
96
+ """处理聊天请求"""
97
+ from fr_cli.core.core import ask
98
+ message = params.get("message", "")
99
+ try:
100
+ result, _ = await asyncio.to_thread(ask, message)
101
+ return result
102
+ except:
103
+ return "Error processing request"
104
+
105
+ def get_tools(self) -> list:
106
+ """获取可用工具列表"""
107
+ return [
108
+ {"name": "bash", "description": "Execute shell commands"},
109
+ {"name": "read", "description": "Read file contents"},
110
+ {"name": "write", "description": "Write file contents"},
111
+ {"name": "search", "description": "Search files"}
112
+ ]
113
+
114
+ def stop(self):
115
+ """停止 ACP 服务器"""
116
+ self.running = False
117
+
118
+
119
+ class ACPClient:
120
+ """ACP 客户端 - 连接其他 Agent"""
121
+
122
+ def __init__(self, server_url: str):
123
+ self.server_url = server_url
124
+ self.ws = None
125
+
126
+ async def connect(self):
127
+ """连接到 ACP 服务器"""
128
+ try:
129
+ import websockets
130
+ self.ws = await websockets.connect(f"{self.server_url}/acp")
131
+ return True
132
+ except Exception as e:
133
+ print(f"ACP 连接失败: {e}")
134
+ return False
135
+
136
+ async def send_message(self, method: str, params: Dict = None) -> Optional[Dict]:
137
+ """发送消息"""
138
+ if not self.ws:
139
+ return None
140
+
141
+ message = {
142
+ "id": str(asyncio.get_event_loop().time()),
143
+ "type": "request",
144
+ "method": method,
145
+ "params": params or {}
146
+ }
147
+
148
+ await self.ws.send(json.dumps(message))
149
+ response = await self.ws.recv()
150
+ return json.loads(response)
151
+
152
+ async def close(self):
153
+ """关闭连接"""
154
+ if self.ws:
155
+ await self.ws.close()
156
+
157
+
158
+ def run_acp_mode():
159
+ """运行 ACP 模式"""
160
+ print("""
161
+ ╔════════════════════════════════════════════════════╗
162
+ ║ ACP (Agent Client Protocol) 模式 ║
163
+ ╚════════════════════════════════════════════════════╝
164
+
165
+ fr-cli 将作为 ACP Agent 服务端运行,
166
+ 可与 Zed, VS Code 等 ACP 兼容编辑器集成。
167
+
168
+ 启动命令示例配置:
169
+
170
+ Zed: ~/.config/zed/settings.json
171
+ {
172
+ "agent_servers": {
173
+ "fr-cli": {
174
+ "command": "fr",
175
+ "args": ["acp"],
176
+ "env": {}
177
+ }
178
+ }
179
+ }
180
+ """)
181
+ server = ACPServer(name="fr-cli")
182
+ asyncio.run(server.start())
183
+
184
+
185
+ # CLI 命令
186
+ def main():
187
+ import sys
188
+ if len(sys.argv) > 1 and sys.argv[1] == "acp":
189
+ run_acp_mode()
190
+ else:
191
+ print("使用: fr acp 启动 ACP 服务")
192
+
193
+
194
+ if __name__ == "__main__":
195
+ main()
@@ -0,0 +1,159 @@
1
+ """
2
+ Context Files 系统 - 参考 Hermes Agent 实现
3
+ 项目上下文文件,塑造每次对话
4
+ """
5
+
6
+ import os
7
+ import glob
8
+ from typing import List, Dict, Optional
9
+ from dataclasses import dataclass
10
+
11
+
12
+ @dataclass
13
+ class ContextFile:
14
+ """上下文文件"""
15
+ path: str
16
+ content: str = ""
17
+ size: int = 0
18
+ loaded: bool = False
19
+
20
+ def load_content(self):
21
+ """加载文件内容"""
22
+ if not self.loaded and os.path.exists(self.path):
23
+ try:
24
+ with open(self.path, 'r', encoding='utf-8') as f:
25
+ self.content = f.read(100000) # 限制 100KB
26
+ self.size = len(self.content)
27
+ self.loaded = True
28
+ except Exception:
29
+ pass
30
+
31
+
32
+ class ContextFilesManager:
33
+ """上下文文件管理器"""
34
+
35
+ def __init__(self, config_dir: str = None):
36
+ if config_dir is None:
37
+ config_dir = os.path.expanduser("~/.fr_cli")
38
+ self.config_dir = config_dir
39
+ self.context_file = os.path.join(config_dir, "context_files.json")
40
+ self.patterns: List[str] = []
41
+ self.exclude_patterns: List[str] = [".git/*", "node_modules/*", "__pycache__/*"]
42
+ self._load_config()
43
+
44
+ def _load_config(self):
45
+ """加载配置"""
46
+ if os.path.exists(self.context_file):
47
+ try:
48
+ import json
49
+ with open(self.context_file, 'r', encoding='utf-8') as f:
50
+ data = json.load(f)
51
+ self.patterns = data.get("patterns", [])
52
+ self.exclude_patterns = data.get("exclude", self.exclude_patterns)
53
+ except Exception:
54
+ pass
55
+
56
+ def _save_config(self):
57
+ """保存配置"""
58
+ import json
59
+ os.makedirs(os.path.dirname(self.context_file), exist_ok=True)
60
+ with open(self.context_file, 'w', encoding='utf-8') as f:
61
+ json.dump({
62
+ "patterns": self.patterns,
63
+ "exclude": self.exclude_patterns
64
+ }, f, indent=2)
65
+
66
+ def add_pattern(self, pattern: str):
67
+ """添加匹配模式"""
68
+ if pattern not in self.patterns:
69
+ self.patterns.append(pattern)
70
+ self._save_config()
71
+
72
+ def remove_pattern(self, pattern: str):
73
+ """移除匹配模式"""
74
+ if pattern in self.patterns:
75
+ self.patterns.remove(pattern)
76
+ self._save_config()
77
+
78
+ def add_exclude(self, pattern: str):
79
+ """添加排除模式"""
80
+ if pattern not in self.exclude_patterns:
81
+ self.exclude_patterns.append(pattern)
82
+ self._save_config()
83
+
84
+ def get_matching_files(self, root_dir: str = ".") -> List[str]:
85
+ """获取匹配的文件列表"""
86
+ files = set()
87
+
88
+ for pattern in self.patterns:
89
+ full_pattern = os.path.join(root_dir, pattern)
90
+ matched = glob.glob(full_pattern, recursive=True)
91
+ files.update(matched)
92
+
93
+ # 应用排除
94
+ excluded = set()
95
+ for exclude in self.exclude_patterns:
96
+ full_exclude = os.path.join(root_dir, exclude)
97
+ matched = glob.glob(full_exclude, recursive=True)
98
+ excluded.update(matched)
99
+
100
+ return [f for f in files if f not in excluded]
101
+
102
+ def load_context_files(self, root_dir: str = ".") -> List[ContextFile]:
103
+ """加载所有上下文文件"""
104
+ files = self.get_matching_files(root_dir)
105
+ context_files = []
106
+
107
+ for filepath in files:
108
+ cf = ContextFile(path=filepath)
109
+ cf.load_content()
110
+ if cf.loaded:
111
+ context_files.append(cf)
112
+
113
+ return context_files
114
+
115
+ def build_context_prompt(self, root_dir: str = ".") -> str:
116
+ """构建上下文提示"""
117
+ context_files = self.load_context_files(root_dir)
118
+ if not context_files:
119
+ return ""
120
+
121
+ parts = ["\n\n# 项目上下文\n"]
122
+
123
+ for cf in context_files:
124
+ parts.append(f"\n## {os.path.relpath(cf.path, root_dir)}")
125
+ parts.append(f"```\n{cf.content}\n```")
126
+
127
+ return "\n".join(parts)
128
+
129
+ def list_patterns(self) -> Dict:
130
+ """列出所有模式"""
131
+ return {
132
+ "include": self.patterns,
133
+ "exclude": self.exclude_patterns
134
+ }
135
+
136
+ def set_patterns(self, patterns: List[str]):
137
+ """设置包含模式"""
138
+ self.patterns = patterns
139
+ self._save_config()
140
+
141
+ def set_exclude_patterns(self, patterns: List[str]):
142
+ """设置排除模式"""
143
+ self.exclude_patterns = patterns
144
+ self._save_config()
145
+
146
+
147
+ # 全局实例
148
+ _context_manager = None
149
+
150
+ def get_context_manager() -> ContextFilesManager:
151
+ global _context_manager
152
+ if _context_manager is None:
153
+ _context_manager = ContextFilesManager()
154
+ return _context_manager
155
+
156
+
157
+ def build_project_context(root_dir: str = ".") -> str:
158
+ """构建项目上下文"""
159
+ return get_context_manager().build_context_prompt(root_dir)