jarvis-ai-assistant 0.3.30__py3-none-any.whl → 0.7.0__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 +289 -87
- jarvis/jarvis_agent/agent_manager.py +17 -8
- jarvis/jarvis_agent/edit_file_handler.py +374 -86
- jarvis/jarvis_agent/event_bus.py +1 -1
- jarvis/jarvis_agent/file_context_handler.py +79 -0
- jarvis/jarvis_agent/jarvis.py +601 -43
- jarvis/jarvis_agent/main.py +32 -2
- jarvis/jarvis_agent/rewrite_file_handler.py +141 -0
- jarvis/jarvis_agent/run_loop.py +38 -5
- jarvis/jarvis_agent/share_manager.py +8 -1
- jarvis/jarvis_agent/stdio_redirect.py +295 -0
- jarvis/jarvis_agent/task_analyzer.py +5 -2
- jarvis/jarvis_agent/task_planner.py +496 -0
- jarvis/jarvis_agent/utils.py +5 -1
- jarvis/jarvis_agent/web_bridge.py +189 -0
- jarvis/jarvis_agent/web_output_sink.py +53 -0
- jarvis/jarvis_agent/web_server.py +751 -0
- jarvis/jarvis_c2rust/__init__.py +26 -0
- jarvis/jarvis_c2rust/cli.py +613 -0
- jarvis/jarvis_c2rust/collector.py +258 -0
- jarvis/jarvis_c2rust/library_replacer.py +1122 -0
- jarvis/jarvis_c2rust/llm_module_agent.py +1300 -0
- jarvis/jarvis_c2rust/optimizer.py +960 -0
- jarvis/jarvis_c2rust/scanner.py +1681 -0
- jarvis/jarvis_c2rust/transpiler.py +2325 -0
- jarvis/jarvis_code_agent/build_validation_config.py +133 -0
- jarvis/jarvis_code_agent/code_agent.py +1171 -94
- jarvis/jarvis_code_agent/code_analyzer/__init__.py +62 -0
- jarvis/jarvis_code_agent/code_analyzer/base_language.py +74 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/__init__.py +44 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/base.py +102 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/cmake.py +59 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/detector.py +125 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/fallback.py +69 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/go.py +38 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/java_gradle.py +44 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/java_maven.py +38 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/makefile.py +50 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/nodejs.py +93 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/python.py +129 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/rust.py +54 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/validator.py +154 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator.py +43 -0
- jarvis/jarvis_code_agent/code_analyzer/context_manager.py +363 -0
- jarvis/jarvis_code_agent/code_analyzer/context_recommender.py +18 -0
- jarvis/jarvis_code_agent/code_analyzer/dependency_analyzer.py +132 -0
- jarvis/jarvis_code_agent/code_analyzer/file_ignore.py +330 -0
- jarvis/jarvis_code_agent/code_analyzer/impact_analyzer.py +781 -0
- jarvis/jarvis_code_agent/code_analyzer/language_registry.py +185 -0
- jarvis/jarvis_code_agent/code_analyzer/language_support.py +89 -0
- jarvis/jarvis_code_agent/code_analyzer/languages/__init__.py +31 -0
- jarvis/jarvis_code_agent/code_analyzer/languages/c_cpp_language.py +231 -0
- jarvis/jarvis_code_agent/code_analyzer/languages/go_language.py +183 -0
- jarvis/jarvis_code_agent/code_analyzer/languages/python_language.py +219 -0
- jarvis/jarvis_code_agent/code_analyzer/languages/rust_language.py +209 -0
- jarvis/jarvis_code_agent/code_analyzer/llm_context_recommender.py +451 -0
- jarvis/jarvis_code_agent/code_analyzer/symbol_extractor.py +77 -0
- jarvis/jarvis_code_agent/code_analyzer/tree_sitter_extractor.py +48 -0
- jarvis/jarvis_code_agent/lint.py +270 -8
- jarvis/jarvis_code_agent/utils.py +142 -0
- jarvis/jarvis_code_analysis/code_review.py +483 -569
- jarvis/jarvis_data/config_schema.json +97 -8
- jarvis/jarvis_git_utils/git_commiter.py +38 -26
- jarvis/jarvis_mcp/sse_mcp_client.py +2 -2
- jarvis/jarvis_mcp/stdio_mcp_client.py +1 -1
- jarvis/jarvis_memory_organizer/memory_organizer.py +1 -1
- jarvis/jarvis_multi_agent/__init__.py +239 -25
- jarvis/jarvis_multi_agent/main.py +37 -1
- jarvis/jarvis_platform/base.py +103 -51
- jarvis/jarvis_platform/openai.py +26 -1
- jarvis/jarvis_platform/yuanbao.py +1 -1
- jarvis/jarvis_platform_manager/service.py +2 -2
- jarvis/jarvis_rag/cli.py +4 -4
- jarvis/jarvis_sec/__init__.py +3605 -0
- jarvis/jarvis_sec/checkers/__init__.py +32 -0
- jarvis/jarvis_sec/checkers/c_checker.py +2680 -0
- jarvis/jarvis_sec/checkers/rust_checker.py +1108 -0
- jarvis/jarvis_sec/cli.py +116 -0
- jarvis/jarvis_sec/report.py +257 -0
- jarvis/jarvis_sec/status.py +264 -0
- jarvis/jarvis_sec/types.py +20 -0
- jarvis/jarvis_sec/workflow.py +219 -0
- jarvis/jarvis_stats/cli.py +1 -1
- jarvis/jarvis_stats/stats.py +1 -1
- jarvis/jarvis_stats/visualizer.py +1 -1
- jarvis/jarvis_tools/cli/main.py +1 -0
- jarvis/jarvis_tools/execute_script.py +46 -9
- jarvis/jarvis_tools/generate_new_tool.py +3 -1
- jarvis/jarvis_tools/read_code.py +275 -12
- jarvis/jarvis_tools/read_symbols.py +141 -0
- jarvis/jarvis_tools/read_webpage.py +5 -3
- jarvis/jarvis_tools/registry.py +73 -35
- jarvis/jarvis_tools/search_web.py +15 -11
- jarvis/jarvis_tools/sub_agent.py +24 -42
- jarvis/jarvis_tools/sub_code_agent.py +14 -13
- jarvis/jarvis_tools/virtual_tty.py +1 -1
- jarvis/jarvis_utils/config.py +187 -35
- jarvis/jarvis_utils/embedding.py +3 -0
- jarvis/jarvis_utils/git_utils.py +181 -6
- jarvis/jarvis_utils/globals.py +3 -3
- jarvis/jarvis_utils/http.py +1 -1
- jarvis/jarvis_utils/input.py +78 -2
- jarvis/jarvis_utils/methodology.py +25 -19
- jarvis/jarvis_utils/utils.py +644 -359
- {jarvis_ai_assistant-0.3.30.dist-info → jarvis_ai_assistant-0.7.0.dist-info}/METADATA +85 -1
- jarvis_ai_assistant-0.7.0.dist-info/RECORD +192 -0
- {jarvis_ai_assistant-0.3.30.dist-info → jarvis_ai_assistant-0.7.0.dist-info}/entry_points.txt +4 -0
- jarvis/jarvis_agent/config.py +0 -92
- jarvis/jarvis_tools/edit_file.py +0 -179
- jarvis/jarvis_tools/rewrite_file.py +0 -191
- jarvis_ai_assistant-0.3.30.dist-info/RECORD +0 -137
- {jarvis_ai_assistant-0.3.30.dist-info → jarvis_ai_assistant-0.7.0.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.3.30.dist-info → jarvis_ai_assistant-0.7.0.dist-info}/licenses/LICENSE +0 -0
- {jarvis_ai_assistant-0.3.30.dist-info → jarvis_ai_assistant-0.7.0.dist-info}/top_level.txt +0 -0
jarvis/jarvis_utils/utils.py
CHANGED
|
@@ -6,6 +6,8 @@ import signal
|
|
|
6
6
|
import subprocess
|
|
7
7
|
import sys
|
|
8
8
|
import time
|
|
9
|
+
import atexit
|
|
10
|
+
import errno
|
|
9
11
|
from pathlib import Path
|
|
10
12
|
from typing import Any, Callable, Dict, List, Optional, Tuple
|
|
11
13
|
from datetime import datetime, date
|
|
@@ -59,6 +61,10 @@ COMMAND_MAPPING = {
|
|
|
59
61
|
"jst": "jarvis-stats",
|
|
60
62
|
# 记忆整理
|
|
61
63
|
"jmo": "jarvis-memory-organizer",
|
|
64
|
+
# 安全分析
|
|
65
|
+
"jsec": "jarvis-sec",
|
|
66
|
+
# C2Rust迁移
|
|
67
|
+
"jc2r": "jarvis-c2rust",
|
|
62
68
|
}
|
|
63
69
|
|
|
64
70
|
# RAG 依赖检测工具函数(更精确)
|
|
@@ -205,6 +211,130 @@ def _setup_signal_handler() -> None:
|
|
|
205
211
|
signal.signal(signal.SIGINT, sigint_handler)
|
|
206
212
|
|
|
207
213
|
|
|
214
|
+
# ----------------------------
|
|
215
|
+
# 单实例文件锁(放置于初始化早期使用)
|
|
216
|
+
# ----------------------------
|
|
217
|
+
_INSTANCE_LOCK_PATH: Optional[Path] = None
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
def _get_instance_lock_path(lock_name: str = "instance.lock") -> Path:
|
|
221
|
+
try:
|
|
222
|
+
data_dir = Path(str(get_data_dir()))
|
|
223
|
+
except Exception:
|
|
224
|
+
data_dir = Path(os.path.expanduser("~/.jarvis"))
|
|
225
|
+
data_dir.mkdir(parents=True, exist_ok=True)
|
|
226
|
+
return data_dir / lock_name
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
def _read_lock_owner_pid(lock_path: Path) -> Optional[int]:
|
|
230
|
+
try:
|
|
231
|
+
txt = lock_path.read_text(encoding="utf-8", errors="ignore").strip()
|
|
232
|
+
if not txt:
|
|
233
|
+
return None
|
|
234
|
+
try:
|
|
235
|
+
info = json.loads(txt)
|
|
236
|
+
pid = info.get("pid")
|
|
237
|
+
return int(pid) if pid is not None else None
|
|
238
|
+
except Exception:
|
|
239
|
+
# 兼容纯数字PID
|
|
240
|
+
return int(txt)
|
|
241
|
+
except Exception:
|
|
242
|
+
return None
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
def _is_process_alive(pid: int) -> bool:
|
|
246
|
+
if pid is None or pid <= 0:
|
|
247
|
+
return False
|
|
248
|
+
try:
|
|
249
|
+
os.kill(pid, 0)
|
|
250
|
+
except ProcessLookupError:
|
|
251
|
+
return False
|
|
252
|
+
except PermissionError:
|
|
253
|
+
# 无权限但进程存在
|
|
254
|
+
return True
|
|
255
|
+
except OSError as e:
|
|
256
|
+
# 某些平台上,EPERM 表示进程存在但无权限
|
|
257
|
+
if getattr(e, "errno", None) == errno.EPERM:
|
|
258
|
+
return True
|
|
259
|
+
return False
|
|
260
|
+
else:
|
|
261
|
+
return True
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
def _release_instance_lock() -> None:
|
|
265
|
+
global _INSTANCE_LOCK_PATH
|
|
266
|
+
try:
|
|
267
|
+
if _INSTANCE_LOCK_PATH and _INSTANCE_LOCK_PATH.exists():
|
|
268
|
+
_INSTANCE_LOCK_PATH.unlink()
|
|
269
|
+
except Exception:
|
|
270
|
+
# 清理失败不影响退出
|
|
271
|
+
pass
|
|
272
|
+
_INSTANCE_LOCK_PATH = None
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
def _acquire_single_instance_lock(lock_name: str = "instance.lock") -> None:
|
|
276
|
+
"""
|
|
277
|
+
在数据目录(~/.jarvis 或配置的数据目录)下创建实例锁,防止重复启动。
|
|
278
|
+
如果检测到已有存活实例,提示后退出。
|
|
279
|
+
"""
|
|
280
|
+
global _INSTANCE_LOCK_PATH
|
|
281
|
+
lock_path = _get_instance_lock_path(lock_name)
|
|
282
|
+
|
|
283
|
+
# 已存在锁:检查是否为有效存活实例
|
|
284
|
+
if lock_path.exists():
|
|
285
|
+
pid = _read_lock_owner_pid(lock_path)
|
|
286
|
+
if pid and _is_process_alive(pid):
|
|
287
|
+
PrettyOutput.print(
|
|
288
|
+
f"检测到已有一个 Jarvis 实例正在运行 (PID: {pid})。\n"
|
|
289
|
+
f"如果确认不存在正在运行的实例,请删除锁文件后重试:{lock_path}",
|
|
290
|
+
OutputType.WARNING,
|
|
291
|
+
)
|
|
292
|
+
sys.exit(0)
|
|
293
|
+
# 尝试移除陈旧锁
|
|
294
|
+
try:
|
|
295
|
+
lock_path.unlink()
|
|
296
|
+
except Exception:
|
|
297
|
+
PrettyOutput.print(
|
|
298
|
+
f"无法删除旧锁文件:{lock_path},请手动清理后重试。",
|
|
299
|
+
OutputType.ERROR,
|
|
300
|
+
)
|
|
301
|
+
sys.exit(1)
|
|
302
|
+
|
|
303
|
+
# 原子创建锁文件,避免并发竞争
|
|
304
|
+
flags = os.O_CREAT | os.O_EXCL | os.O_WRONLY
|
|
305
|
+
try:
|
|
306
|
+
fd = os.open(str(lock_path), flags)
|
|
307
|
+
with os.fdopen(fd, "w", encoding="utf-8") as fp:
|
|
308
|
+
payload = {
|
|
309
|
+
"pid": os.getpid(),
|
|
310
|
+
"time": int(time.time()),
|
|
311
|
+
"argv": sys.argv[:10],
|
|
312
|
+
}
|
|
313
|
+
try:
|
|
314
|
+
fp.write(json.dumps(payload, ensure_ascii=False))
|
|
315
|
+
except Exception:
|
|
316
|
+
fp.write(str(os.getpid()))
|
|
317
|
+
_INSTANCE_LOCK_PATH = lock_path
|
|
318
|
+
atexit.register(_release_instance_lock)
|
|
319
|
+
except FileExistsError:
|
|
320
|
+
# 极端并发下再次校验
|
|
321
|
+
pid = _read_lock_owner_pid(lock_path)
|
|
322
|
+
if pid and _is_process_alive(pid):
|
|
323
|
+
PrettyOutput.print(
|
|
324
|
+
f"检测到已有一个 Jarvis 实例正在运行 (PID: {pid})。",
|
|
325
|
+
OutputType.WARNING,
|
|
326
|
+
)
|
|
327
|
+
sys.exit(0)
|
|
328
|
+
PrettyOutput.print(
|
|
329
|
+
f"锁文件已存在但可能为陈旧状态:{lock_path},请手动删除后重试。",
|
|
330
|
+
OutputType.ERROR,
|
|
331
|
+
)
|
|
332
|
+
sys.exit(1)
|
|
333
|
+
except Exception as e:
|
|
334
|
+
PrettyOutput.print(f"创建实例锁失败: {e}", OutputType.ERROR)
|
|
335
|
+
sys.exit(1)
|
|
336
|
+
|
|
337
|
+
|
|
208
338
|
def _check_pip_updates() -> bool:
|
|
209
339
|
"""检查pip安装的Jarvis是否有更新
|
|
210
340
|
|
|
@@ -248,7 +378,7 @@ def _check_pip_updates() -> bool:
|
|
|
248
378
|
)
|
|
249
379
|
|
|
250
380
|
# 检测是否在虚拟环境中
|
|
251
|
-
|
|
381
|
+
hasattr(sys, "real_prefix") or (
|
|
252
382
|
hasattr(sys, "base_prefix") and sys.base_prefix != sys.prefix
|
|
253
383
|
)
|
|
254
384
|
|
|
@@ -338,14 +468,22 @@ def _check_jarvis_updates() -> bool:
|
|
|
338
468
|
返回:
|
|
339
469
|
bool: 是否需要重启进程
|
|
340
470
|
"""
|
|
341
|
-
|
|
471
|
+
# 从当前文件目录向上查找包含 .git 的仓库根目录,修复原先只检查 src/jarvis 的问题
|
|
472
|
+
try:
|
|
473
|
+
script_path = Path(__file__).resolve()
|
|
474
|
+
repo_root: Optional[Path] = None
|
|
475
|
+
for d in [script_path.parent] + list(script_path.parents):
|
|
476
|
+
if (d / ".git").exists():
|
|
477
|
+
repo_root = d
|
|
478
|
+
break
|
|
479
|
+
except Exception:
|
|
480
|
+
repo_root = None
|
|
342
481
|
|
|
343
|
-
# 先检查是否是git
|
|
344
|
-
|
|
345
|
-
if git_dir.exists():
|
|
482
|
+
# 先检查是否是git源码安装(找到仓库根目录即认为是源码安装)
|
|
483
|
+
if repo_root and (repo_root / ".git").exists():
|
|
346
484
|
from jarvis.jarvis_utils.git_utils import check_and_update_git_repo
|
|
347
485
|
|
|
348
|
-
return check_and_update_git_repo(str(
|
|
486
|
+
return check_and_update_git_repo(str(repo_root))
|
|
349
487
|
|
|
350
488
|
# 检查是否是pip/uv pip安装的版本
|
|
351
489
|
return _check_pip_updates()
|
|
@@ -679,7 +817,7 @@ def _show_usage_stats(welcome_str: str) -> None:
|
|
|
679
817
|
|
|
680
818
|
# 愿景 Panel
|
|
681
819
|
vision_text = Text(
|
|
682
|
-
"
|
|
820
|
+
"让开发者与AI成为共生伙伴",
|
|
683
821
|
justify="center",
|
|
684
822
|
style="italic",
|
|
685
823
|
)
|
|
@@ -694,7 +832,7 @@ def _show_usage_stats(welcome_str: str) -> None:
|
|
|
694
832
|
|
|
695
833
|
# 使命 Panel
|
|
696
834
|
mission_text = Text(
|
|
697
|
-
"
|
|
835
|
+
"让灵感高效落地为代码与行动",
|
|
698
836
|
justify="center",
|
|
699
837
|
style="italic",
|
|
700
838
|
)
|
|
@@ -1016,359 +1154,465 @@ def _process_env_variables(config_data: dict) -> None:
|
|
|
1016
1154
|
)
|
|
1017
1155
|
|
|
1018
1156
|
|
|
1019
|
-
def
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
bool
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1157
|
+
def _ask_config_bool(config_data: dict, ask_all: bool, _key: str, _tip: str, _default: bool) -> bool:
|
|
1158
|
+
"""询问并设置布尔类型配置项"""
|
|
1159
|
+
try:
|
|
1160
|
+
if not ask_all and _key in config_data:
|
|
1161
|
+
return False
|
|
1162
|
+
from jarvis.jarvis_utils.input import user_confirm as get_yes_no
|
|
1163
|
+
cur = bool(config_data.get(_key, _default))
|
|
1164
|
+
val = get_yes_no(_tip, default=cur)
|
|
1165
|
+
if bool(val) == cur:
|
|
1166
|
+
return False
|
|
1167
|
+
config_data[_key] = bool(val)
|
|
1168
|
+
return True
|
|
1169
|
+
except Exception:
|
|
1170
|
+
return False
|
|
1033
1171
|
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
cur = bool(config_data.get(_key, _default))
|
|
1040
|
-
val = get_yes_no(_tip, default=cur)
|
|
1041
|
-
# 与当前值相同则不写入,避免冗余
|
|
1042
|
-
if bool(val) == cur:
|
|
1043
|
-
return False
|
|
1044
|
-
config_data[_key] = bool(val)
|
|
1045
|
-
else:
|
|
1046
|
-
cur = str(config_data.get(_key, _default or ""))
|
|
1047
|
-
val = get_single_line_input(f"{_tip}", default=cur)
|
|
1048
|
-
v = ("" if val is None else str(val)).strip()
|
|
1049
|
-
# 输入与当前值相同则不写入
|
|
1050
|
-
if v == cur:
|
|
1051
|
-
return False
|
|
1052
|
-
config_data[_key] = v
|
|
1053
|
-
return True
|
|
1054
|
-
except Exception:
|
|
1055
|
-
# 异常时不写入,保持精简
|
|
1172
|
+
|
|
1173
|
+
def _ask_config_str(config_data: dict, ask_all: bool, _key: str, _tip: str, _default: str = "") -> bool:
|
|
1174
|
+
"""询问并设置字符串类型配置项"""
|
|
1175
|
+
try:
|
|
1176
|
+
if not ask_all and _key in config_data:
|
|
1056
1177
|
return False
|
|
1178
|
+
from jarvis.jarvis_utils.input import get_single_line_input
|
|
1179
|
+
cur = str(config_data.get(_key, _default or ""))
|
|
1180
|
+
val = get_single_line_input(f"{_tip}", default=cur)
|
|
1181
|
+
v = ("" if val is None else str(val)).strip()
|
|
1182
|
+
if v == cur:
|
|
1183
|
+
return False
|
|
1184
|
+
config_data[_key] = v
|
|
1185
|
+
return True
|
|
1186
|
+
except Exception:
|
|
1187
|
+
return False
|
|
1057
1188
|
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
if s == "":
|
|
1069
|
-
return False
|
|
1070
|
-
if s == cur:
|
|
1071
|
-
return False
|
|
1072
|
-
config_data[_key] = s
|
|
1073
|
-
return True
|
|
1074
|
-
except Exception:
|
|
1189
|
+
|
|
1190
|
+
def _ask_config_optional_str(config_data: dict, ask_all: bool, _key: str, _tip: str, _default: str = "") -> bool:
|
|
1191
|
+
"""询问并设置可选字符串类型配置项(空输入表示不改变)"""
|
|
1192
|
+
try:
|
|
1193
|
+
if not ask_all and _key in config_data:
|
|
1194
|
+
return False
|
|
1195
|
+
from jarvis.jarvis_utils.input import get_single_line_input
|
|
1196
|
+
cur = str(config_data.get(_key, _default or ""))
|
|
1197
|
+
val = get_single_line_input(f"{_tip}", default=cur)
|
|
1198
|
+
if val is None:
|
|
1075
1199
|
return False
|
|
1200
|
+
s = str(val).strip()
|
|
1201
|
+
if s == "" or s == cur:
|
|
1202
|
+
return False
|
|
1203
|
+
config_data[_key] = s
|
|
1204
|
+
return True
|
|
1205
|
+
except Exception:
|
|
1206
|
+
return False
|
|
1207
|
+
|
|
1076
1208
|
|
|
1077
|
-
|
|
1209
|
+
def _ask_config_int(config_data: dict, ask_all: bool, _key: str, _tip: str, _default: int) -> bool:
|
|
1210
|
+
"""询问并设置整数类型配置项"""
|
|
1211
|
+
try:
|
|
1212
|
+
if not ask_all and _key in config_data:
|
|
1213
|
+
return False
|
|
1214
|
+
from jarvis.jarvis_utils.input import get_single_line_input
|
|
1215
|
+
cur = str(config_data.get(_key, _default))
|
|
1216
|
+
val_str = get_single_line_input(f"{_tip}", default=cur)
|
|
1217
|
+
s = "" if val_str is None else str(val_str).strip()
|
|
1218
|
+
if s == "" or s == cur:
|
|
1219
|
+
return False
|
|
1078
1220
|
try:
|
|
1079
|
-
|
|
1080
|
-
return False
|
|
1081
|
-
cur = str(config_data.get(_key, _default))
|
|
1082
|
-
val_str = get_single_line_input(f"{_tip}", default=cur)
|
|
1083
|
-
s = "" if val_str is None else str(val_str).strip()
|
|
1084
|
-
if s == "" or s == cur:
|
|
1085
|
-
return False
|
|
1086
|
-
try:
|
|
1087
|
-
v = int(s)
|
|
1088
|
-
except Exception:
|
|
1089
|
-
return False
|
|
1090
|
-
if str(v) == cur:
|
|
1091
|
-
return False
|
|
1092
|
-
config_data[_key] = v
|
|
1093
|
-
return True
|
|
1221
|
+
v = int(s)
|
|
1094
1222
|
except Exception:
|
|
1095
1223
|
return False
|
|
1224
|
+
if str(v) == cur:
|
|
1225
|
+
return False
|
|
1226
|
+
config_data[_key] = v
|
|
1227
|
+
return True
|
|
1228
|
+
except Exception:
|
|
1229
|
+
return False
|
|
1096
1230
|
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
return True
|
|
1120
|
-
except Exception:
|
|
1231
|
+
|
|
1232
|
+
def _ask_config_list(config_data: dict, ask_all: bool, _key: str, _tip: str) -> bool:
|
|
1233
|
+
"""询问并设置列表类型配置项(逗号分隔)"""
|
|
1234
|
+
try:
|
|
1235
|
+
if not ask_all and _key in config_data:
|
|
1236
|
+
return False
|
|
1237
|
+
from jarvis.jarvis_utils.input import get_single_line_input
|
|
1238
|
+
cur_val = config_data.get(_key, [])
|
|
1239
|
+
if isinstance(cur_val, list):
|
|
1240
|
+
cur_display = ", ".join([str(x) for x in cur_val])
|
|
1241
|
+
else:
|
|
1242
|
+
cur_display = str(cur_val or "")
|
|
1243
|
+
val = get_single_line_input(f"{_tip}", default=cur_display)
|
|
1244
|
+
if val is None:
|
|
1245
|
+
return False
|
|
1246
|
+
s = str(val).strip()
|
|
1247
|
+
if s == cur_display.strip():
|
|
1248
|
+
return False
|
|
1249
|
+
if not s:
|
|
1250
|
+
return False
|
|
1251
|
+
items = [x.strip() for x in s.split(",") if x.strip()]
|
|
1252
|
+
if isinstance(cur_val, list) and items == cur_val:
|
|
1121
1253
|
return False
|
|
1254
|
+
config_data[_key] = items
|
|
1255
|
+
return True
|
|
1256
|
+
except Exception:
|
|
1257
|
+
return False
|
|
1122
1258
|
|
|
1259
|
+
|
|
1260
|
+
def _collect_basic_switches(config_data: dict, ask_all: bool) -> bool:
|
|
1261
|
+
"""收集基础开关配置"""
|
|
1123
1262
|
changed = False
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
"在进入默认通用代理前,是否先列出可用配置(agent/multi_agent/roles)供选择?",
|
|
1138
|
-
False,
|
|
1139
|
-
"bool",
|
|
1140
|
-
)
|
|
1141
|
-
or changed
|
|
1142
|
-
)
|
|
1263
|
+
changed = _ask_config_bool(
|
|
1264
|
+
config_data, ask_all,
|
|
1265
|
+
"JARVIS_ENABLE_GIT_JCA_SWITCH",
|
|
1266
|
+
"是否在检测到Git仓库时,提示并可自动切换到代码开发模式(jca)?",
|
|
1267
|
+
False,
|
|
1268
|
+
) or changed
|
|
1269
|
+
changed = _ask_config_bool(
|
|
1270
|
+
config_data, ask_all,
|
|
1271
|
+
"JARVIS_ENABLE_STARTUP_CONFIG_SELECTOR",
|
|
1272
|
+
"在进入默认通用代理前,是否先列出可用配置(agent/multi_agent/roles)供选择?",
|
|
1273
|
+
False,
|
|
1274
|
+
) or changed
|
|
1275
|
+
return changed
|
|
1143
1276
|
|
|
1144
|
-
# 新增的配置项交互(通用体验相关)
|
|
1145
|
-
changed = (
|
|
1146
|
-
_ask_and_set(
|
|
1147
|
-
"JARVIS_PRETTY_OUTPUT",
|
|
1148
|
-
"是否启用更美观的终端输出(Pretty Output)?",
|
|
1149
|
-
False,
|
|
1150
|
-
"bool",
|
|
1151
|
-
)
|
|
1152
|
-
or changed
|
|
1153
|
-
)
|
|
1154
|
-
changed = (
|
|
1155
|
-
_ask_and_set(
|
|
1156
|
-
"JARVIS_PRINT_PROMPT",
|
|
1157
|
-
"是否打印发送给模型的提示词(Prompt)?",
|
|
1158
|
-
False,
|
|
1159
|
-
"bool",
|
|
1160
|
-
)
|
|
1161
|
-
or changed
|
|
1162
|
-
)
|
|
1163
|
-
changed = (
|
|
1164
|
-
_ask_and_set(
|
|
1165
|
-
"JARVIS_IMMEDIATE_ABORT",
|
|
1166
|
-
"是否启用立即中断?\n- 选择 是/true:在对话输出流的每次迭代中检测到用户中断(例如 Ctrl+C)时,立即返回当前已生成的内容并停止继续输出。\n- 选择 否/false:不会在输出过程中立刻返回,而是按既有流程处理(不中途打断输出)。",
|
|
1167
|
-
False,
|
|
1168
|
-
"bool",
|
|
1169
|
-
)
|
|
1170
|
-
or changed
|
|
1171
|
-
)
|
|
1172
|
-
changed = (
|
|
1173
|
-
_ask_and_set(
|
|
1174
|
-
"JARVIS_ENABLE_STATIC_ANALYSIS",
|
|
1175
|
-
"是否启用静态代码分析(Static Analysis)?",
|
|
1176
|
-
True,
|
|
1177
|
-
"bool",
|
|
1178
|
-
)
|
|
1179
|
-
or changed
|
|
1180
|
-
)
|
|
1181
|
-
changed = (
|
|
1182
|
-
_ask_and_set(
|
|
1183
|
-
"JARVIS_USE_METHODOLOGY",
|
|
1184
|
-
"是否启用方法论系统(Methodology)?",
|
|
1185
|
-
True,
|
|
1186
|
-
"bool",
|
|
1187
|
-
)
|
|
1188
|
-
or changed
|
|
1189
|
-
)
|
|
1190
|
-
changed = (
|
|
1191
|
-
_ask_and_set(
|
|
1192
|
-
"JARVIS_USE_ANALYSIS",
|
|
1193
|
-
"是否启用分析流程(Analysis)?",
|
|
1194
|
-
True,
|
|
1195
|
-
"bool",
|
|
1196
|
-
)
|
|
1197
|
-
or changed
|
|
1198
|
-
)
|
|
1199
|
-
changed = (
|
|
1200
|
-
_ask_and_set(
|
|
1201
|
-
"JARVIS_FORCE_SAVE_MEMORY",
|
|
1202
|
-
"是否强制保存会话记忆?",
|
|
1203
|
-
True,
|
|
1204
|
-
"bool",
|
|
1205
|
-
)
|
|
1206
|
-
or changed
|
|
1207
|
-
)
|
|
1208
1277
|
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1278
|
+
def _collect_ui_experience_config(config_data: dict, ask_all: bool) -> bool:
|
|
1279
|
+
"""收集UI体验相关配置"""
|
|
1280
|
+
changed = False
|
|
1281
|
+
try:
|
|
1282
|
+
import platform as _platform_mod
|
|
1283
|
+
_default_pretty = False if _platform_mod.system() == "Windows" else True
|
|
1284
|
+
except Exception:
|
|
1285
|
+
_default_pretty = True
|
|
1286
|
+
|
|
1287
|
+
changed = _ask_config_bool(
|
|
1288
|
+
config_data, ask_all,
|
|
1289
|
+
"JARVIS_PRETTY_OUTPUT",
|
|
1290
|
+
"是否启用更美观的终端输出(Pretty Output)?",
|
|
1291
|
+
_default_pretty,
|
|
1292
|
+
) or changed
|
|
1293
|
+
changed = _ask_config_bool(
|
|
1294
|
+
config_data, ask_all,
|
|
1295
|
+
"JARVIS_PRINT_PROMPT",
|
|
1296
|
+
"是否打印发送给模型的提示词(Prompt)?",
|
|
1297
|
+
False,
|
|
1298
|
+
) or changed
|
|
1299
|
+
changed = _ask_config_bool(
|
|
1300
|
+
config_data, ask_all,
|
|
1301
|
+
"JARVIS_IMMEDIATE_ABORT",
|
|
1302
|
+
"是否启用立即中断?\n- 选择 是/true:在对话输出流的每次迭代中检测到用户中断(例如 Ctrl+C)时,立即返回当前已生成的内容并停止继续输出。\n- 选择 否/false:不会在输出过程中立刻返回,而是按既有流程处理(不中途打断输出)。",
|
|
1303
|
+
False,
|
|
1304
|
+
) or changed
|
|
1305
|
+
return changed
|
|
1228
1306
|
|
|
1229
|
-
# 数据目录与最大输入Token
|
|
1230
|
-
from jarvis.jarvis_utils.config import get_data_dir as _get_data_dir # lazy import
|
|
1231
1307
|
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
)
|
|
1247
|
-
changed = (
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1308
|
+
def _collect_analysis_config(config_data: dict, ask_all: bool) -> bool:
|
|
1309
|
+
"""收集代码分析相关配置"""
|
|
1310
|
+
changed = False
|
|
1311
|
+
changed = _ask_config_bool(
|
|
1312
|
+
config_data, ask_all,
|
|
1313
|
+
"JARVIS_ENABLE_STATIC_ANALYSIS",
|
|
1314
|
+
"是否启用静态代码分析(Static Analysis)?",
|
|
1315
|
+
True,
|
|
1316
|
+
) or changed
|
|
1317
|
+
changed = _ask_config_bool(
|
|
1318
|
+
config_data, ask_all,
|
|
1319
|
+
"JARVIS_ENABLE_BUILD_VALIDATION",
|
|
1320
|
+
"是否启用构建验证(Build Validation)?在代码编辑后自动验证代码能否成功编译/构建。",
|
|
1321
|
+
True,
|
|
1322
|
+
) or changed
|
|
1323
|
+
changed = _ask_config_int(
|
|
1324
|
+
config_data, ask_all,
|
|
1325
|
+
"JARVIS_BUILD_VALIDATION_TIMEOUT",
|
|
1326
|
+
"构建验证的超时时间(秒,默认30秒)",
|
|
1327
|
+
30,
|
|
1328
|
+
) or changed
|
|
1329
|
+
changed = _ask_config_bool(
|
|
1330
|
+
config_data, ask_all,
|
|
1331
|
+
"JARVIS_ENABLE_IMPACT_ANALYSIS",
|
|
1332
|
+
"是否启用编辑影响范围分析(Impact Analysis)?分析代码编辑的影响范围,识别可能受影响的文件、函数、测试等。",
|
|
1333
|
+
True,
|
|
1334
|
+
) or changed
|
|
1335
|
+
return changed
|
|
1255
1336
|
|
|
1256
|
-
# 目录类配置(逗号分隔)
|
|
1257
|
-
changed = (
|
|
1258
|
-
_ask_and_set_list(
|
|
1259
|
-
"JARVIS_TOOL_LOAD_DIRS",
|
|
1260
|
-
"指定工具加载目录(逗号分隔,留空跳过):",
|
|
1261
|
-
)
|
|
1262
|
-
or changed
|
|
1263
|
-
)
|
|
1264
|
-
changed = (
|
|
1265
|
-
_ask_and_set_list(
|
|
1266
|
-
"JARVIS_METHODOLOGY_DIRS",
|
|
1267
|
-
"指定方法论加载目录(逗号分隔,留空跳过):",
|
|
1268
|
-
)
|
|
1269
|
-
or changed
|
|
1270
|
-
)
|
|
1271
|
-
changed = (
|
|
1272
|
-
_ask_and_set_list(
|
|
1273
|
-
"JARVIS_AGENT_DEFINITION_DIRS",
|
|
1274
|
-
"指定 agent 定义加载目录(逗号分隔,留空跳过):",
|
|
1275
|
-
)
|
|
1276
|
-
or changed
|
|
1277
|
-
)
|
|
1278
|
-
changed = (
|
|
1279
|
-
_ask_and_set_list(
|
|
1280
|
-
"JARVIS_MULTI_AGENT_DIRS",
|
|
1281
|
-
"指定 multi_agent 加载目录(逗号分隔,留空跳过):",
|
|
1282
|
-
)
|
|
1283
|
-
or changed
|
|
1284
|
-
)
|
|
1285
|
-
changed = (
|
|
1286
|
-
_ask_and_set_list(
|
|
1287
|
-
"JARVIS_ROLES_DIRS",
|
|
1288
|
-
"指定 roles 加载目录(逗号分隔,留空跳过):",
|
|
1289
|
-
)
|
|
1290
|
-
or changed
|
|
1291
|
-
)
|
|
1292
1337
|
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
)
|
|
1338
|
+
def _collect_agent_features_config(config_data: dict, ask_all: bool) -> bool:
|
|
1339
|
+
"""收集Agent功能相关配置"""
|
|
1340
|
+
changed = False
|
|
1341
|
+
changed = _ask_config_bool(
|
|
1342
|
+
config_data, ask_all,
|
|
1343
|
+
"JARVIS_USE_METHODOLOGY",
|
|
1344
|
+
"是否启用方法论系统(Methodology)?",
|
|
1345
|
+
True,
|
|
1346
|
+
) or changed
|
|
1347
|
+
changed = _ask_config_bool(
|
|
1348
|
+
config_data, ask_all,
|
|
1349
|
+
"JARVIS_USE_ANALYSIS",
|
|
1350
|
+
"是否启用分析流程(Analysis)?",
|
|
1351
|
+
True,
|
|
1352
|
+
) or changed
|
|
1353
|
+
changed = _ask_config_bool(
|
|
1354
|
+
config_data, ask_all,
|
|
1355
|
+
"JARVIS_FORCE_SAVE_MEMORY",
|
|
1356
|
+
"是否强制保存会话记忆?",
|
|
1357
|
+
False,
|
|
1358
|
+
) or changed
|
|
1359
|
+
return changed
|
|
1308
1360
|
|
|
1309
|
-
# Git 校验模式
|
|
1310
|
-
def _ask_git_check_mode() -> bool:
|
|
1311
|
-
try:
|
|
1312
|
-
_key = "JARVIS_GIT_CHECK_MODE"
|
|
1313
|
-
if not ask_all and _key in config_data:
|
|
1314
|
-
return False
|
|
1315
1361
|
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1362
|
+
def _collect_session_config(config_data: dict, ask_all: bool) -> bool:
|
|
1363
|
+
"""收集会话与调试相关配置"""
|
|
1364
|
+
changed = False
|
|
1365
|
+
changed = _ask_config_bool(
|
|
1366
|
+
config_data, ask_all,
|
|
1367
|
+
"JARVIS_SAVE_SESSION_HISTORY",
|
|
1368
|
+
"是否保存会话记录?",
|
|
1369
|
+
False,
|
|
1370
|
+
) or changed
|
|
1371
|
+
changed = _ask_config_bool(
|
|
1372
|
+
config_data, ask_all,
|
|
1373
|
+
"JARVIS_PRINT_ERROR_TRACEBACK",
|
|
1374
|
+
"是否在错误输出时打印回溯调用链?",
|
|
1375
|
+
False,
|
|
1376
|
+
) or changed
|
|
1377
|
+
changed = _ask_config_bool(
|
|
1378
|
+
config_data, ask_all,
|
|
1379
|
+
"JARVIS_SKIP_PREDEFINED_TASKS",
|
|
1380
|
+
"是否跳过预定义任务加载(不读取 pre-command 列表)?",
|
|
1381
|
+
False,
|
|
1382
|
+
) or changed
|
|
1383
|
+
return changed
|
|
1330
1384
|
|
|
1331
1385
|
|
|
1386
|
+
def _collect_safety_config(config_data: dict, ask_all: bool) -> bool:
|
|
1387
|
+
"""收集代码与工具操作安全提示配置"""
|
|
1388
|
+
changed = False
|
|
1389
|
+
changed = _ask_config_bool(
|
|
1390
|
+
config_data, ask_all,
|
|
1391
|
+
"JARVIS_EXECUTE_TOOL_CONFIRM",
|
|
1392
|
+
"执行工具前是否需要确认?",
|
|
1393
|
+
False,
|
|
1394
|
+
) or changed
|
|
1395
|
+
changed = _ask_config_bool(
|
|
1396
|
+
config_data, ask_all,
|
|
1397
|
+
"JARVIS_CONFIRM_BEFORE_APPLY_PATCH",
|
|
1398
|
+
"应用补丁前是否需要确认?",
|
|
1399
|
+
False,
|
|
1400
|
+
) or changed
|
|
1401
|
+
return changed
|
|
1332
1402
|
|
|
1333
|
-
new_mode = get_choice(
|
|
1334
|
-
tip,
|
|
1335
|
-
choices,
|
|
1336
|
-
)
|
|
1337
1403
|
|
|
1338
|
-
|
|
1339
|
-
|
|
1404
|
+
def _collect_data_and_token_config(config_data: dict, ask_all: bool) -> bool:
|
|
1405
|
+
"""收集数据目录与最大输入Token配置"""
|
|
1406
|
+
changed = False
|
|
1407
|
+
from jarvis.jarvis_utils.config import get_data_dir as _get_data_dir
|
|
1408
|
+
changed = _ask_config_optional_str(
|
|
1409
|
+
config_data, ask_all,
|
|
1410
|
+
"JARVIS_DATA_PATH",
|
|
1411
|
+
f"是否自定义数据目录路径(JARVIS_DATA_PATH)?留空使用默认: {_get_data_dir()}",
|
|
1412
|
+
) or changed
|
|
1413
|
+
changed = _ask_config_int(
|
|
1414
|
+
config_data, ask_all,
|
|
1415
|
+
"JARVIS_MAX_INPUT_TOKEN_COUNT",
|
|
1416
|
+
"自定义最大输入Token数量(留空使用默认: 32000)",
|
|
1417
|
+
32000,
|
|
1418
|
+
) or changed
|
|
1419
|
+
changed = _ask_config_int(
|
|
1420
|
+
config_data, ask_all,
|
|
1421
|
+
"JARVIS_TOOL_FILTER_THRESHOLD",
|
|
1422
|
+
"设置AI工具筛选阈值 (当可用工具数超过此值时触发AI筛选, 默认30)",
|
|
1423
|
+
30,
|
|
1424
|
+
) or changed
|
|
1425
|
+
return changed
|
|
1340
1426
|
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1427
|
+
|
|
1428
|
+
def _collect_planning_config(config_data: dict, ask_all: bool) -> bool:
|
|
1429
|
+
"""收集规划相关配置"""
|
|
1430
|
+
changed = False
|
|
1431
|
+
changed = _ask_config_bool(
|
|
1432
|
+
config_data, ask_all,
|
|
1433
|
+
"JARVIS_PLAN_ENABLED",
|
|
1434
|
+
"是否默认启用任务规划?当 Agent 初始化时 plan 参数未指定,将从此配置加载",
|
|
1435
|
+
True,
|
|
1436
|
+
) or changed
|
|
1437
|
+
changed = _ask_config_int(
|
|
1438
|
+
config_data, ask_all,
|
|
1439
|
+
"JARVIS_PLAN_MAX_DEPTH",
|
|
1440
|
+
"任务规划的最大层数(限制递归拆分深度,默认2;仅在启用规划时生效)",
|
|
1441
|
+
2,
|
|
1442
|
+
) or changed
|
|
1443
|
+
return changed
|
|
1444
|
+
|
|
1445
|
+
|
|
1446
|
+
def _collect_advanced_config(config_data: dict, ask_all: bool) -> bool:
|
|
1447
|
+
"""收集高级配置(自动总结、脚本超时等)"""
|
|
1448
|
+
changed = False
|
|
1449
|
+
changed = _ask_config_int(
|
|
1450
|
+
config_data, ask_all,
|
|
1451
|
+
"JARVIS_AUTO_SUMMARY_ROUNDS",
|
|
1452
|
+
"基于对话轮次的自动总结阈值(达到该轮次后自动总结并清理历史,默认50)",
|
|
1453
|
+
50,
|
|
1454
|
+
) or changed
|
|
1455
|
+
changed = _ask_config_int(
|
|
1456
|
+
config_data, ask_all,
|
|
1457
|
+
"JARVIS_SCRIPT_EXECUTION_TIMEOUT",
|
|
1458
|
+
"脚本执行超时时间(秒,默认300,仅非交互模式生效)",
|
|
1459
|
+
300,
|
|
1460
|
+
) or changed
|
|
1461
|
+
changed = _ask_config_int(
|
|
1462
|
+
config_data, ask_all,
|
|
1463
|
+
"JARVIS_ADDON_PROMPT_THRESHOLD",
|
|
1464
|
+
"附加提示的触发阈值(字符数,默认1024)。当消息长度超过此值时,会自动添加默认的附加提示",
|
|
1465
|
+
1024,
|
|
1466
|
+
) or changed
|
|
1467
|
+
changed = _ask_config_bool(
|
|
1468
|
+
config_data, ask_all,
|
|
1469
|
+
"JARVIS_ENABLE_INTENT_RECOGNITION",
|
|
1470
|
+
"是否启用意图识别功能?用于智能上下文推荐中的LLM意图提取和语义分析",
|
|
1471
|
+
True,
|
|
1472
|
+
) or changed
|
|
1473
|
+
return changed
|
|
1474
|
+
|
|
1475
|
+
|
|
1476
|
+
def _collect_directory_config(config_data: dict, ask_all: bool) -> bool:
|
|
1477
|
+
"""收集目录类配置(逗号分隔)"""
|
|
1478
|
+
changed = False
|
|
1479
|
+
changed = _ask_config_list(
|
|
1480
|
+
config_data, ask_all,
|
|
1481
|
+
"JARVIS_TOOL_LOAD_DIRS",
|
|
1482
|
+
"指定工具加载目录(逗号分隔,留空跳过):",
|
|
1483
|
+
) or changed
|
|
1484
|
+
changed = _ask_config_list(
|
|
1485
|
+
config_data, ask_all,
|
|
1486
|
+
"JARVIS_METHODOLOGY_DIRS",
|
|
1487
|
+
"指定方法论加载目录(逗号分隔,留空跳过):",
|
|
1488
|
+
) or changed
|
|
1489
|
+
changed = _ask_config_list(
|
|
1490
|
+
config_data, ask_all,
|
|
1491
|
+
"JARVIS_AGENT_DEFINITION_DIRS",
|
|
1492
|
+
"指定 agent 定义加载目录(逗号分隔,留空跳过):",
|
|
1493
|
+
) or changed
|
|
1494
|
+
changed = _ask_config_list(
|
|
1495
|
+
config_data, ask_all,
|
|
1496
|
+
"JARVIS_MULTI_AGENT_DIRS",
|
|
1497
|
+
"指定 multi_agent 加载目录(逗号分隔,留空跳过):",
|
|
1498
|
+
) or changed
|
|
1499
|
+
changed = _ask_config_list(
|
|
1500
|
+
config_data, ask_all,
|
|
1501
|
+
"JARVIS_ROLES_DIRS",
|
|
1502
|
+
"指定 roles 加载目录(逗号分隔,留空跳过):",
|
|
1503
|
+
) or changed
|
|
1504
|
+
changed = _ask_config_list(
|
|
1505
|
+
config_data, ask_all,
|
|
1506
|
+
"JARVIS_AFTER_TOOL_CALL_CB_DIRS",
|
|
1507
|
+
"指定工具调用后回调实现目录(逗号分隔,留空跳过):",
|
|
1508
|
+
) or changed
|
|
1509
|
+
return changed
|
|
1510
|
+
|
|
1511
|
+
|
|
1512
|
+
def _collect_web_search_config(config_data: dict, ask_all: bool) -> bool:
|
|
1513
|
+
"""收集Web搜索配置"""
|
|
1514
|
+
changed = False
|
|
1515
|
+
changed = _ask_config_optional_str(
|
|
1516
|
+
config_data, ask_all,
|
|
1517
|
+
"JARVIS_WEB_SEARCH_PLATFORM",
|
|
1518
|
+
"配置 Web 搜索平台名称(留空跳过):",
|
|
1519
|
+
) or changed
|
|
1520
|
+
changed = _ask_config_optional_str(
|
|
1521
|
+
config_data, ask_all,
|
|
1522
|
+
"JARVIS_WEB_SEARCH_MODEL",
|
|
1523
|
+
"配置 Web 搜索模型名称(留空跳过):",
|
|
1524
|
+
) or changed
|
|
1525
|
+
return changed
|
|
1526
|
+
|
|
1527
|
+
|
|
1528
|
+
def _ask_git_check_mode(config_data: dict, ask_all: bool) -> bool:
|
|
1529
|
+
"""询问Git校验模式"""
|
|
1530
|
+
try:
|
|
1531
|
+
_key = "JARVIS_GIT_CHECK_MODE"
|
|
1532
|
+
if not ask_all and _key in config_data:
|
|
1533
|
+
return False
|
|
1534
|
+
from jarvis.jarvis_utils.input import get_choice
|
|
1535
|
+
from jarvis.jarvis_utils.config import get_git_check_mode
|
|
1536
|
+
current_mode = config_data.get(_key, get_git_check_mode())
|
|
1537
|
+
choices = ["strict", "warn"]
|
|
1538
|
+
tip = (
|
|
1539
|
+
"请选择 Git 仓库检查模式 (JARVIS_GIT_CHECK_MODE):\n"
|
|
1540
|
+
"此设置决定了当在 Git 仓库中检测到未提交的更改时,Jarvis应如何处理。\n"
|
|
1541
|
+
"这对于确保代码修改和提交操作在干净的工作区上进行至关重要。\n"
|
|
1542
|
+
" - strict: (推荐) 如果存在未提交的更改,则中断相关操作(如代码修改、自动提交)。\n"
|
|
1543
|
+
" 这可以防止意外覆盖或丢失本地工作。\n"
|
|
1544
|
+
" - warn: 如果存在未提交的更改,仅显示警告信息,然后继续执行操作。\n"
|
|
1545
|
+
" 适用于您希望绕过检查并自行管理仓库状态的场景。"
|
|
1546
|
+
)
|
|
1547
|
+
new_mode = get_choice(tip, choices)
|
|
1548
|
+
if new_mode == current_mode:
|
|
1344
1549
|
return False
|
|
1550
|
+
config_data[_key] = new_mode
|
|
1551
|
+
return True
|
|
1552
|
+
except Exception:
|
|
1553
|
+
return False
|
|
1345
1554
|
|
|
1346
|
-
changed = _ask_git_check_mode() or changed
|
|
1347
1555
|
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1556
|
+
def _ask_patch_format_mode(config_data: dict, ask_all: bool) -> bool:
|
|
1557
|
+
"""询问补丁格式模式"""
|
|
1558
|
+
try:
|
|
1559
|
+
_key = "JARVIS_PATCH_FORMAT"
|
|
1560
|
+
if not ask_all and _key in config_data:
|
|
1561
|
+
return False
|
|
1562
|
+
from jarvis.jarvis_utils.input import get_choice
|
|
1563
|
+
from jarvis.jarvis_utils.config import get_patch_format
|
|
1564
|
+
current_mode = config_data.get(_key, get_patch_format())
|
|
1565
|
+
choices = ["all", "search", "search_range"]
|
|
1566
|
+
tip = (
|
|
1567
|
+
"请选择补丁格式处理模式 (JARVIS_PATCH_FORMAT):\n"
|
|
1568
|
+
"该设置影响 edit_file_handler 在处理补丁时允许的匹配方式。\n"
|
|
1569
|
+
" - all: 同时支持 SEARCH 与 SEARCH_START/SEARCH_END 两种模式(默认)。\n"
|
|
1570
|
+
" - search: 仅允许精确片段匹配(SEARCH)。更稳定,适合较弱模型或严格控制改动。\n"
|
|
1571
|
+
" - search_range: 仅允许范围匹配(SEARCH_START/SEARCH_END)。更灵活,适合较强模型和块内细粒度修改。"
|
|
1353
1572
|
)
|
|
1354
|
-
|
|
1355
|
-
|
|
1573
|
+
new_mode = get_choice(tip, choices)
|
|
1574
|
+
if new_mode == current_mode:
|
|
1575
|
+
return False
|
|
1576
|
+
config_data[_key] = new_mode
|
|
1577
|
+
return True
|
|
1578
|
+
except Exception:
|
|
1579
|
+
return False
|
|
1580
|
+
|
|
1581
|
+
|
|
1582
|
+
def _collect_git_config(config_data: dict, ask_all: bool) -> bool:
|
|
1583
|
+
"""收集Git相关配置"""
|
|
1584
|
+
changed = False
|
|
1585
|
+
changed = _ask_git_check_mode(config_data, ask_all) or changed
|
|
1586
|
+
changed = _ask_patch_format_mode(config_data, ask_all) or changed
|
|
1587
|
+
changed = _ask_config_optional_str(
|
|
1588
|
+
config_data, ask_all,
|
|
1589
|
+
"JARVIS_GIT_COMMIT_PROMPT",
|
|
1590
|
+
"自定义 Git 提交提示模板(留空跳过):",
|
|
1591
|
+
) or changed
|
|
1592
|
+
return changed
|
|
1593
|
+
|
|
1356
1594
|
|
|
1357
|
-
|
|
1595
|
+
def _collect_rag_config(config_data: dict, ask_all: bool) -> bool:
|
|
1596
|
+
"""收集RAG配置"""
|
|
1597
|
+
changed = False
|
|
1358
1598
|
try:
|
|
1359
1599
|
from jarvis.jarvis_utils.config import (
|
|
1360
1600
|
get_rag_embedding_model as _get_rag_embedding_model,
|
|
1361
1601
|
get_rag_rerank_model as _get_rag_rerank_model,
|
|
1362
1602
|
)
|
|
1363
|
-
|
|
1603
|
+
from jarvis.jarvis_utils.input import user_confirm as get_yes_no
|
|
1604
|
+
from jarvis.jarvis_utils.input import get_single_line_input
|
|
1605
|
+
|
|
1364
1606
|
rag_default_embed = _get_rag_embedding_model()
|
|
1365
1607
|
rag_default_rerank = _get_rag_rerank_model()
|
|
1366
1608
|
except Exception:
|
|
1367
1609
|
rag_default_embed = "BAAI/bge-m3"
|
|
1368
1610
|
rag_default_rerank = "BAAI/bge-reranker-v2-m3"
|
|
1369
|
-
|
|
1611
|
+
get_yes_no = None
|
|
1612
|
+
get_single_line_input = None
|
|
1613
|
+
|
|
1370
1614
|
try:
|
|
1371
|
-
if "JARVIS_RAG" not in config_data:
|
|
1615
|
+
if "JARVIS_RAG" not in config_data and get_yes_no:
|
|
1372
1616
|
if get_yes_no("是否配置 RAG 检索增强参数?", default=False):
|
|
1373
1617
|
rag_conf: Dict[str, Any] = {}
|
|
1374
1618
|
emb = get_single_line_input(
|
|
@@ -1395,49 +1639,75 @@ def _collect_optional_config_interactively(
|
|
|
1395
1639
|
changed = True
|
|
1396
1640
|
except Exception:
|
|
1397
1641
|
pass
|
|
1642
|
+
return changed
|
|
1398
1643
|
|
|
1399
|
-
# 中心仓库配置
|
|
1400
|
-
changed = (
|
|
1401
|
-
_ask_and_set(
|
|
1402
|
-
"JARVIS_CENTRAL_METHODOLOGY_REPO",
|
|
1403
|
-
"请输入中心方法论仓库地址(可留空跳过):",
|
|
1404
|
-
"",
|
|
1405
|
-
"str",
|
|
1406
|
-
)
|
|
1407
|
-
or changed
|
|
1408
|
-
)
|
|
1409
|
-
changed = (
|
|
1410
|
-
_ask_and_set(
|
|
1411
|
-
"JARVIS_CENTRAL_TOOL_REPO",
|
|
1412
|
-
"请输入中心工具仓库地址(可留空跳过):",
|
|
1413
|
-
"",
|
|
1414
|
-
"str",
|
|
1415
|
-
)
|
|
1416
|
-
or changed
|
|
1417
|
-
)
|
|
1418
|
-
|
|
1419
|
-
# 已移除 LLM 组配置交互
|
|
1420
1644
|
|
|
1421
|
-
|
|
1645
|
+
def _collect_central_repo_config(config_data: dict, ask_all: bool) -> bool:
|
|
1646
|
+
"""收集中心仓库配置"""
|
|
1647
|
+
changed = False
|
|
1648
|
+
changed = _ask_config_str(
|
|
1649
|
+
config_data, ask_all,
|
|
1650
|
+
"JARVIS_CENTRAL_METHODOLOGY_REPO",
|
|
1651
|
+
"请输入中心方法论仓库路径或Git地址(可留空跳过):",
|
|
1652
|
+
"",
|
|
1653
|
+
) or changed
|
|
1654
|
+
changed = _ask_config_str(
|
|
1655
|
+
config_data, ask_all,
|
|
1656
|
+
"JARVIS_CENTRAL_TOOL_REPO",
|
|
1657
|
+
"请输入中心工具仓库路径或Git地址(可留空跳过):",
|
|
1658
|
+
"",
|
|
1659
|
+
) or changed
|
|
1660
|
+
return changed
|
|
1422
1661
|
|
|
1423
|
-
# 已移除 工具组配置交互
|
|
1424
1662
|
|
|
1425
|
-
|
|
1426
|
-
|
|
1663
|
+
def _collect_shell_config(config_data: dict, ask_all: bool) -> bool:
|
|
1664
|
+
"""收集SHELL覆盖配置"""
|
|
1665
|
+
changed = False
|
|
1427
1666
|
try:
|
|
1667
|
+
import os
|
|
1428
1668
|
default_shell = os.getenv("SHELL", "/bin/bash")
|
|
1429
|
-
changed = (
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
or changed
|
|
1436
|
-
)
|
|
1669
|
+
changed = _ask_config_optional_str(
|
|
1670
|
+
config_data, ask_all,
|
|
1671
|
+
"SHELL",
|
|
1672
|
+
f"覆盖 SHELL 路径(留空使用系统默认: {default_shell}):",
|
|
1673
|
+
default_shell,
|
|
1674
|
+
) or changed
|
|
1437
1675
|
except Exception:
|
|
1438
1676
|
pass
|
|
1677
|
+
return changed
|
|
1678
|
+
|
|
1439
1679
|
|
|
1440
|
-
|
|
1680
|
+
def _collect_optional_config_interactively(
|
|
1681
|
+
config_data: dict, ask_all: bool = False
|
|
1682
|
+
) -> bool:
|
|
1683
|
+
"""
|
|
1684
|
+
复用的交互式配置收集逻辑:
|
|
1685
|
+
- ask_all=False(默认):仅对缺省的新功能开关/可选项逐项询问,已存在项跳过
|
|
1686
|
+
- ask_all=True:对所有项进行询问,默认值取自当前配置文件,可覆盖现有设置
|
|
1687
|
+
- 修改传入的 config_data
|
|
1688
|
+
- 包含更多来自 config.py 的可选项
|
|
1689
|
+
返回:
|
|
1690
|
+
bool: 是否有变更
|
|
1691
|
+
"""
|
|
1692
|
+
changed = False
|
|
1693
|
+
|
|
1694
|
+
# 收集各类配置
|
|
1695
|
+
changed = _collect_basic_switches(config_data, ask_all) or changed
|
|
1696
|
+
changed = _collect_ui_experience_config(config_data, ask_all) or changed
|
|
1697
|
+
changed = _collect_analysis_config(config_data, ask_all) or changed
|
|
1698
|
+
changed = _collect_agent_features_config(config_data, ask_all) or changed
|
|
1699
|
+
changed = _collect_session_config(config_data, ask_all) or changed
|
|
1700
|
+
changed = _collect_safety_config(config_data, ask_all) or changed
|
|
1701
|
+
changed = _collect_data_and_token_config(config_data, ask_all) or changed
|
|
1702
|
+
changed = _collect_planning_config(config_data, ask_all) or changed
|
|
1703
|
+
changed = _collect_advanced_config(config_data, ask_all) or changed
|
|
1704
|
+
changed = _collect_directory_config(config_data, ask_all) or changed
|
|
1705
|
+
changed = _collect_web_search_config(config_data, ask_all) or changed
|
|
1706
|
+
changed = _collect_git_config(config_data, ask_all) or changed
|
|
1707
|
+
changed = _collect_rag_config(config_data, ask_all) or changed
|
|
1708
|
+
changed = _collect_central_repo_config(config_data, ask_all) or changed
|
|
1709
|
+
changed = _collect_shell_config(config_data, ask_all) or changed
|
|
1710
|
+
|
|
1441
1711
|
return changed
|
|
1442
1712
|
|
|
1443
1713
|
|
|
@@ -1680,11 +1950,11 @@ def while_success(func: Callable[[], Any], sleep_time: float = 0.1, max_retries:
|
|
|
1680
1950
|
try:
|
|
1681
1951
|
result = func()
|
|
1682
1952
|
break
|
|
1683
|
-
except Exception:
|
|
1953
|
+
except Exception as e:
|
|
1684
1954
|
retry_count += 1
|
|
1685
1955
|
if retry_count < max_retries:
|
|
1686
1956
|
PrettyOutput.print(
|
|
1687
|
-
f"
|
|
1957
|
+
f"发生异常:\n{e}\n重试中 ({retry_count}/{max_retries}),等待 {sleep_time}s...",
|
|
1688
1958
|
OutputType.WARNING,
|
|
1689
1959
|
)
|
|
1690
1960
|
time.sleep(sleep_time)
|
|
@@ -1730,9 +2000,22 @@ def get_file_md5(filepath: str) -> str:
|
|
|
1730
2000
|
filepath: 要计算哈希的文件路径
|
|
1731
2001
|
|
|
1732
2002
|
返回:
|
|
1733
|
-
str: 文件内容的MD5
|
|
2003
|
+
str: 文件内容的MD5哈希值(为降低内存占用,仅读取前100MB进行计算)
|
|
1734
2004
|
"""
|
|
1735
|
-
|
|
2005
|
+
# 采用流式读取,避免一次性加载100MB到内存
|
|
2006
|
+
h = hashlib.md5()
|
|
2007
|
+
max_bytes = 100 * 1024 * 1024 # 与原实现保持一致:仅读取前100MB
|
|
2008
|
+
buf_size = 8 * 1024 * 1024 # 8MB缓冲
|
|
2009
|
+
read_bytes = 0
|
|
2010
|
+
with open(filepath, "rb") as f:
|
|
2011
|
+
while read_bytes < max_bytes:
|
|
2012
|
+
to_read = min(buf_size, max_bytes - read_bytes)
|
|
2013
|
+
chunk = f.read(to_read)
|
|
2014
|
+
if not chunk:
|
|
2015
|
+
break
|
|
2016
|
+
h.update(chunk)
|
|
2017
|
+
read_bytes += len(chunk)
|
|
2018
|
+
return h.hexdigest()
|
|
1736
2019
|
|
|
1737
2020
|
|
|
1738
2021
|
def get_file_line_count(filename: str) -> int:
|
|
@@ -1745,7 +2028,9 @@ def get_file_line_count(filename: str) -> int:
|
|
|
1745
2028
|
int: 文件中的行数,如果文件无法读取则返回0
|
|
1746
2029
|
"""
|
|
1747
2030
|
try:
|
|
1748
|
-
|
|
2031
|
+
# 使用流式逐行计数,避免将整个文件读入内存
|
|
2032
|
+
with open(filename, "r", encoding="utf-8", errors="ignore") as f:
|
|
2033
|
+
return sum(1 for _ in f)
|
|
1749
2034
|
except Exception:
|
|
1750
2035
|
return 0
|
|
1751
2036
|
|