jarvis-ai-assistant 0.3.18__py3-none-any.whl → 0.3.20__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.
- jarvis/__init__.py +1 -1
- jarvis/jarvis_agent/__init__.py +30 -12
- jarvis/jarvis_agent/config_editor.py +1 -1
- jarvis/jarvis_agent/edit_file_handler.py +8 -13
- jarvis/jarvis_agent/memory_manager.py +4 -4
- jarvis/jarvis_agent/shell_input_handler.py +17 -2
- jarvis/jarvis_agent/task_analyzer.py +4 -3
- jarvis/jarvis_agent/task_manager.py +6 -6
- jarvis/jarvis_agent/tool_executor.py +2 -2
- jarvis/jarvis_code_agent/code_agent.py +21 -29
- jarvis/jarvis_code_analysis/code_review.py +2 -4
- jarvis/jarvis_git_utils/git_commiter.py +17 -18
- jarvis/jarvis_methodology/main.py +12 -12
- jarvis/jarvis_platform/ai8.py +0 -4
- jarvis/jarvis_platform/base.py +16 -15
- jarvis/jarvis_platform/kimi.py +13 -13
- jarvis/jarvis_platform/tongyi.py +17 -15
- jarvis/jarvis_platform/yuanbao.py +11 -11
- jarvis/jarvis_platform_manager/service.py +2 -2
- jarvis/jarvis_rag/cli.py +36 -32
- jarvis/jarvis_rag/embedding_manager.py +11 -6
- jarvis/jarvis_rag/llm_interface.py +6 -5
- jarvis/jarvis_rag/rag_pipeline.py +9 -8
- jarvis/jarvis_rag/reranker.py +3 -2
- jarvis/jarvis_rag/retriever.py +18 -8
- jarvis/jarvis_smart_shell/main.py +307 -47
- jarvis/jarvis_stats/cli.py +2 -1
- jarvis/jarvis_stats/stats.py +45 -5
- jarvis/jarvis_stats/storage.py +220 -9
- jarvis/jarvis_tools/clear_memory.py +0 -11
- jarvis/jarvis_tools/cli/main.py +18 -17
- jarvis/jarvis_tools/edit_file.py +4 -4
- jarvis/jarvis_tools/execute_script.py +5 -1
- jarvis/jarvis_tools/file_analyzer.py +6 -6
- jarvis/jarvis_tools/generate_new_tool.py +6 -17
- jarvis/jarvis_tools/read_code.py +3 -6
- jarvis/jarvis_tools/read_webpage.py +4 -4
- jarvis/jarvis_tools/registry.py +8 -28
- jarvis/jarvis_tools/retrieve_memory.py +5 -16
- jarvis/jarvis_tools/rewrite_file.py +0 -4
- jarvis/jarvis_tools/save_memory.py +2 -10
- jarvis/jarvis_tools/search_web.py +5 -8
- jarvis/jarvis_tools/virtual_tty.py +22 -40
- jarvis/jarvis_utils/clipboard.py +2 -2
- jarvis/jarvis_utils/input.py +316 -30
- jarvis/jarvis_utils/methodology.py +3 -3
- jarvis/jarvis_utils/output.py +215 -135
- jarvis/jarvis_utils/utils.py +35 -58
- {jarvis_ai_assistant-0.3.18.dist-info → jarvis_ai_assistant-0.3.20.dist-info}/METADATA +1 -1
- {jarvis_ai_assistant-0.3.18.dist-info → jarvis_ai_assistant-0.3.20.dist-info}/RECORD +54 -54
- {jarvis_ai_assistant-0.3.18.dist-info → jarvis_ai_assistant-0.3.20.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.3.18.dist-info → jarvis_ai_assistant-0.3.20.dist-info}/entry_points.txt +0 -0
- {jarvis_ai_assistant-0.3.18.dist-info → jarvis_ai_assistant-0.3.20.dist-info}/licenses/LICENSE +0 -0
- {jarvis_ai_assistant-0.3.18.dist-info → jarvis_ai_assistant-0.3.20.dist-info}/top_level.txt +0 -0
jarvis/jarvis_utils/output.py
CHANGED
@@ -10,7 +10,7 @@
|
|
10
10
|
"""
|
11
11
|
from datetime import datetime
|
12
12
|
from enum import Enum
|
13
|
-
from typing import Dict, Optional, Tuple, Any
|
13
|
+
from typing import Dict, Optional, Tuple, Any, List
|
14
14
|
|
15
15
|
from pygments.lexers import guess_lexer
|
16
16
|
from pygments.util import ClassNotFound
|
@@ -22,6 +22,8 @@ from rich.text import Text
|
|
22
22
|
|
23
23
|
from jarvis.jarvis_utils.config import get_pretty_output
|
24
24
|
from jarvis.jarvis_utils.globals import console, get_agent_list
|
25
|
+
from dataclasses import dataclass
|
26
|
+
from abc import ABC, abstractmethod
|
25
27
|
|
26
28
|
|
27
29
|
class OutputType(Enum):
|
@@ -57,6 +59,181 @@ class OutputType(Enum):
|
|
57
59
|
TOOL = "TOOL"
|
58
60
|
|
59
61
|
|
62
|
+
@dataclass
|
63
|
+
class OutputEvent:
|
64
|
+
"""
|
65
|
+
输出事件的通用结构,供不同输出后端(Sink)消费。
|
66
|
+
- text: 文本内容
|
67
|
+
- output_type: 输出类型
|
68
|
+
- timestamp: 是否显示时间戳
|
69
|
+
- lang: 语法高亮语言(可选,不提供则自动检测)
|
70
|
+
- traceback: 是否显示异常堆栈
|
71
|
+
- section: 若为章节标题输出,填入标题文本;否则为None
|
72
|
+
- context: 额外上下文(预留给TUI/日志等)
|
73
|
+
"""
|
74
|
+
text: str
|
75
|
+
output_type: OutputType
|
76
|
+
timestamp: bool = True
|
77
|
+
lang: Optional[str] = None
|
78
|
+
traceback: bool = False
|
79
|
+
section: Optional[str] = None
|
80
|
+
context: Optional[Dict[str, Any]] = None
|
81
|
+
|
82
|
+
|
83
|
+
class OutputSink(ABC):
|
84
|
+
"""输出后端抽象接口,不同前端(控制台/TUI/SSE/日志)实现该接口以消费输出事件。"""
|
85
|
+
|
86
|
+
@abstractmethod
|
87
|
+
def emit(self, event: OutputEvent) -> None: # pragma: no cover - 抽象方法
|
88
|
+
raise NotImplementedError
|
89
|
+
|
90
|
+
|
91
|
+
class ConsoleOutputSink(OutputSink):
|
92
|
+
"""
|
93
|
+
默认控制台输出实现,保持与原 PrettyOutput 行为一致。
|
94
|
+
"""
|
95
|
+
|
96
|
+
def emit(self, event: OutputEvent) -> None:
|
97
|
+
# 章节输出
|
98
|
+
if event.section is not None:
|
99
|
+
text = Text(event.section, style=event.output_type.value, justify="center")
|
100
|
+
panel = Panel(text, border_style=event.output_type.value)
|
101
|
+
if get_pretty_output():
|
102
|
+
console.print(panel)
|
103
|
+
else:
|
104
|
+
console.print(text)
|
105
|
+
return
|
106
|
+
|
107
|
+
# 普通内容输出
|
108
|
+
lang = (
|
109
|
+
event.lang
|
110
|
+
if event.lang is not None
|
111
|
+
else PrettyOutput._detect_language(event.text, default_lang="markdown")
|
112
|
+
)
|
113
|
+
|
114
|
+
# 与原实现保持一致的样式定义
|
115
|
+
styles: Dict[OutputType, Dict[str, Any]] = {
|
116
|
+
OutputType.SYSTEM: dict(bgcolor="#1e2b3c"),
|
117
|
+
OutputType.CODE: dict(bgcolor="#1c2b1c"),
|
118
|
+
OutputType.RESULT: dict(bgcolor="#1c1c2b"),
|
119
|
+
OutputType.ERROR: dict(bgcolor="#2b1c1c"),
|
120
|
+
OutputType.INFO: dict(bgcolor="#2b2b1c", meta={"icon": "ℹ️"}),
|
121
|
+
OutputType.PLANNING: dict(bgcolor="#2b1c2b"),
|
122
|
+
OutputType.PROGRESS: dict(bgcolor="#1c1c1c"),
|
123
|
+
OutputType.SUCCESS: dict(bgcolor="#1c2b1c"),
|
124
|
+
OutputType.WARNING: dict(bgcolor="#2b2b1c"),
|
125
|
+
OutputType.DEBUG: dict(bgcolor="#1c1c1c"),
|
126
|
+
OutputType.USER: dict(bgcolor="#1c2b2b"),
|
127
|
+
OutputType.TOOL: dict(bgcolor="#1c2b2b"),
|
128
|
+
}
|
129
|
+
|
130
|
+
header_styles = {
|
131
|
+
OutputType.SYSTEM: RichStyle(
|
132
|
+
color="bright_cyan", bgcolor="#1e2b3c", frame=True, meta={"icon": "🤖"}
|
133
|
+
),
|
134
|
+
OutputType.CODE: RichStyle(
|
135
|
+
color="green", bgcolor="#1c2b1c", frame=True, meta={"icon": "📝"}
|
136
|
+
),
|
137
|
+
OutputType.RESULT: RichStyle(
|
138
|
+
color="bright_blue", bgcolor="#1c1c2b", frame=True, meta={"icon": "✨"}
|
139
|
+
),
|
140
|
+
OutputType.ERROR: RichStyle(
|
141
|
+
color="red", frame=True, bgcolor="#2b1c1c", meta={"icon": "❌"}
|
142
|
+
),
|
143
|
+
OutputType.INFO: RichStyle(
|
144
|
+
color="bright_cyan", frame=True, bgcolor="#2b2b1c", meta={"icon": "ℹ️"}
|
145
|
+
),
|
146
|
+
OutputType.PLANNING: RichStyle(
|
147
|
+
color="purple",
|
148
|
+
bold=True,
|
149
|
+
frame=True,
|
150
|
+
bgcolor="#2b1c2b",
|
151
|
+
meta={"icon": "📋"},
|
152
|
+
),
|
153
|
+
OutputType.PROGRESS: RichStyle(
|
154
|
+
color="white",
|
155
|
+
encircle=True,
|
156
|
+
frame=True,
|
157
|
+
bgcolor="#1c1c1c",
|
158
|
+
meta={"icon": "⏳"},
|
159
|
+
),
|
160
|
+
OutputType.SUCCESS: RichStyle(
|
161
|
+
color="bright_green",
|
162
|
+
bold=True,
|
163
|
+
strike=False,
|
164
|
+
bgcolor="#1c2b1c",
|
165
|
+
meta={"icon": "✅"},
|
166
|
+
),
|
167
|
+
OutputType.WARNING: RichStyle(
|
168
|
+
color="yellow",
|
169
|
+
bold=True,
|
170
|
+
blink2=True,
|
171
|
+
bgcolor="#2b2b1c",
|
172
|
+
meta={"icon": "⚠️"},
|
173
|
+
),
|
174
|
+
OutputType.DEBUG: RichStyle(
|
175
|
+
color="grey58",
|
176
|
+
dim=True,
|
177
|
+
conceal=True,
|
178
|
+
bgcolor="#1c1c1c",
|
179
|
+
meta={"icon": "🔍"},
|
180
|
+
),
|
181
|
+
OutputType.USER: RichStyle(
|
182
|
+
color="spring_green2",
|
183
|
+
frame=True,
|
184
|
+
bgcolor="#1c2b2b",
|
185
|
+
meta={"icon": "👤"},
|
186
|
+
),
|
187
|
+
OutputType.TOOL: RichStyle(
|
188
|
+
color="dark_sea_green4",
|
189
|
+
bgcolor="#1c2b2b",
|
190
|
+
frame=True,
|
191
|
+
meta={"icon": "🔧"},
|
192
|
+
),
|
193
|
+
}
|
194
|
+
|
195
|
+
header = Text(
|
196
|
+
PrettyOutput._format(event.output_type, event.timestamp),
|
197
|
+
style=header_styles[event.output_type],
|
198
|
+
)
|
199
|
+
content = Syntax(
|
200
|
+
event.text,
|
201
|
+
lang,
|
202
|
+
theme="monokai",
|
203
|
+
word_wrap=True,
|
204
|
+
background_color=styles[event.output_type]["bgcolor"],
|
205
|
+
)
|
206
|
+
panel = Panel(
|
207
|
+
content,
|
208
|
+
border_style=header_styles[event.output_type],
|
209
|
+
padding=(0, 0),
|
210
|
+
highlight=True,
|
211
|
+
)
|
212
|
+
if get_pretty_output():
|
213
|
+
console.print(panel)
|
214
|
+
else:
|
215
|
+
console.print(content)
|
216
|
+
if event.traceback or event.output_type == OutputType.ERROR:
|
217
|
+
try:
|
218
|
+
console.print_exception()
|
219
|
+
except Exception as e:
|
220
|
+
console.print(f"Error: {e}")
|
221
|
+
|
222
|
+
|
223
|
+
# 模块级输出分发器(默认注册控制台后端)
|
224
|
+
_output_sinks: List[OutputSink] = [ConsoleOutputSink()]
|
225
|
+
|
226
|
+
|
227
|
+
def emit_output(event: OutputEvent) -> None:
|
228
|
+
"""向所有已注册的输出后端广播事件。"""
|
229
|
+
for sink in list(_output_sinks):
|
230
|
+
try:
|
231
|
+
sink.emit(event)
|
232
|
+
except Exception as e:
|
233
|
+
# 后端故障不影响其他后端
|
234
|
+
console.print(f"[输出后端错误] {sink.__class__.__name__}: {e}")
|
235
|
+
|
236
|
+
|
60
237
|
class PrettyOutput:
|
61
238
|
"""
|
62
239
|
使用rich库格式化和显示富文本输出的类。
|
@@ -166,148 +343,51 @@ class PrettyOutput:
|
|
166
343
|
traceback: bool = False,
|
167
344
|
):
|
168
345
|
"""
|
169
|
-
|
170
|
-
|
171
|
-
参数:
|
172
|
-
text: 要打印的文本内容
|
173
|
-
output_type: 输出类型(影响样式)
|
174
|
-
timestamp: 是否显示时间戳
|
175
|
-
lang: 语法高亮的语言
|
176
|
-
traceback: 是否显示错误的回溯信息
|
346
|
+
使用样式和语法高亮打印格式化输出(已抽象为事件 + Sink 机制)。
|
347
|
+
保持对现有调用方的向后兼容,同时为TUI/日志等前端预留扩展点。
|
177
348
|
"""
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
OutputType.PLANNING: dict(bgcolor="#2b1c2b"),
|
185
|
-
OutputType.PROGRESS: dict(bgcolor="#1c1c1c"),
|
186
|
-
OutputType.SUCCESS: dict(bgcolor="#1c2b1c"),
|
187
|
-
OutputType.WARNING: dict(bgcolor="#2b2b1c"),
|
188
|
-
OutputType.DEBUG: dict(bgcolor="#1c1c1c"),
|
189
|
-
OutputType.USER: dict(bgcolor="#1c2b2b"),
|
190
|
-
OutputType.TOOL: dict(bgcolor="#1c2b2b"),
|
191
|
-
}
|
192
|
-
|
193
|
-
header_styles = {
|
194
|
-
OutputType.SYSTEM: RichStyle(
|
195
|
-
color="bright_cyan", bgcolor="#1e2b3c", frame=True, meta={"icon": "🤖"}
|
196
|
-
),
|
197
|
-
OutputType.CODE: RichStyle(
|
198
|
-
color="green", bgcolor="#1c2b1c", frame=True, meta={"icon": "📝"}
|
199
|
-
),
|
200
|
-
OutputType.RESULT: RichStyle(
|
201
|
-
color="bright_blue", bgcolor="#1c1c2b", frame=True, meta={"icon": "✨"}
|
202
|
-
),
|
203
|
-
OutputType.ERROR: RichStyle(
|
204
|
-
color="red", frame=True, bgcolor="#2b1c1c", meta={"icon": "❌"}
|
205
|
-
),
|
206
|
-
OutputType.INFO: RichStyle(
|
207
|
-
color="bright_cyan", frame=True, bgcolor="#2b2b1c", meta={"icon": "ℹ️"}
|
208
|
-
),
|
209
|
-
OutputType.PLANNING: RichStyle(
|
210
|
-
color="purple",
|
211
|
-
bold=True,
|
212
|
-
frame=True,
|
213
|
-
bgcolor="#2b1c2b",
|
214
|
-
meta={"icon": "📋"},
|
215
|
-
),
|
216
|
-
OutputType.PROGRESS: RichStyle(
|
217
|
-
color="white",
|
218
|
-
encircle=True,
|
219
|
-
frame=True,
|
220
|
-
bgcolor="#1c1c1c",
|
221
|
-
meta={"icon": "⏳"},
|
222
|
-
),
|
223
|
-
OutputType.SUCCESS: RichStyle(
|
224
|
-
color="bright_green",
|
225
|
-
bold=True,
|
226
|
-
strike=False,
|
227
|
-
bgcolor="#1c2b1c",
|
228
|
-
meta={"icon": "✅"},
|
229
|
-
),
|
230
|
-
OutputType.WARNING: RichStyle(
|
231
|
-
color="yellow",
|
232
|
-
bold=True,
|
233
|
-
blink2=True,
|
234
|
-
bgcolor="#2b2b1c",
|
235
|
-
meta={"icon": "⚠️"},
|
236
|
-
),
|
237
|
-
OutputType.DEBUG: RichStyle(
|
238
|
-
color="grey58",
|
239
|
-
dim=True,
|
240
|
-
conceal=True,
|
241
|
-
bgcolor="#1c1c1c",
|
242
|
-
meta={"icon": "🔍"},
|
243
|
-
),
|
244
|
-
OutputType.USER: RichStyle(
|
245
|
-
color="spring_green2",
|
246
|
-
frame=True,
|
247
|
-
bgcolor="#1c2b2b",
|
248
|
-
meta={"icon": "👤"},
|
249
|
-
),
|
250
|
-
OutputType.TOOL: RichStyle(
|
251
|
-
color="dark_sea_green4",
|
252
|
-
bgcolor="#1c2b2b",
|
253
|
-
frame=True,
|
254
|
-
meta={"icon": "🔧"},
|
255
|
-
),
|
256
|
-
}
|
257
|
-
|
258
|
-
lang = (
|
259
|
-
lang
|
260
|
-
if lang is not None
|
261
|
-
else PrettyOutput._detect_language(text, default_lang="markdown")
|
349
|
+
event = OutputEvent(
|
350
|
+
text=text,
|
351
|
+
output_type=output_type,
|
352
|
+
timestamp=timestamp,
|
353
|
+
lang=lang,
|
354
|
+
traceback=traceback,
|
262
355
|
)
|
263
|
-
|
264
|
-
PrettyOutput._format(output_type, timestamp),
|
265
|
-
style=header_styles[output_type],
|
266
|
-
)
|
267
|
-
content = Syntax(
|
268
|
-
text,
|
269
|
-
lang,
|
270
|
-
theme="monokai",
|
271
|
-
word_wrap=True,
|
272
|
-
background_color=styles[output_type]["bgcolor"],
|
273
|
-
)
|
274
|
-
panel = Panel(
|
275
|
-
content,
|
276
|
-
border_style=header_styles[output_type],
|
277
|
-
title=header,
|
278
|
-
title_align="left",
|
279
|
-
padding=(0, 0),
|
280
|
-
highlight=True,
|
281
|
-
)
|
282
|
-
if get_pretty_output():
|
283
|
-
console.print(panel)
|
284
|
-
else:
|
285
|
-
if len(text.strip().splitlines()) > 1:
|
286
|
-
console.print(header)
|
287
|
-
console.print(content)
|
288
|
-
else:
|
289
|
-
console.print(header, content)
|
290
|
-
if traceback or output_type == OutputType.ERROR:
|
291
|
-
try:
|
292
|
-
console.print_exception()
|
293
|
-
except Exception as e:
|
294
|
-
console.print(f"Error: {e}")
|
356
|
+
emit_output(event)
|
295
357
|
|
296
358
|
@staticmethod
|
297
359
|
def section(title: str, output_type: OutputType = OutputType.INFO):
|
298
360
|
"""
|
299
|
-
|
300
|
-
|
301
|
-
参数:
|
302
|
-
title: 章节标题文本
|
303
|
-
output_type: 输出类型(影响样式)
|
361
|
+
在样式化面板中打印章节标题(通过事件 + Sink 机制分发)。
|
304
362
|
"""
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
363
|
+
event = OutputEvent(
|
364
|
+
text="",
|
365
|
+
output_type=output_type,
|
366
|
+
section=title,
|
367
|
+
)
|
368
|
+
emit_output(event)
|
369
|
+
|
370
|
+
@staticmethod
|
371
|
+
# Sink管理(为外部注册自定义后端预留)
|
372
|
+
@staticmethod
|
373
|
+
def add_sink(sink: OutputSink) -> None:
|
374
|
+
"""注册一个新的输出后端。"""
|
375
|
+
_output_sinks.append(sink)
|
376
|
+
|
377
|
+
@staticmethod
|
378
|
+
def clear_sinks(keep_default: bool = True) -> None:
|
379
|
+
"""清空已注册的输出后端;可选择保留默认控制台后端。"""
|
380
|
+
if keep_default:
|
381
|
+
globals()["_output_sinks"] = [
|
382
|
+
s for s in _output_sinks if isinstance(s, ConsoleOutputSink)
|
383
|
+
]
|
309
384
|
else:
|
310
|
-
|
385
|
+
_output_sinks.clear()
|
386
|
+
|
387
|
+
@staticmethod
|
388
|
+
def get_sinks() -> List[OutputSink]:
|
389
|
+
"""获取当前已注册的输出后端列表(副本)。"""
|
390
|
+
return list(_output_sinks)
|
311
391
|
|
312
392
|
@staticmethod
|
313
393
|
def print_gradient_text(
|
jarvis/jarvis_utils/utils.py
CHANGED
@@ -139,7 +139,7 @@ def _check_pip_updates() -> bool:
|
|
139
139
|
# 检测是否安装了 RAG 特性
|
140
140
|
rag_installed = False
|
141
141
|
try:
|
142
|
-
import langchain # noqa
|
142
|
+
import langchain # type: ignore # noqa
|
143
143
|
|
144
144
|
rag_installed = True
|
145
145
|
except ImportError:
|
@@ -212,46 +212,37 @@ def _show_usage_stats(welcome_str: str) -> None:
|
|
212
212
|
"commit": {"title": "💾 提交统计", "metrics": {}, "suffix": "个"},
|
213
213
|
"command": {"title": "📱 命令使用", "metrics": {}, "suffix": "次"},
|
214
214
|
"adoption": {"title": "🎯 采纳情况", "metrics": {}, "suffix": ""},
|
215
|
+
"other": {"title": "📦 其他指标", "metrics": {}, "suffix": ""},
|
215
216
|
}
|
216
217
|
|
217
|
-
#
|
218
|
+
# 遍历所有指标,使用快速总量读取以避免全量扫描
|
218
219
|
for metric in all_metrics:
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
end_time=datetime.now(),
|
224
|
-
)
|
220
|
+
try:
|
221
|
+
total = StatsManager.get_metric_total(metric)
|
222
|
+
except Exception:
|
223
|
+
total = 0.0
|
225
224
|
|
226
|
-
if
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
total
|
248
|
-
)
|
249
|
-
else:
|
250
|
-
categorized_stats["code"]["metrics"][metric] = int(
|
251
|
-
total
|
252
|
-
)
|
253
|
-
elif group == "command":
|
254
|
-
categorized_stats["command"]["metrics"][metric] = int(total)
|
225
|
+
if not total or total <= 0:
|
226
|
+
continue
|
227
|
+
|
228
|
+
# 优先使用元信息中的分组(在写入指标时已记录)
|
229
|
+
info = StatsManager.get_metric_info(metric) or {}
|
230
|
+
group = info.get("group", "other")
|
231
|
+
|
232
|
+
if group == "tool":
|
233
|
+
categorized_stats["tool"]["metrics"][metric] = int(total)
|
234
|
+
elif group == "code_agent":
|
235
|
+
# 根据指标名称细分
|
236
|
+
if metric.startswith("code_lines_"):
|
237
|
+
categorized_stats["lines"]["metrics"][metric] = int(total)
|
238
|
+
elif "commit" in metric:
|
239
|
+
categorized_stats["commit"]["metrics"][metric] = int(total)
|
240
|
+
else:
|
241
|
+
categorized_stats["code"]["metrics"][metric] = int(total)
|
242
|
+
elif group == "command":
|
243
|
+
categorized_stats["command"]["metrics"][metric] = int(total)
|
244
|
+
else:
|
245
|
+
categorized_stats["other"]["metrics"][metric] = int(total)
|
255
246
|
|
256
247
|
# 合并长短命令的历史统计数据
|
257
248
|
command_stats = categorized_stats["command"]["metrics"]
|
@@ -560,7 +551,7 @@ def _show_usage_stats(welcome_str: str) -> None:
|
|
560
551
|
layout_items: List[RenderableType] = []
|
561
552
|
layout_items.append(right_column_group)
|
562
553
|
if has_content:
|
563
|
-
layout_items.append(table)
|
554
|
+
layout_items.append(Align.center(table))
|
564
555
|
layout_renderable = Group(*layout_items)
|
565
556
|
else:
|
566
557
|
# 左右布局(当前)
|
@@ -576,8 +567,8 @@ def _show_usage_stats(welcome_str: str) -> None:
|
|
576
567
|
layout_table.add_column(ratio=5) # 右侧
|
577
568
|
|
578
569
|
if has_content:
|
579
|
-
#
|
580
|
-
layout_table.add_row(right_column_group, table)
|
570
|
+
# 将总结信息放在左侧,统计表格放在右侧(表格居中显示)
|
571
|
+
layout_table.add_row(right_column_group, Align.center(table))
|
581
572
|
else:
|
582
573
|
# 如果没有统计数据,则总结信息占满
|
583
574
|
layout_table.add_row(right_column_group)
|
@@ -1161,7 +1152,7 @@ def while_success(func: Callable[[], Any], sleep_time: float = 0.1) -> Any:
|
|
1161
1152
|
return func()
|
1162
1153
|
except Exception as e:
|
1163
1154
|
PrettyOutput.print(
|
1164
|
-
f"
|
1155
|
+
f"重试中,等待 {sleep_time}s...", OutputType.WARNING
|
1165
1156
|
)
|
1166
1157
|
time.sleep(sleep_time)
|
1167
1158
|
continue
|
@@ -1185,7 +1176,7 @@ def while_true(func: Callable[[], bool], sleep_time: float = 0.1) -> Any:
|
|
1185
1176
|
ret = func()
|
1186
1177
|
if ret:
|
1187
1178
|
break
|
1188
|
-
PrettyOutput.print(f"
|
1179
|
+
PrettyOutput.print(f"重试中,等待 {sleep_time}s...", OutputType.WARNING)
|
1189
1180
|
time.sleep(sleep_time)
|
1190
1181
|
return ret
|
1191
1182
|
|
@@ -1267,7 +1258,7 @@ def _pull_git_repo(repo_path: Path, repo_type: str):
|
|
1267
1258
|
if not git_dir.is_dir():
|
1268
1259
|
return
|
1269
1260
|
|
1270
|
-
|
1261
|
+
|
1271
1262
|
try:
|
1272
1263
|
# 检查是否有远程仓库
|
1273
1264
|
remote_result = subprocess.run(
|
@@ -1281,10 +1272,6 @@ def _pull_git_repo(repo_path: Path, repo_type: str):
|
|
1281
1272
|
timeout=10,
|
1282
1273
|
)
|
1283
1274
|
if not remote_result.stdout.strip():
|
1284
|
-
PrettyOutput.print(
|
1285
|
-
f"'{repo_path.name}' 未配置远程仓库,跳过更新。",
|
1286
|
-
OutputType.INFO,
|
1287
|
-
)
|
1288
1275
|
return
|
1289
1276
|
|
1290
1277
|
# 检查git仓库状态
|
@@ -1356,10 +1343,6 @@ def _pull_git_repo(repo_path: Path, repo_type: str):
|
|
1356
1343
|
)
|
1357
1344
|
|
1358
1345
|
if not ls_remote_result.stdout.strip():
|
1359
|
-
PrettyOutput.print(
|
1360
|
-
f"{repo_type}库 '{repo_path.name}' 的远程仓库是空的,跳过更新。",
|
1361
|
-
OutputType.INFO,
|
1362
|
-
)
|
1363
1346
|
return
|
1364
1347
|
|
1365
1348
|
# 执行 git pull
|
@@ -1387,12 +1370,6 @@ def _pull_git_repo(repo_path: Path, repo_type: str):
|
|
1387
1370
|
PrettyOutput.print(
|
1388
1371
|
f"{repo_type}库 '{repo_path.name}' 已更新。", OutputType.SUCCESS
|
1389
1372
|
)
|
1390
|
-
if pull_result.stdout.strip():
|
1391
|
-
PrettyOutput.print(pull_result.stdout.strip(), OutputType.INFO)
|
1392
|
-
else:
|
1393
|
-
PrettyOutput.print(
|
1394
|
-
f"{repo_type}库 '{repo_path.name}' 已是最新版本。", OutputType.INFO
|
1395
|
-
)
|
1396
1373
|
|
1397
1374
|
except FileNotFoundError:
|
1398
1375
|
PrettyOutput.print(
|
@@ -1433,7 +1410,7 @@ def daily_check_git_updates(repo_dirs: List[str], repo_type: str):
|
|
1433
1410
|
pass
|
1434
1411
|
|
1435
1412
|
if should_check_for_updates:
|
1436
|
-
|
1413
|
+
|
1437
1414
|
for repo_dir in repo_dirs:
|
1438
1415
|
p_repo_dir = Path(repo_dir)
|
1439
1416
|
if p_repo_dir.exists() and p_repo_dir.is_dir():
|