feedback-mcp 1.0.64__py3-none-any.whl

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.

Potentially problematic release.


This version of feedback-mcp might be problematic. Click here for more details.

@@ -0,0 +1,262 @@
1
+ """
2
+ Markdown显示组件
3
+
4
+ 用于渲染和显示Markdown内容的只读文本框。
5
+ """
6
+
7
+ import sys
8
+ import re
9
+ import markdown
10
+ import os
11
+
12
+ from PySide6.QtWidgets import QTextEdit, QDialog, QVBoxLayout, QLabel
13
+ from PySide6.QtCore import Qt, QUrl
14
+ from PySide6.QtGui import QPixmap, QMouseEvent, QTextDocument, QDesktopServices
15
+
16
+
17
+ class ImageViewerDialog(QDialog):
18
+ """简单的图片查看对话框"""
19
+ def __init__(self, image_path: str, parent=None):
20
+ super().__init__(parent)
21
+ self.setWindowTitle("图片查看")
22
+ self.setModal(True)
23
+
24
+ layout = QVBoxLayout(self)
25
+ label = QLabel()
26
+ pixmap = QPixmap(image_path)
27
+ if not pixmap.isNull():
28
+ # 限制最大尺寸为屏幕的80%
29
+ screen = self.screen().geometry()
30
+ max_width = int(screen.width() * 0.8)
31
+ max_height = int(screen.height() * 0.8)
32
+ if pixmap.width() > max_width or pixmap.height() > max_height:
33
+ pixmap = pixmap.scaled(max_width, max_height, Qt.KeepAspectRatio, Qt.SmoothTransformation)
34
+ label.setPixmap(pixmap)
35
+ layout.addWidget(label)
36
+ self.resize(pixmap.width(), pixmap.height())
37
+
38
+
39
+ class MarkdownDisplayWidget(QTextEdit):
40
+ """支持Markdown格式显示的组件"""
41
+
42
+ def __init__(self, parent=None):
43
+ super().__init__(parent)
44
+ self.setReadOnly(True)
45
+ self.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
46
+ self.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
47
+ self.setMouseTracking(True) # 启用鼠标追踪以支持hover效果
48
+
49
+ # 设置样式,去掉边框,更简洁的文档显示区域
50
+ self.setStyleSheet("""
51
+ QTextEdit {
52
+ background-color: #2b2b2b;
53
+ border: none;
54
+ padding: 0px;
55
+ color: white;
56
+ font-family: 'Segoe UI', 'Arial', sans-serif;
57
+ font-size: 13px;
58
+ line-height: 1.4;
59
+ }
60
+ """)
61
+
62
+ def setMarkdownText(self, markdown_text: str):
63
+ """设置Markdown文本并渲染为HTML"""
64
+ try:
65
+ # 预处理:转换图片路径为HTML
66
+ def _convert_image_paths(text: str) -> tuple[str, list[str]]:
67
+ pattern = r'\[图片(\d+):\s*(.+?)\]'
68
+ image_paths = []
69
+ def replace_image(match):
70
+ img_num = match.group(1)
71
+ img_path = match.group(2).strip()
72
+ if os.path.exists(img_path):
73
+ image_paths.append(img_path)
74
+ return f'''<div class="image-container">
75
+ <img src="{img_path}" style="max-width:400px;cursor:pointer;">
76
+ <div class="image-path">[图片{img_num}: {img_path}]</div>
77
+ </div>'''
78
+ return match.group(0)
79
+ return re.sub(pattern, replace_image, text), image_paths
80
+
81
+ markdown_text, image_paths = _convert_image_paths(markdown_text)
82
+
83
+ # 预处理:修正嵌套列表的缩进(避免无序子项被识别为有序项)
84
+ # 一些编辑器/模型会在有序列表下的无序子项前仅缩进1-3个空格,
85
+ # Python-Markdown 需至少4个空格才会解析为嵌套列表。
86
+ def _normalize_markdown_lists(md: str) -> str:
87
+ lines = md.splitlines()
88
+ in_ol_block = False
89
+ in_code_block = False
90
+ fence_pat = re.compile(r"^\s*(```|~~~)")
91
+ ol_pat = re.compile(r"^\s*\d+[\.)]\s")
92
+ unordered_child_pat = re.compile(r"^ {1,3}[-*+]\s")
93
+
94
+ for i, line in enumerate(lines):
95
+ # 代码块内不处理
96
+ if fence_pat.match(line):
97
+ in_code_block = not in_code_block
98
+ if in_code_block:
99
+ in_ol_block = False
100
+ continue
101
+ if in_code_block:
102
+ continue
103
+
104
+ # 空行或标题重置状态
105
+ if not line.strip() or line.lstrip().startswith('#'):
106
+ in_ol_block = False
107
+ continue
108
+
109
+ # 新的有序列表项开始
110
+ if ol_pat.match(line):
111
+ in_ol_block = True
112
+ continue
113
+
114
+ # 在有序列表块中,将1-3空格缩进的无序项提升为4空格
115
+ if in_ol_block and unordered_child_pat.match(line):
116
+ lines[i] = ' ' + line.lstrip()
117
+
118
+ return '\n'.join(lines)
119
+
120
+ markdown_text = _normalize_markdown_lists(markdown_text)
121
+
122
+ # 配置markdown扩展
123
+ md = markdown.Markdown(extensions=[
124
+ 'fenced_code', # 代码块支持
125
+ 'tables', # 表格支持
126
+ 'nl2br', # 换行转换
127
+ 'codehilite' # 代码高亮
128
+ ])
129
+
130
+ # 转换markdown为HTML
131
+ html_content = md.convert(markdown_text)
132
+
133
+ # 添加CSS样式来美化HTML显示
134
+ styled_html = f"""
135
+ <style>
136
+ body {{
137
+ font-family: 'Segoe UI', 'Arial', sans-serif;
138
+ line-height: 1.6;
139
+ color: #ffffff;
140
+ margin: 0;
141
+ padding: 5px;
142
+ }}
143
+ h1, h2, h3, h4, h5, h6 {{
144
+ color: #ffffff;
145
+ margin-top: 20px;
146
+ margin-bottom: 10px;
147
+ }}
148
+ h1 {{ border-bottom: 2px solid #444; padding-bottom: 8px; }}
149
+ h2 {{ border-bottom: 1px solid #444; padding-bottom: 4px; }}
150
+ code {{
151
+ background-color: #2d2d2d;
152
+ padding: 2px 4px;
153
+ border-radius: 3px;
154
+ font-family: 'Consolas', 'Monaco', monospace;
155
+ color: #ffa500;
156
+ }}
157
+ pre {{
158
+ background-color: #2d2d2d;
159
+ padding: 12px;
160
+ border-radius: 6px;
161
+ border-left: 4px solid #007bff;
162
+ overflow-x: auto;
163
+ }}
164
+ pre code {{
165
+ background-color: transparent;
166
+ padding: 0;
167
+ color: #ffffff;
168
+ }}
169
+ blockquote {{
170
+ border-left: 4px solid #007bff;
171
+ margin: 16px 0;
172
+ padding-left: 16px;
173
+ color: #cccccc;
174
+ font-style: italic;
175
+ }}
176
+ table {{
177
+ border-collapse: collapse;
178
+ width: 100%;
179
+ margin: 16px 0;
180
+ }}
181
+ th, td {{
182
+ border: 1px solid #444;
183
+ padding: 8px 12px;
184
+ text-align: left;
185
+ }}
186
+ th {{
187
+ background-color: #333;
188
+ font-weight: bold;
189
+ }}
190
+ ul, ol {{
191
+ padding-left: 20px;
192
+ margin: 12px 0;
193
+ }}
194
+ li {{
195
+ margin: 4px 0;
196
+ }}
197
+ a {{
198
+ color: #4da6ff;
199
+ text-decoration: none;
200
+ }}
201
+ a:hover {{
202
+ text-decoration: underline;
203
+ }}
204
+ .image-container {{
205
+ margin: 8px 0;
206
+ }}
207
+ .image-path {{
208
+ font-size: 11px;
209
+ color: #888;
210
+ margin-top: 4px;
211
+ word-break: break-all;
212
+ }}
213
+ img {{
214
+ border-radius: 4px;
215
+ }}
216
+ </style>
217
+ {html_content}
218
+ """
219
+
220
+ # 先添加图片资源到文档(必须在setHtml之前)
221
+ for path in image_paths:
222
+ pixmap = QPixmap(path)
223
+ if not pixmap.isNull():
224
+ self.document().addResource(QTextDocument.ResourceType.ImageResource, QUrl(path), pixmap)
225
+
226
+ self.setHtml(styled_html)
227
+
228
+ except Exception as e:
229
+ # 如果markdown解析失败,回退到纯文本显示
230
+ self.setPlainText(markdown_text)
231
+ sys.stderr.write(f"Markdown parsing error: {e}\n")
232
+
233
+ def mousePressEvent(self, event: QMouseEvent):
234
+ """处理鼠标点击事件,支持点击图片放大和链接打开"""
235
+ if event.button() == Qt.LeftButton:
236
+ # 检查是否点击了链接
237
+ anchor = self.anchorAt(event.pos())
238
+ if anchor:
239
+ QDesktopServices.openUrl(QUrl(anchor))
240
+ return
241
+
242
+ cursor = self.cursorForPosition(event.pos())
243
+ char_format = cursor.charFormat()
244
+
245
+ # 检查是否点击了图片
246
+ if char_format.isImageFormat():
247
+ img_path = char_format.toImageFormat().name()
248
+ if os.path.exists(img_path):
249
+ dialog = ImageViewerDialog(img_path, self)
250
+ dialog.exec()
251
+ return
252
+
253
+ super().mousePressEvent(event)
254
+
255
+ def mouseMoveEvent(self, event: QMouseEvent):
256
+ """处理鼠标移动事件,链接上显示手型光标"""
257
+ anchor = self.anchorAt(event.pos())
258
+ if anchor:
259
+ self.viewport().setCursor(Qt.PointingHandCursor)
260
+ else:
261
+ self.viewport().setCursor(Qt.IBeamCursor)
262
+ super().mouseMoveEvent(event)
context_formatter.py ADDED
@@ -0,0 +1,301 @@
1
+ """
2
+ 上下文信息格式化模块
3
+ 用于格式化工作空间、阶段、任务等上下文信息
4
+ """
5
+ import json
6
+ from pathlib import Path
7
+ from typing import Optional, Dict, Any
8
+ from workspace_manager import WorkspaceManager, get_session_title_for_session
9
+
10
+
11
+ def load_task_list(session_id: str, project_path: str = None) -> list:
12
+ """加载任务列表
13
+
14
+ Args:
15
+ session_id: 会话ID
16
+ project_path: 项目路径
17
+
18
+ Returns:
19
+ 任务列表,每个任务包含 id, title, state
20
+ """
21
+ try:
22
+ if not project_path:
23
+ project_path = Path.cwd()
24
+ else:
25
+ project_path = Path(project_path)
26
+
27
+ task_file = project_path / '.workspace' / 'tasks' / f'{session_id}.json'
28
+ if not task_file.exists():
29
+ return []
30
+
31
+ with open(task_file, 'r', encoding='utf-8') as f:
32
+ data = json.load(f)
33
+ return data.get('tasks', [])
34
+ except Exception:
35
+ return []
36
+
37
+
38
+ def format_for_stop_hook(session_id: str, project_path: str = None) -> str:
39
+ """格式化上下文信息用于stop hook
40
+
41
+ Args:
42
+ session_id: 会话ID
43
+ project_path: 项目路径
44
+
45
+ Returns:
46
+ 格式化的上下文信息字符串
47
+ """
48
+ lines = []
49
+ has_content = False
50
+
51
+ # 获取会话标题
52
+ session_title = get_session_title_for_session(session_id, project_path)
53
+
54
+ # 获取阶段信息
55
+ workspace_mgr = WorkspaceManager(project_path)
56
+ stage_info = workspace_mgr.get_stage_info(session_id)
57
+
58
+ # 获取任务列表
59
+ tasks = load_task_list(session_id, project_path)
60
+
61
+ # 只有在有阶段信息或任务列表时才显示
62
+ if stage_info or tasks or session_title:
63
+ lines.append("# 当前上下文相关信息:")
64
+ lines.append("")
65
+ has_content = True
66
+
67
+ # 显示会话标题(仅当有值时)
68
+ if session_title:
69
+ # 检查是否为默认的新会话标题(包含"新会话"或以"New conversation"开头)
70
+ if "新会话" in session_title or session_title.startswith("New conversation"):
71
+ lines.append("## 当前会话标题:" + session_title)
72
+ lines.append("⚠️ **提醒:请根据工作内容及时更新会话标题**")
73
+ lines.append("")
74
+ else:
75
+ lines.append("## 当前会话标题:" + session_title)
76
+ lines.append("")
77
+
78
+ # 显示阶段信息(仅当有值时)
79
+ if stage_info:
80
+ current_stage = stage_info.get('current_stage', {})
81
+ next_stage = stage_info.get('next_stage', {})
82
+
83
+ if current_stage and current_stage.get('title'):
84
+ lines.append(f"## 当前阶段:{current_stage.get('title')}")
85
+
86
+ if next_stage and next_stage.get('title'):
87
+ lines.append(f"## 下一个阶段:{next_stage.get('title')}")
88
+ elif current_stage: # 有当前阶段但没有下一阶段
89
+ lines.append("## 下一个阶段:已完成所有阶段")
90
+
91
+ # 显示任务列表(仅当有任务时)
92
+ if tasks:
93
+ lines.append("## 任务列表:")
94
+
95
+ for task in tasks:
96
+ state = task.get('state', 'pending')
97
+ title = task.get('title', '未命名任务')
98
+ task_id = task.get('id', '')
99
+
100
+ # 简化标题显示
101
+ if '(执行前请查看该步骤的详细规则)' in title:
102
+ title = title.replace('(执行前请查看该步骤的详细规则)', '').strip()
103
+
104
+ if state == 'completed':
105
+ checkbox = '[x]'
106
+ elif state == 'in_progress':
107
+ checkbox = '[~]'
108
+ else:
109
+ checkbox = '[ ]'
110
+
111
+ lines.append(f"- {checkbox} {task_id}. {title}")
112
+
113
+ lines.append("")
114
+
115
+ # 添加提示信息
116
+ if has_content:
117
+ lines.append(f"session_id:{session_id}")
118
+ lines.append("请分析接下来的行动计划,是继续自动工作还是使用 feedback mcp 工具向用户反馈/请示")
119
+ lines.append("请注意:只有通过 feedback mcp 用户才能收到你的信息,才能对你的消息进行反馈、确认")
120
+
121
+ else:
122
+ # 没有上下文信息时的简化提示
123
+ lines.append(f"session_id:{session_id}")
124
+ lines.append("请使用 feedback 工具向用户反馈/请示")
125
+
126
+ return "\n".join(lines)
127
+
128
+
129
+ def format_context_info(session_id: str, project_path: str = None) -> Optional[str]:
130
+ """格式化上下文信息(仅用于显示给用户)
131
+
132
+ Args:
133
+ session_id: 会话ID
134
+ project_path: 项目路径
135
+
136
+ Returns:
137
+ 格式化的上下文信息字符串,如果没有上下文信息则返回None
138
+ """
139
+ lines = []
140
+ has_content = False
141
+
142
+ # 获取阶段信息
143
+ workspace_mgr = WorkspaceManager(project_path)
144
+ stage_info = workspace_mgr.get_stage_info(session_id)
145
+
146
+ # 获取任务列表
147
+ tasks = load_task_list(session_id, project_path)
148
+
149
+ # 只有在有阶段信息或任务列表时才添加内容
150
+ if not stage_info and not tasks:
151
+ return None
152
+
153
+ lines.append("## 📋 当前上下文")
154
+ lines.append("")
155
+
156
+ # 显示阶段信息(仅当有值时)
157
+ if stage_info:
158
+ current_stage = stage_info.get('current_stage', {})
159
+ next_stage = stage_info.get('next_stage', {})
160
+
161
+ if current_stage and current_stage.get('title'):
162
+ lines.append(f"**当前阶段**: {current_stage.get('title')}")
163
+ has_content = True
164
+
165
+ if next_stage and next_stage.get('title'):
166
+ lines.append(f"**下一阶段**: {next_stage.get('title')}")
167
+ has_content = True
168
+ elif current_stage: # 有当前阶段但没有下一阶段
169
+ lines.append("**下一阶段**: 已完成所有阶段")
170
+ has_content = True
171
+
172
+ if has_content:
173
+ lines.append("")
174
+
175
+ # 显示任务列表(仅当有任务时)
176
+ if tasks:
177
+ # 找出当前任务和下一任务
178
+ current_task = None
179
+ next_task = None
180
+
181
+ for i, task in enumerate(tasks):
182
+ state = task.get('state', 'pending')
183
+ if state == 'in_progress':
184
+ current_task = task
185
+ # 找下一个待处理的任务
186
+ for j in range(i + 1, len(tasks)):
187
+ if tasks[j].get('state', 'pending') == 'pending':
188
+ next_task = tasks[j]
189
+ break
190
+ break
191
+
192
+ # 如果没有进行中的任务,找第一个待处理的任务
193
+ if not current_task:
194
+ for task in tasks:
195
+ if task.get('state', 'pending') == 'pending':
196
+ current_task = task
197
+ # 找下一个
198
+ idx = tasks.index(task)
199
+ for j in range(idx + 1, len(tasks)):
200
+ if tasks[j].get('state', 'pending') == 'pending':
201
+ next_task = tasks[j]
202
+ break
203
+ break
204
+
205
+ if current_task:
206
+ title = current_task.get('title', '未命名任务')
207
+ # 简化标题显示
208
+ if '(执行前请查看该步骤的详细规则)' in title:
209
+ title = title.replace('(执行前请查看该步骤的详细规则)', '').strip()
210
+ lines.append(f"**当前任务**: {title}")
211
+ has_content = True
212
+
213
+ if next_task:
214
+ title = next_task.get('title', '未命名任务')
215
+ # 简化标题显示
216
+ if '(执行前请查看该步骤的详细规则)' in title:
217
+ title = title.replace('(执行前请查看该步骤的详细规则)', '').strip()
218
+ lines.append(f"**下一任务**: {title}")
219
+ has_content = True
220
+ elif current_task and not next_task:
221
+ lines.append("**下一任务**: 无(已是最后一个任务)")
222
+ has_content = True
223
+
224
+ if not has_content:
225
+ return None
226
+
227
+ return "\n".join(lines)
228
+
229
+
230
+ def format_for_feedback(session_id: str, project_path: str = None) -> Optional[str]:
231
+ """格式化完整的feedback信息(AI规则+上下文),用于返回给AI
232
+
233
+ Args:
234
+ session_id: 会话ID
235
+ project_path: 项目路径
236
+
237
+ Returns:
238
+ 格式化的完整信息字符串,如果没有上下文信息则返回None
239
+ """
240
+ # 获取阶段信息
241
+ workspace_mgr = WorkspaceManager(project_path)
242
+ stage_info = workspace_mgr.get_stage_info(session_id)
243
+
244
+ # 获取任务列表
245
+ tasks = load_task_list(session_id, project_path)
246
+
247
+ # 只有在有阶段信息或任务列表时才添加内容
248
+ if not stage_info and not tasks:
249
+ return None
250
+
251
+ lines = []
252
+ lines.append("## AI(你的)工作规则")
253
+ lines.append("")
254
+ lines.append("### 核心原则")
255
+ lines.append("1. **严格遵循当前阶段**: 只能执行当前阶段的工作内容")
256
+ lines.append("2. **严格遵循任务顺序**: 只能执行当前任务,完成后才能进入下一任务")
257
+ lines.append("3. **禁止跨阶段**: feedback反馈选项只能针对『当前阶段』或『下一阶段』,禁止跨过下一阶段")
258
+ lines.append("")
259
+ lines.append("### Feedback使用规则(必须遵守)")
260
+ lines.append("**禁止反馈以下内容:**")
261
+ lines.append("- ❌ '好的,需求分析已确认'(没有实际工作)")
262
+ lines.append("- ❌ '现在我将切换到下一阶段'(意图声明)")
263
+ lines.append("- ❌ '让我开始XXX'(计划声明)")
264
+ lines.append("- ❌ '我正在XXX'(进度声明)")
265
+ lines.append("")
266
+ lines.append("**只能反馈:**")
267
+ lines.append("- ✓ 已完成的工作结果")
268
+ lines.append("- ✓ 需要用户确认/选择的事项")
269
+ lines.append("- ✓ 遇到的问题需要用户解决")
270
+ lines.append("")
271
+ lines.append("**正确流程:**")
272
+ lines.append("用户要求切换阶段 → 先调用workspace_next_stage → 再开始工作 → 完成后再feedback")
273
+ lines.append("")
274
+ lines.append("### 反馈选项约束")
275
+ lines.append("**允许的反馈选项:**")
276
+ lines.append("- ✓ 当前阶段的操作 (如『继续当前工作』『修改当前成果』等)")
277
+ lines.append("- ✓ 进入下一阶段 (如『确认,进入<下一阶段名称>』)")
278
+ lines.append("")
279
+ lines.append("**禁止的反馈选项:**")
280
+ lines.append("- ❌ 跨过下一阶段的操作 (如当前=阶段1,下一=阶段2,禁止出现『直接进入阶段3』)")
281
+ lines.append("- ❌ 跳过当前阶段的流程 (如当前阶段未完成就提供后续阶段的选项)")
282
+ lines.append("")
283
+ lines.append("### 操作检查清单")
284
+ lines.append("在提供feedback选项前,必须检查:")
285
+ lines.append("1. ✓ 当前阶段名称是什么?")
286
+ lines.append("2. ✓ 下一阶段名称是什么?")
287
+ lines.append("3. ✓ 我提供的选项是否只涉及『当前阶段』或『下一阶段』?")
288
+ lines.append("4. ✓ 是否有跨过下一阶段的选项? (有则删除)")
289
+ lines.append("")
290
+ lines.append("### 示例说明")
291
+ lines.append("假设: 当前阶段=A, 下一阶段=B, 再下一阶段=C")
292
+ lines.append("- ✓ 正确: 反馈选项『继续A阶段工作』『完成A进入B阶段』")
293
+ lines.append("- ❌ 错误: 反馈选项『继续A阶段工作』『直接进入C阶段』(跨过了B)")
294
+ lines.append("")
295
+
296
+ # 添加上下文信息
297
+ context_info = format_context_info(session_id, project_path)
298
+ if context_info:
299
+ lines.append(context_info)
300
+
301
+ return "\n".join(lines)
debug_logger.py ADDED
@@ -0,0 +1,107 @@
1
+ """
2
+ 调试日志模块 - 用于路径配置和指令加载的调试
3
+ """
4
+ import os
5
+ import time
6
+ from datetime import datetime
7
+ from typing import Optional
8
+
9
+
10
+ class DebugLogger:
11
+ """调试日志记录器"""
12
+
13
+ def __init__(self, log_file: str = "log.txt"):
14
+ # 统一使用脚本所在目录,确保日志位置固定
15
+ script_dir = os.path.dirname(os.path.abspath(__file__))
16
+ self.log_path = os.path.join(script_dir, log_file)
17
+ self._init_log()
18
+
19
+ def _init_log(self):
20
+ """初始化日志文件"""
21
+ try:
22
+ with open(self.log_path, 'a', encoding='utf-8') as f:
23
+ f.write(f"\n=== Statistics Log Session Started at {datetime.now()} ===\n")
24
+ f.write(f"Log file: {self.log_path}\n")
25
+ f.write(f"Script dir: {os.path.dirname(os.path.abspath(__file__))}\n")
26
+ f.write("=" * 50 + "\n")
27
+ except Exception as e:
28
+ pass # 如果无法创建日志文件,静默失败
29
+
30
+ def log(self, message: str, category: str = "INFO"):
31
+ """写入日志消息"""
32
+ try:
33
+ timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
34
+ log_line = f"{timestamp} [INFO] statistics: [{category}] {message}\n"
35
+
36
+ with open(self.log_path, 'a', encoding='utf-8') as f:
37
+ f.write(log_line)
38
+ except Exception:
39
+ pass # 如果写入失败,静默失败
40
+
41
+ def log_path_config(self, script_dir: str, config_type: str = "INIT"):
42
+ """记录路径配置信息"""
43
+ self.log(f"PathConfig {config_type}: script_dir={script_dir}", "PATH")
44
+
45
+ def log_personal_commands_load(self, prompts_dir: str, exists: bool, use_config: bool):
46
+ """记录个人指令加载信息"""
47
+ source = "路径配置" if use_config else "降级路径"
48
+ self.log(f"加载个人指令,使用{source}: {prompts_dir}", "LOAD")
49
+ self.log(f"个人指令目录是否存在: {exists}", "LOAD")
50
+
51
+ def log_dir_scan(self, prompts_dir: str, filenames: list, mdc_files: list):
52
+ """记录目录扫描结果"""
53
+ self.log(f"目录扫描 {prompts_dir}", "SCAN")
54
+ self.log(f"所有文件: {filenames}", "SCAN")
55
+ self.log(f"mdc文件: {mdc_files}", "SCAN")
56
+
57
+ def log_commands_result(self, command_type: str, count: int):
58
+ """记录指令加载结果"""
59
+ self.log(f"加载到{command_type}指令数量: {count}", "RESULT")
60
+
61
+ def log_save_operation(self, operation: str, path: str, success: bool, error: Optional[str] = None):
62
+ """记录保存操作"""
63
+ status = "成功" if success else "失败"
64
+ self.log(f"{operation}保存{status}: {path}", "SAVE")
65
+ if error:
66
+ self.log(f"错误详情: {error}", "ERROR")
67
+
68
+ def log_error(self, message: str, module: str = "SYSTEM"):
69
+ """记录ERROR级别日志"""
70
+ try:
71
+ timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
72
+ log_line = f"{timestamp} [ERROR] {module}: {message}\n"
73
+
74
+ with open(self.log_path, 'a', encoding='utf-8') as f:
75
+ f.write(log_line)
76
+ except Exception:
77
+ pass # 如果写入失败,静默失败
78
+
79
+ def log_warning(self, message: str, module: str = "SYSTEM"):
80
+ """记录WARNING级别日志"""
81
+ try:
82
+ timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
83
+ log_line = f"{timestamp} [WARNING] {module}: {message}\n"
84
+
85
+ with open(self.log_path, 'a', encoding='utf-8') as f:
86
+ f.write(log_line)
87
+ except Exception:
88
+ pass # 如果写入失败,静默失败
89
+
90
+ def log_legacy_error(self, operation: str, error: str):
91
+ """记录错误信息 - 兼容旧版本"""
92
+ self.log(f"{operation}: {error}", "ERROR")
93
+
94
+ def get_log_path(self) -> str:
95
+ """获取日志文件路径"""
96
+ return self.log_path
97
+
98
+
99
+ # 全局日志实例
100
+ _debug_logger = None
101
+
102
+ def get_debug_logger() -> DebugLogger:
103
+ """获取全局调试日志实例"""
104
+ global _debug_logger
105
+ if _debug_logger is None:
106
+ _debug_logger = DebugLogger()
107
+ return _debug_logger