jarvis-ai-assistant 0.2.6__py3-none-any.whl → 0.2.7__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 CHANGED
@@ -1,4 +1,4 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  """Jarvis AI Assistant"""
3
3
 
4
- __version__ = "0.2.6"
4
+ __version__ = "0.2.7"
@@ -614,8 +614,19 @@ class Agent:
614
614
  memory_tags = get_all_memory_tags()
615
615
  memory_tags_prompt = ""
616
616
 
617
+ # 检查是否有save_memory工具
618
+ tool_registry = self.get_tool_registry()
619
+ has_save_memory = False
620
+ if tool_registry:
621
+ tool_names = [tool.name for tool in tool_registry.tools.values()]
622
+ has_save_memory = "save_memory" in tool_names
623
+
624
+ # 如果有save_memory工具,添加记录关键信息的提示
625
+ if has_save_memory:
626
+ memory_tags_prompt = "\n\n💡 提示:在分析任务之前,建议使用 save_memory 工具将关键信息记录下来,便于后续检索和复用。"
627
+
617
628
  if any(tags for tags in memory_tags.values()):
618
- memory_tags_prompt = "\n\n系统中存在以下记忆标签,你可以使用 retrieve_memory 工具检索相关记忆:"
629
+ memory_tags_prompt += "\n\n系统中存在以下记忆标签,你可以使用 retrieve_memory 工具检索相关记忆:"
619
630
  for memory_type, tags in memory_tags.items():
620
631
  if tags:
621
632
  type_name = {
@@ -4,7 +4,7 @@ import shutil
4
4
  import subprocess
5
5
  import sys
6
6
  from pathlib import Path
7
- from typing import Dict, Optional
7
+ from typing import Dict, Optional, List
8
8
 
9
9
  import typer
10
10
  import yaml # type: ignore
@@ -91,13 +91,19 @@ def _select_task(tasks: Dict[str, str]) -> str:
91
91
  selected_task = tasks[task_names[choice - 1]]
92
92
  PrettyOutput.print(f"将要执行任务:\n {selected_task}", OutputType.INFO)
93
93
  # 询问是否需要补充信息
94
- need_additional = user_confirm("需要为此任务添加补充信息吗?", default=False)
94
+ need_additional = user_confirm(
95
+ "需要为此任务添加补充信息吗?", default=False
96
+ )
95
97
  if need_additional:
96
98
  additional_input = get_multiline_input("请输入补充信息:")
97
99
  if additional_input:
98
- selected_task = f"{selected_task}\n\n补充信息:\n{additional_input}"
100
+ selected_task = (
101
+ f"{selected_task}\n\n补充信息:\n{additional_input}"
102
+ )
99
103
  return selected_task
100
- PrettyOutput.print("无效的选择。请选择列表中的一个号码。", OutputType.WARNING)
104
+ PrettyOutput.print(
105
+ "无效的选择。请选择列表中的一个号码。", OutputType.WARNING
106
+ )
101
107
 
102
108
  except (KeyboardInterrupt, EOFError):
103
109
  return ""
@@ -183,6 +189,217 @@ def _get_and_run_task(agent: Agent, task_content: Optional[str] = None) -> None:
183
189
  raise typer.Exit(code=0)
184
190
 
185
191
 
192
+ def _parse_selection(selection_str: str, max_value: int) -> List[int]:
193
+ """解析用户输入的选择字符串,支持逗号分隔和范围选择
194
+
195
+ 例如: "1,2,3,4-9,20" -> [1, 2, 3, 4, 5, 6, 7, 8, 9, 20]
196
+ """
197
+ selected: set[int] = set()
198
+ parts = selection_str.split(",")
199
+
200
+ for part in parts:
201
+ part = part.strip()
202
+ if "-" in part:
203
+ # 处理范围选择
204
+ try:
205
+ start_str, end_str = part.split("-")
206
+ start_num = int(start_str.strip())
207
+ end_num = int(end_str.strip())
208
+ if 1 <= start_num <= max_value and 1 <= end_num <= max_value:
209
+ selected.update(range(start_num, end_num + 1))
210
+ except ValueError:
211
+ continue
212
+ else:
213
+ # 处理单个数字
214
+ try:
215
+ num = int(part)
216
+ if 1 <= num <= max_value:
217
+ selected.add(num)
218
+ except ValueError:
219
+ continue
220
+
221
+ return sorted(list(selected))
222
+
223
+
224
+ def _handle_share_methodology(config_file: Optional[str] = None) -> None:
225
+ """处理方法论分享功能"""
226
+ from jarvis.jarvis_utils.config import (
227
+ get_central_methodology_repo,
228
+ get_methodology_dirs,
229
+ get_data_dir,
230
+ )
231
+ import glob
232
+ import json
233
+ import shutil
234
+
235
+ # 获取中心方法论仓库配置
236
+ central_repo = get_central_methodology_repo()
237
+ if not central_repo:
238
+ PrettyOutput.print(
239
+ "错误:未配置中心方法论仓库(JARVIS_CENTRAL_METHODOLOGY_REPO)",
240
+ OutputType.ERROR,
241
+ )
242
+ PrettyOutput.print("请在配置文件中设置中心方法论仓库的Git地址", OutputType.INFO)
243
+ raise typer.Exit(code=1)
244
+
245
+ # 克隆或更新中心方法论仓库
246
+ central_repo_path = os.path.join(get_data_dir(), "central_methodology_repo")
247
+ if not os.path.exists(central_repo_path):
248
+ PrettyOutput.print(f"正在克隆中心方法论仓库...", OutputType.INFO)
249
+ subprocess.run(["git", "clone", central_repo, central_repo_path], check=True)
250
+ else:
251
+ PrettyOutput.print(f"正在更新中心方法论仓库...", OutputType.INFO)
252
+ subprocess.run(["git", "pull"], cwd=central_repo_path, check=True)
253
+
254
+ # 获取中心仓库中已有的方法论
255
+ existing_methodologies = {} # 改为字典,存储 problem_type -> content 的映射
256
+ for filepath in glob.glob(os.path.join(central_repo_path, "*.json")):
257
+ try:
258
+ with open(filepath, "r", encoding="utf-8", errors="ignore") as f:
259
+ methodology = json.load(f)
260
+ problem_type = methodology.get("problem_type", "")
261
+ content = methodology.get("content", "")
262
+ if problem_type and content:
263
+ existing_methodologies[problem_type] = content
264
+ except Exception:
265
+ pass
266
+
267
+ # 获取所有方法论目录
268
+ from jarvis.jarvis_utils.methodology import _get_methodology_directory
269
+
270
+ methodology_dirs = [_get_methodology_directory()] + get_methodology_dirs()
271
+
272
+ # 收集所有方法论文件(排除中心仓库目录和已存在的方法论)
273
+ all_methodologies = {}
274
+ methodology_files = []
275
+ seen_problem_types = set() # 用于去重
276
+
277
+ for directory in set(methodology_dirs):
278
+ # 跳过中心仓库目录
279
+ if os.path.abspath(directory) == os.path.abspath(central_repo_path):
280
+ continue
281
+
282
+ if not os.path.isdir(directory):
283
+ continue
284
+
285
+ for filepath in glob.glob(os.path.join(directory, "*.json")):
286
+ try:
287
+ with open(filepath, "r", encoding="utf-8", errors="ignore") as f:
288
+ methodology = json.load(f)
289
+ problem_type = methodology.get("problem_type", "")
290
+ content = methodology.get("content", "")
291
+ # 基于内容判断是否已存在于中心仓库
292
+ is_duplicate = False
293
+ if problem_type in existing_methodologies:
294
+ # 如果problem_type相同,比较内容
295
+ if (
296
+ content.strip()
297
+ == existing_methodologies[problem_type].strip()
298
+ ):
299
+ is_duplicate = True
300
+
301
+ # 排除已存在于中心仓库的方法论(基于内容),以及本地重复的方法论
302
+ if (
303
+ problem_type
304
+ and content
305
+ and not is_duplicate
306
+ and problem_type not in seen_problem_types
307
+ ):
308
+ methodology_files.append(
309
+ {
310
+ "path": filepath,
311
+ "problem_type": problem_type,
312
+ "directory": directory,
313
+ }
314
+ )
315
+ all_methodologies[problem_type] = methodology
316
+ seen_problem_types.add(problem_type)
317
+ except Exception:
318
+ pass
319
+
320
+ if not methodology_files:
321
+ PrettyOutput.print(
322
+ "没有找到新的方法论文件(所有方法论可能已存在于中心仓库)",
323
+ OutputType.WARNING,
324
+ )
325
+ raise typer.Exit(code=0)
326
+
327
+ # 显示可选的方法论
328
+ methodology_list = ["\n可分享的方法论(已排除中心仓库中已有的):"]
329
+ for i, meth in enumerate(methodology_files, 1):
330
+ dir_name = os.path.basename(meth["directory"])
331
+ methodology_list.append(f"[{i}] {meth['problem_type']} (来自: {dir_name})")
332
+
333
+ # 一次性打印所有方法论
334
+ PrettyOutput.print("\n".join(methodology_list), OutputType.INFO)
335
+
336
+ # 让用户选择要分享的方法论
337
+ while True:
338
+ try:
339
+ choice_str = prompt(
340
+ "\n请选择要分享的方法论编号(支持格式: 1,2,3,4-9,20 或 all):"
341
+ ).strip()
342
+ if choice_str == "0":
343
+ raise typer.Exit(code=0)
344
+
345
+ selected_methodologies = []
346
+ if choice_str.lower() == "all":
347
+ selected_methodologies = methodology_files
348
+ else:
349
+ selected_indices = _parse_selection(choice_str, len(methodology_files))
350
+ if not selected_indices:
351
+ PrettyOutput.print("无效的选择", OutputType.WARNING)
352
+ continue
353
+ selected_methodologies = [
354
+ methodology_files[i - 1] for i in selected_indices
355
+ ]
356
+
357
+ # 确认操作
358
+ share_list = ["\n将要分享以下方法论到中心仓库:"]
359
+ for meth in selected_methodologies:
360
+ share_list.append(f"- {meth['problem_type']}")
361
+ PrettyOutput.print("\n".join(share_list), OutputType.INFO)
362
+
363
+ if not user_confirm("确认分享这些方法论吗?"):
364
+ continue
365
+
366
+ # 复制选中的方法论到中心仓库
367
+ copied_list = []
368
+ for meth in selected_methodologies:
369
+ src_file = meth["path"]
370
+ dst_file = os.path.join(central_repo_path, os.path.basename(src_file))
371
+ shutil.copy2(src_file, dst_file)
372
+ copied_list.append(f"已复制: {meth['problem_type']}")
373
+
374
+ # 一次性显示所有复制结果
375
+ if copied_list:
376
+ PrettyOutput.print("\n".join(copied_list), OutputType.SUCCESS)
377
+
378
+ # 提交并推送更改
379
+ PrettyOutput.print("\n正在提交更改...", OutputType.INFO)
380
+ subprocess.run(["git", "add", "."], cwd=central_repo_path, check=True)
381
+
382
+ commit_msg = f"Add {len(selected_methodologies)} methodology(ies) from local collection"
383
+ subprocess.run(
384
+ ["git", "commit", "-m", commit_msg], cwd=central_repo_path, check=True
385
+ )
386
+
387
+ PrettyOutput.print("正在推送到远程仓库...", OutputType.INFO)
388
+ subprocess.run(["git", "push"], cwd=central_repo_path, check=True)
389
+
390
+ PrettyOutput.print("\n方法论已成功分享到中心仓库!", OutputType.SUCCESS)
391
+ break
392
+
393
+ except ValueError:
394
+ PrettyOutput.print("请输入有效的数字", OutputType.WARNING)
395
+ except subprocess.CalledProcessError as e:
396
+ PrettyOutput.print(f"Git操作失败: {str(e)}", OutputType.ERROR)
397
+ raise typer.Exit(code=1)
398
+ except Exception as e:
399
+ PrettyOutput.print(f"分享方法论时出错: {str(e)}", OutputType.ERROR)
400
+ raise typer.Exit(code=1)
401
+
402
+
186
403
  @app.callback(invoke_without_command=True)
187
404
  def run_cli(
188
405
  ctx: typer.Context,
@@ -191,17 +408,24 @@ def run_cli(
191
408
  "--llm_type",
192
409
  help="使用的LLM类型,可选值:'normal'(普通)或 'thinking'(思考模式)",
193
410
  ),
194
- task: Optional[str] = typer.Option(None, "-t", "--task", help="从命令行直接输入任务内容"),
411
+ task: Optional[str] = typer.Option(
412
+ None, "-t", "--task", help="从命令行直接输入任务内容"
413
+ ),
195
414
  model_group: Optional[str] = typer.Option(
196
415
  None, "--llm_group", help="使用的模型组,覆盖配置文件中的设置"
197
416
  ),
198
- config_file: Optional[str] = typer.Option(None, "-f", "--config", help="自定义配置文件路径"),
417
+ config_file: Optional[str] = typer.Option(
418
+ None, "-f", "--config", help="自定义配置文件路径"
419
+ ),
199
420
  restore_session: bool = typer.Option(
200
421
  False,
201
422
  "--restore-session",
202
423
  help="从 .jarvis/saved_session.json 恢复会话",
203
424
  ),
204
425
  edit: bool = typer.Option(False, "-e", "--edit", help="编辑配置文件"),
426
+ share_methodology: bool = typer.Option(
427
+ False, "--share-methodology", help="分享本地方法论到中心方法论仓库"
428
+ ),
205
429
  ) -> None:
206
430
  """Jarvis AI assistant command-line interface."""
207
431
  if ctx.invoked_subcommand is not None:
@@ -209,7 +433,15 @@ def run_cli(
209
433
 
210
434
  _handle_edit_mode(edit, config_file)
211
435
 
212
- init_env("欢迎使用 Jarvis AI 助手,您的智能助理已准备就绪!", config_file=config_file)
436
+ # 处理方法论分享
437
+ if share_methodology:
438
+ init_env("", config_file=config_file) # 初始化配置但不显示欢迎信息
439
+ _handle_share_methodology(config_file)
440
+ raise typer.Exit(code=0)
441
+
442
+ init_env(
443
+ "欢迎使用 Jarvis AI 助手,您的智能助理已准备就绪!", config_file=config_file
444
+ )
213
445
 
214
446
  try:
215
447
  agent = _initialize_agent(llm_type, model_group, restore_session)
@@ -67,6 +67,7 @@ class CodeAgent:
67
67
  "rewrite_file",
68
68
  "save_memory",
69
69
  "retrieve_memory",
70
+ "clear_memory",
70
71
  ]
71
72
  )
72
73
  code_system_prompt = """
@@ -225,6 +225,11 @@
225
225
  },
226
226
  "default": []
227
227
  },
228
+ "JARVIS_CENTRAL_METHODOLOGY_REPO": {
229
+ "type": "string",
230
+ "description": "中心方法论Git仓库地址,该仓库会自动添加到方法论加载路径中",
231
+ "default": ""
232
+ },
228
233
  "JARVIS_PRINT_PROMPT": {
229
234
  "type": "boolean",
230
235
  "description": "是否打印提示",
@@ -245,7 +245,7 @@ class StatsStorage:
245
245
  metrics_from_meta = set(meta.get("metrics", {}).keys())
246
246
 
247
247
  # 扫描所有数据文件获取实际存在的指标
248
- metrics_from_data = set()
248
+ metrics_from_data: set[str] = set()
249
249
  for data_file in self.data_dir.glob("stats_*.json"):
250
250
  try:
251
251
  data = self._load_json(data_file)
@@ -285,7 +285,7 @@ class StatsStorage:
285
285
  return {}
286
286
 
287
287
  # 聚合数据
288
- aggregated = defaultdict(
288
+ aggregated: Dict[str, Dict[str, Any]] = defaultdict(
289
289
  lambda: {
290
290
  "count": 0,
291
291
  "sum": 0,
@@ -0,0 +1,252 @@
1
+ # -*- coding: utf-8 -*-
2
+ import json
3
+ import shutil
4
+ from pathlib import Path
5
+ from typing import Any, Dict, List, Optional
6
+
7
+ from jarvis.jarvis_utils.config import get_data_dir
8
+ from jarvis.jarvis_utils.output import OutputType, PrettyOutput
9
+ from jarvis.jarvis_utils.globals import (
10
+ clear_short_term_memories,
11
+ get_short_term_memories,
12
+ short_term_memories,
13
+ )
14
+
15
+
16
+ class ClearMemoryTool:
17
+ """清除记忆工具,用于批量清除指定的记忆"""
18
+
19
+ name = "clear_memory"
20
+ description = """批量清除指定的记忆。
21
+
22
+ 支持的清除方式:
23
+ 1. 按记忆类型清除所有记忆
24
+ 2. 按标签清除特定记忆
25
+ 3. 按记忆ID清除单个记忆
26
+
27
+ 支持的记忆类型:
28
+ - project_long_term: 项目长期记忆
29
+ - global_long_term: 全局长期记忆
30
+ - short_term: 短期记忆
31
+ - all: 所有类型的记忆
32
+
33
+ 注意:清除操作不可恢复,请谨慎使用
34
+ """
35
+
36
+ parameters = {
37
+ "type": "object",
38
+ "properties": {
39
+ "memory_types": {
40
+ "type": "array",
41
+ "items": {
42
+ "type": "string",
43
+ "enum": [
44
+ "project_long_term",
45
+ "global_long_term",
46
+ "short_term",
47
+ "all",
48
+ ],
49
+ },
50
+ "description": "要清除的记忆类型列表",
51
+ },
52
+ "tags": {
53
+ "type": "array",
54
+ "items": {"type": "string"},
55
+ "description": "要清除的记忆标签列表(可选,如果指定则只清除带有这些标签的记忆)",
56
+ },
57
+ "memory_ids": {
58
+ "type": "array",
59
+ "items": {"type": "string"},
60
+ "description": "要清除的具体记忆ID列表(可选)",
61
+ },
62
+ "confirm": {
63
+ "type": "boolean",
64
+ "description": "确认清除操作(必须为true才会执行清除)",
65
+ "default": False,
66
+ },
67
+ },
68
+ "required": ["memory_types", "confirm"],
69
+ }
70
+
71
+ def __init__(self):
72
+ """初始化清除记忆工具"""
73
+ self.project_memory_dir = Path(".jarvis/memory")
74
+ self.global_memory_dir = Path(get_data_dir()) / "memory"
75
+
76
+ def _get_memory_dir(self, memory_type: str) -> Path:
77
+ """根据记忆类型获取存储目录"""
78
+ if memory_type == "project_long_term":
79
+ return self.project_memory_dir
80
+ elif memory_type in ["global_long_term", "short_term"]:
81
+ return self.global_memory_dir / memory_type
82
+ else:
83
+ raise ValueError(f"未知的记忆类型: {memory_type}")
84
+
85
+ def _clear_short_term_memories(
86
+ self, tags: Optional[List[str]] = None, memory_ids: Optional[List[str]] = None
87
+ ) -> Dict[str, int]:
88
+ """清除短期记忆"""
89
+ global short_term_memories
90
+
91
+ initial_count = len(short_term_memories)
92
+ removed_count = 0
93
+
94
+ if memory_ids:
95
+ # 按ID清除
96
+ new_memories = []
97
+ for memory in short_term_memories:
98
+ if memory.get("id") not in memory_ids:
99
+ new_memories.append(memory)
100
+ else:
101
+ removed_count += 1
102
+ short_term_memories[:] = new_memories
103
+ elif tags:
104
+ # 按标签清除
105
+ new_memories = []
106
+ for memory in short_term_memories:
107
+ memory_tags = memory.get("tags", [])
108
+ if not any(tag in memory_tags for tag in tags):
109
+ new_memories.append(memory)
110
+ else:
111
+ removed_count += 1
112
+ short_term_memories[:] = new_memories
113
+ else:
114
+ # 清除所有
115
+ clear_short_term_memories()
116
+ removed_count = initial_count
117
+
118
+ return {"total": initial_count, "removed": removed_count}
119
+
120
+ def _clear_long_term_memories(
121
+ self,
122
+ memory_type: str,
123
+ tags: Optional[List[str]] = None,
124
+ memory_ids: Optional[List[str]] = None,
125
+ ) -> Dict[str, int]:
126
+ """清除长期记忆"""
127
+ memory_dir = self._get_memory_dir(memory_type)
128
+
129
+ if not memory_dir.exists():
130
+ return {"total": 0, "removed": 0}
131
+
132
+ total_count = 0
133
+ removed_count = 0
134
+
135
+ # 获取所有记忆文件
136
+ memory_files = list(memory_dir.glob("*.json"))
137
+ total_count = len(memory_files)
138
+
139
+ for memory_file in memory_files:
140
+ try:
141
+ # 读取记忆内容
142
+ with open(memory_file, "r", encoding="utf-8") as f:
143
+ memory_data = json.load(f)
144
+
145
+ should_remove = False
146
+
147
+ if memory_ids:
148
+ # 按ID判断
149
+ if memory_data.get("id") in memory_ids:
150
+ should_remove = True
151
+ elif tags:
152
+ # 按标签判断
153
+ memory_tags = memory_data.get("tags", [])
154
+ if any(tag in memory_tags for tag in tags):
155
+ should_remove = True
156
+ else:
157
+ # 清除所有
158
+ should_remove = True
159
+
160
+ if should_remove:
161
+ memory_file.unlink()
162
+ removed_count += 1
163
+
164
+ except Exception as e:
165
+ PrettyOutput.print(
166
+ f"处理记忆文件 {memory_file} 时出错: {str(e)}", OutputType.WARNING
167
+ )
168
+
169
+ # 如果目录为空,可以删除目录
170
+ if not any(memory_dir.iterdir()) and memory_dir != self.project_memory_dir:
171
+ memory_dir.rmdir()
172
+
173
+ return {"total": total_count, "removed": removed_count}
174
+
175
+ def execute(self, args: Dict[str, Any]) -> Dict[str, Any]:
176
+ """执行清除记忆操作"""
177
+ try:
178
+ memory_types = args.get("memory_types", [])
179
+ tags = args.get("tags", [])
180
+ memory_ids = args.get("memory_ids", [])
181
+ confirm = args.get("confirm", False)
182
+
183
+ if not confirm:
184
+ return {
185
+ "success": False,
186
+ "stdout": "",
187
+ "stderr": "必须设置 confirm=true 才能执行清除操作",
188
+ }
189
+
190
+ # 确定要清除的记忆类型
191
+ if "all" in memory_types:
192
+ types_to_clear = ["project_long_term", "global_long_term", "short_term"]
193
+ else:
194
+ types_to_clear = memory_types
195
+
196
+ # 统计结果
197
+ results = {}
198
+ total_removed = 0
199
+
200
+ # 清除各类型的记忆
201
+ for memory_type in types_to_clear:
202
+ if memory_type == "short_term":
203
+ result = self._clear_short_term_memories(tags, memory_ids)
204
+ else:
205
+ result = self._clear_long_term_memories(
206
+ memory_type, tags, memory_ids
207
+ )
208
+
209
+ results[memory_type] = result
210
+ total_removed += result["removed"]
211
+
212
+ # 生成结果报告
213
+ PrettyOutput.print(
214
+ f"记忆清除完成,共清除 {total_removed} 条记忆", OutputType.SUCCESS
215
+ )
216
+
217
+ # 详细报告
218
+ report = f"# 记忆清除报告\n\n"
219
+ report += f"**总计清除**: {total_removed} 条记忆\n\n"
220
+
221
+ if tags:
222
+ report += f"**使用标签过滤**: {', '.join(tags)}\n\n"
223
+
224
+ if memory_ids:
225
+ report += f"**指定记忆ID**: {', '.join(memory_ids)}\n\n"
226
+
227
+ report += "## 详细结果\n\n"
228
+
229
+ for memory_type, result in results.items():
230
+ report += f"### {memory_type}\n"
231
+ report += f"- 原有记忆: {result['total']} 条\n"
232
+ report += f"- 已清除: {result['removed']} 条\n"
233
+ report += f"- 剩余: {result['total'] - result['removed']} 条\n\n"
234
+
235
+ # 在终端显示摘要
236
+ for memory_type, result in results.items():
237
+ if result["removed"] > 0:
238
+ PrettyOutput.print(
239
+ f"{memory_type}: 清除了 {result['removed']}/{result['total']} 条记忆",
240
+ OutputType.INFO,
241
+ )
242
+
243
+ return {
244
+ "success": True,
245
+ "stdout": report,
246
+ "stderr": "",
247
+ }
248
+
249
+ except Exception as e:
250
+ error_msg = f"清除记忆失败: {str(e)}"
251
+ PrettyOutput.print(error_msg, OutputType.ERROR)
252
+ return {"success": False, "stdout": "", "stderr": error_msg}
@@ -22,6 +22,7 @@ class RetrieveMemoryTool:
22
22
  - all: 从所有类型中检索
23
23
 
24
24
  可以通过标签过滤检索结果,支持多个标签(满足任一标签即可)
25
+ 注意:标签数量建议不要超过10个,以保证检索效率
25
26
  """
26
27
 
27
28
  parameters = {
@@ -149,35 +150,40 @@ class RetrieveMemoryTool:
149
150
  # 格式化为Markdown输出
150
151
  markdown_output = f"# 记忆检索结果\n\n"
151
152
  markdown_output += f"**检索到 {len(all_memories)} 条记忆**\n\n"
152
-
153
+
153
154
  if tags:
154
155
  markdown_output += f"**使用标签过滤**: {', '.join(tags)}\n\n"
155
-
156
+
156
157
  markdown_output += f"**记忆类型**: {', '.join(types_to_search)}\n\n"
157
-
158
+
158
159
  markdown_output += "---\n\n"
159
-
160
+
160
161
  # 输出所有记忆
161
162
  for i, memory in enumerate(all_memories):
162
163
  markdown_output += f"## {i+1}. {memory.get('id', '未知ID')}\n\n"
163
164
  markdown_output += f"**类型**: {memory.get('type', '未知类型')}\n\n"
164
165
  markdown_output += f"**标签**: {', '.join(memory.get('tags', []))}\n\n"
165
- markdown_output += f"**创建时间**: {memory.get('created_at', '未知时间')}\n\n"
166
-
166
+ markdown_output += (
167
+ f"**创建时间**: {memory.get('created_at', '未知时间')}\n\n"
168
+ )
169
+
167
170
  # 内容部分
168
- content = memory.get('content', '')
171
+ content = memory.get("content", "")
169
172
  if content:
170
173
  markdown_output += f"**内容**:\n\n{content}\n\n"
171
-
174
+
172
175
  # 如果有额外的元数据
173
- metadata = {k: v for k, v in memory.items()
174
- if k not in ['id', 'type', 'tags', 'created_at', 'content']}
176
+ metadata = {
177
+ k: v
178
+ for k, v in memory.items()
179
+ if k not in ["id", "type", "tags", "created_at", "content"]
180
+ }
175
181
  if metadata:
176
182
  markdown_output += f"**其他信息**:\n"
177
183
  for key, value in metadata.items():
178
184
  markdown_output += f"- {key}: {value}\n"
179
185
  markdown_output += "\n"
180
-
186
+
181
187
  markdown_output += "---\n\n"
182
188
 
183
189
  # 如果记忆较多,在终端显示摘要
@@ -1,5 +1,6 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  import json
3
+ import time
3
4
  from datetime import datetime
4
5
  from pathlib import Path
5
6
  from typing import Any, Dict, List
@@ -15,6 +16,8 @@ class SaveMemoryTool:
15
16
  name = "save_memory"
16
17
  description = """保存信息到长短期记忆系统。
17
18
 
19
+ 支持批量保存多条记忆,可以同时保存不同类型的记忆。
20
+
18
21
  支持的记忆类型:
19
22
  - project_long_term: 项目长期记忆(与当前项目相关的信息)
20
23
  - global_long_term: 全局长期记忆(通用的信息、用户喜好、知识、方法等)
@@ -27,19 +30,36 @@ class SaveMemoryTool:
27
30
  parameters = {
28
31
  "type": "object",
29
32
  "properties": {
30
- "memory_type": {
31
- "type": "string",
32
- "enum": ["project_long_term", "global_long_term", "short_term"],
33
- "description": "记忆类型",
34
- },
35
- "tags": {
33
+ "memories": {
36
34
  "type": "array",
37
- "items": {"type": "string"},
38
- "description": "用于索引记忆的标签列表",
39
- },
40
- "content": {"type": "string", "description": "要保存的记忆内容"},
35
+ "items": {
36
+ "type": "object",
37
+ "properties": {
38
+ "memory_type": {
39
+ "type": "string",
40
+ "enum": [
41
+ "project_long_term",
42
+ "global_long_term",
43
+ "short_term",
44
+ ],
45
+ "description": "记忆类型",
46
+ },
47
+ "tags": {
48
+ "type": "array",
49
+ "items": {"type": "string"},
50
+ "description": "用于索引记忆的标签列表",
51
+ },
52
+ "content": {
53
+ "type": "string",
54
+ "description": "要保存的记忆内容",
55
+ },
56
+ },
57
+ "required": ["memory_type", "tags", "content"],
58
+ },
59
+ "description": "要保存的记忆列表",
60
+ }
41
61
  },
42
- "required": ["memory_type", "tags", "content"],
62
+ "required": ["memories"],
43
63
  }
44
64
 
45
65
  def __init__(self):
@@ -58,81 +78,122 @@ class SaveMemoryTool:
58
78
 
59
79
  def _generate_memory_id(self) -> str:
60
80
  """生成唯一的记忆ID"""
81
+ # 添加微秒级时间戳确保唯一性
82
+ time.sleep(0.001) # 确保不同记忆有不同的时间戳
61
83
  return datetime.now().strftime("%Y%m%d_%H%M%S_%f")
62
84
 
85
+ def _save_single_memory(self, memory_data: Dict[str, Any]) -> Dict[str, Any]:
86
+ """保存单条记忆"""
87
+ memory_type = memory_data["memory_type"]
88
+ tags = memory_data.get("tags", [])
89
+ content = memory_data.get("content", "")
90
+
91
+ # 生成记忆ID
92
+ memory_id = self._generate_memory_id()
93
+
94
+ # 创建记忆对象
95
+ memory_obj = {
96
+ "id": memory_id,
97
+ "type": memory_type,
98
+ "tags": tags,
99
+ "content": content,
100
+ "created_at": datetime.now().isoformat(),
101
+ "updated_at": datetime.now().isoformat(),
102
+ }
103
+
104
+ if memory_type == "short_term":
105
+ # 短期记忆保存到全局变量
106
+ add_short_term_memory(memory_obj)
107
+
108
+ result = {
109
+ "memory_id": memory_id,
110
+ "memory_type": memory_type,
111
+ "tags": tags,
112
+ "storage": "memory",
113
+ "message": f"短期记忆已成功保存到内存,ID: {memory_id}",
114
+ }
115
+ else:
116
+ # 长期记忆保存到文件
117
+ # 获取存储目录并确保存在
118
+ memory_dir = self._get_memory_dir(memory_type)
119
+ memory_dir.mkdir(parents=True, exist_ok=True)
120
+
121
+ # 保存记忆文件
122
+ memory_file = memory_dir / f"{memory_id}.json"
123
+ with open(memory_file, "w", encoding="utf-8") as f:
124
+ json.dump(memory_obj, f, ensure_ascii=False, indent=2)
125
+
126
+ result = {
127
+ "memory_id": memory_id,
128
+ "memory_type": memory_type,
129
+ "tags": tags,
130
+ "file_path": str(memory_file),
131
+ "message": f"记忆已成功保存,ID: {memory_id}",
132
+ }
133
+
134
+ return result
135
+
63
136
  def execute(self, args: Dict[str, Any]) -> Dict[str, Any]:
64
137
  """执行保存记忆操作"""
65
138
  try:
66
- memory_type = args["memory_type"]
67
- tags = args.get("tags", [])
68
- content = args.get("content", "")
139
+ memories = args.get("memories", [])
69
140
 
70
- # 生成记忆ID
71
- memory_id = self._generate_memory_id()
141
+ if not memories:
142
+ return {
143
+ "success": False,
144
+ "stdout": "",
145
+ "stderr": "没有提供要保存的记忆",
146
+ }
72
147
 
73
- # 创建记忆对象
74
- memory_data = {
75
- "id": memory_id,
76
- "type": memory_type,
77
- "tags": tags,
78
- "content": content,
79
- "created_at": datetime.now().isoformat(),
80
- "updated_at": datetime.now().isoformat(),
148
+ results = []
149
+ success_count = 0
150
+ failed_count = 0
151
+
152
+ # 保存每条记忆
153
+ for i, memory_data in enumerate(memories):
154
+ try:
155
+ result = self._save_single_memory(memory_data)
156
+ results.append(result)
157
+ success_count += 1
158
+
159
+ # 打印单条记忆保存信息
160
+ memory_type = memory_data["memory_type"]
161
+ tags = memory_data.get("tags", [])
162
+ PrettyOutput.print(
163
+ f"[{i+1}/{len(memories)}] {memory_type} 记忆已保存\n"
164
+ f"ID: {result['memory_id']}\n"
165
+ f"标签: {', '.join(tags)}",
166
+ OutputType.SUCCESS,
167
+ )
168
+ except Exception as e:
169
+ failed_count += 1
170
+ error_msg = f"保存第 {i+1} 条记忆失败: {str(e)}"
171
+ PrettyOutput.print(error_msg, OutputType.ERROR)
172
+ results.append(
173
+ {
174
+ "error": error_msg,
175
+ "memory_type": memory_data.get("memory_type", "unknown"),
176
+ "tags": memory_data.get("tags", []),
177
+ }
178
+ )
179
+
180
+ # 生成总结报告
181
+ PrettyOutput.print(
182
+ f"\n批量保存完成:成功 {success_count} 条,失败 {failed_count} 条",
183
+ OutputType.INFO,
184
+ )
185
+
186
+ # 构建返回结果
187
+ output = {
188
+ "total": len(memories),
189
+ "success": success_count,
190
+ "failed": failed_count,
191
+ "results": results,
81
192
  }
82
193
 
83
- if memory_type == "short_term":
84
- # 短期记忆保存到全局变量
85
- add_short_term_memory(memory_data)
86
-
87
- # 打印成功信息
88
- PrettyOutput.print(
89
- f"短期记忆已保存\n"
90
- f"ID: {memory_id}\n"
91
- f"类型: {memory_type}\n"
92
- f"标签: {', '.join(tags)}\n"
93
- f"存储位置: 内存(非持久化)",
94
- OutputType.SUCCESS,
95
- )
96
-
97
- result = {
98
- "memory_id": memory_id,
99
- "memory_type": memory_type,
100
- "tags": tags,
101
- "storage": "memory",
102
- "message": f"短期记忆已成功保存到内存,ID: {memory_id}",
103
- }
104
- else:
105
- # 长期记忆保存到文件
106
- # 获取存储目录并确保存在
107
- memory_dir = self._get_memory_dir(memory_type)
108
- memory_dir.mkdir(parents=True, exist_ok=True)
109
-
110
- # 保存记忆文件
111
- memory_file = memory_dir / f"{memory_id}.json"
112
- with open(memory_file, "w", encoding="utf-8") as f:
113
- json.dump(memory_data, f, ensure_ascii=False, indent=2)
114
-
115
- # 打印成功信息
116
- PrettyOutput.print(
117
- f"记忆已保存\n"
118
- f"ID: {memory_id}\n"
119
- f"类型: {memory_type}\n"
120
- f"标签: {', '.join(tags)}\n"
121
- f"位置: {memory_file}",
122
- OutputType.SUCCESS,
123
- )
124
-
125
- result = {
126
- "memory_id": memory_id,
127
- "memory_type": memory_type,
128
- "tags": tags,
129
- "file_path": str(memory_file),
130
- "message": f"记忆已成功保存,ID: {memory_id}",
131
- }
132
-
133
194
  return {
134
195
  "success": True,
135
- "stdout": json.dumps(result, ensure_ascii=False, indent=2),
196
+ "stdout": json.dumps(output, ensure_ascii=False, indent=2),
136
197
  "stderr": "",
137
198
  }
138
199
 
@@ -309,6 +309,16 @@ def get_methodology_dirs() -> List[str]:
309
309
  return GLOBAL_CONFIG_DATA.get("JARVIS_METHODOLOGY_DIRS", [])
310
310
 
311
311
 
312
+ def get_central_methodology_repo() -> str:
313
+ """
314
+ 获取中心方法论Git仓库地址。
315
+
316
+ 返回:
317
+ str: 中心方法论Git仓库地址,如果未配置则返回空字符串
318
+ """
319
+ return GLOBAL_CONFIG_DATA.get("JARVIS_CENTRAL_METHODOLOGY_REPO", "")
320
+
321
+
312
322
  def is_print_prompt() -> bool:
313
323
  """
314
324
  获取是否打印提示。
@@ -216,9 +216,11 @@ def _get_multiline_input_internal(tip: str) -> str:
216
216
  @bindings.add("enter")
217
217
  def _(event):
218
218
  if event.current_buffer.complete_state:
219
- event.current_buffer.apply_completion(
220
- event.current_buffer.complete_state.current_completion
221
- )
219
+ completion = event.current_buffer.complete_state.current_completion
220
+ if completion:
221
+ event.current_buffer.apply_completion(completion)
222
+ else:
223
+ event.current_buffer.insert_text("\n")
222
224
  else:
223
225
  event.current_buffer.insert_text("\n")
224
226
 
@@ -15,11 +15,16 @@ from typing import Any, Dict, List, Optional
15
15
 
16
16
  from jarvis.jarvis_platform.base import BasePlatform
17
17
  from jarvis.jarvis_platform.registry import PlatformRegistry
18
- from jarvis.jarvis_utils.config import get_data_dir, get_methodology_dirs
18
+ from jarvis.jarvis_utils.config import (
19
+ get_data_dir,
20
+ get_methodology_dirs,
21
+ get_central_methodology_repo,
22
+ )
19
23
  from jarvis.jarvis_utils.globals import get_agent, current_agent_name
20
24
  from jarvis.jarvis_utils.output import OutputType, PrettyOutput
21
25
  from jarvis.jarvis_utils.utils import is_context_overflow, daily_check_git_updates
22
26
 
27
+
23
28
  def _get_methodology_directory() -> str:
24
29
  """
25
30
  获取方法论目录路径,如果不存在则创建
@@ -46,6 +51,29 @@ def _load_all_methodologies() -> Dict[str, str]:
46
51
  all_methodologies = {}
47
52
  methodology_dirs = [_get_methodology_directory()] + get_methodology_dirs()
48
53
 
54
+ # 如果配置了中心方法论仓库,将其添加到加载路径
55
+ central_repo = get_central_methodology_repo()
56
+ if central_repo:
57
+ # 中心方法论仓库存储在数据目录下的特定位置
58
+ central_repo_path = os.path.join(get_data_dir(), "central_methodology_repo")
59
+ methodology_dirs.append(central_repo_path)
60
+
61
+ # 确保中心方法论仓库被克隆/更新
62
+ if not os.path.exists(central_repo_path):
63
+ try:
64
+ import subprocess
65
+
66
+ PrettyOutput.print(
67
+ f"正在克隆中心方法论仓库: {central_repo}", OutputType.INFO
68
+ )
69
+ subprocess.run(
70
+ ["git", "clone", central_repo, central_repo_path], check=True
71
+ )
72
+ except Exception as e:
73
+ PrettyOutput.print(
74
+ f"克隆中心方法论仓库失败: {str(e)}", OutputType.ERROR
75
+ )
76
+
49
77
  # --- 全局每日更新检查 ---
50
78
  daily_check_git_updates(methodology_dirs, "methodologies")
51
79
 
@@ -53,7 +81,9 @@ def _load_all_methodologies() -> Dict[str, str]:
53
81
 
54
82
  for directory in set(methodology_dirs): # Use set to avoid duplicates
55
83
  if not os.path.isdir(directory):
56
- PrettyOutput.print(f"警告: 方法论目录不存在或不是一个目录: {directory}", OutputType.WARNING)
84
+ PrettyOutput.print(
85
+ f"警告: 方法论目录不存在或不是一个目录: {directory}", OutputType.WARNING
86
+ )
57
87
  continue
58
88
 
59
89
  for filepath in glob.glob(os.path.join(directory, "*.json")):
@@ -64,7 +94,7 @@ def _load_all_methodologies() -> Dict[str, str]:
64
94
  content = methodology.get("content", "")
65
95
  if problem_type and content:
66
96
  if problem_type in all_methodologies:
67
- PrettyOutput.print(f"警告: 方法论 '{problem_type}' 被 '{filepath}' 覆盖。", OutputType.WARNING)
97
+ pass
68
98
  all_methodologies[problem_type] = content
69
99
  except Exception as e:
70
100
  filename = os.path.basename(filepath)
@@ -211,8 +241,9 @@ def load_methodology(user_input: str, tool_registery: Optional[Any] = None) -> s
211
241
 
212
242
  # 从响应中提取<NUM>标签内的内容
213
243
  import re
214
- num_match = re.search(r'<NUM>(.*?)</NUM>', response, re.DOTALL)
215
-
244
+
245
+ num_match = re.search(r"<NUM>(.*?)</NUM>", response, re.DOTALL)
246
+
216
247
  if not num_match:
217
248
  # 如果没有找到<NUM>标签,尝试直接解析响应
218
249
  selected_indices_str = response
@@ -226,7 +257,11 @@ def load_methodology(user_input: str, tool_registery: Optional[Any] = None) -> s
226
257
  selected_methodologies = {}
227
258
  try:
228
259
  if selected_indices_str:
229
- indices = [int(idx.strip()) for idx in selected_indices_str.split(",") if idx.strip().isdigit()]
260
+ indices = [
261
+ int(idx.strip())
262
+ for idx in selected_indices_str.split(",")
263
+ if idx.strip().isdigit()
264
+ ]
230
265
  for idx in indices:
231
266
  if 1 <= idx <= len(methodology_titles):
232
267
  title = methodology_titles[idx - 1]
@@ -204,7 +204,7 @@ class PrettyOutput:
204
204
  color="red", frame=True, bgcolor="#2b1c1c", meta={"icon": "❌"}
205
205
  ),
206
206
  OutputType.INFO: RichStyle(
207
- color="gold1", frame=True, bgcolor="#2b2b1c", meta={"icon": "ℹ️"}
207
+ color="bright_cyan", frame=True, bgcolor="#2b2b1c", meta={"icon": "ℹ️"}
208
208
  ),
209
209
  OutputType.PLANNING: RichStyle(
210
210
  color="purple",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: jarvis-ai-assistant
3
- Version: 0.2.6
3
+ Version: 0.2.7
4
4
  Summary: Jarvis: An AI assistant that uses tools to interact with the system
5
5
  Home-page: https://github.com/skyfireitdiy/Jarvis
6
6
  Author: skyfire
@@ -1,8 +1,8 @@
1
- jarvis/__init__.py,sha256=LeVLn20oHpcp1TpDsXJ8P6m_z_MKXTzTQvPUv3CRNK4,73
2
- jarvis/jarvis_agent/__init__.py,sha256=nhJ9QOdFQJHpdLsFO0_JNH0XJHvXxMDHDTP4b7-OSnA,26370
1
+ jarvis/__init__.py,sha256=2oEyA1qaR5QI1ZfAHJn9_9z3DRmOtWPUEtfnySBHunk,73
2
+ jarvis/jarvis_agent/__init__.py,sha256=feyq1jxhgzAy8y0X7nNidmFTm8gkpJdhikzmphZNU7M,26945
3
3
  jarvis/jarvis_agent/builtin_input_handler.py,sha256=Qs4LAr4xdKLBJpQE81YP4CkucAop86ms0iVoKa1nnso,2468
4
4
  jarvis/jarvis_agent/edit_file_handler.py,sha256=w-byNJ4TN_SlV3djjfFC7OksySOFGrM8ku49w662dzc,11854
5
- jarvis/jarvis_agent/jarvis.py,sha256=L2sI-Y7gxqH6M4E4F2GlNoZcxxvz_f72rxvjh7bxuZE,8443
5
+ jarvis/jarvis_agent/jarvis.py,sha256=Z4Alt7J5cOKUkdkPuSqPD-Zy338Zg8IizJZ3SzCRWbg,17696
6
6
  jarvis/jarvis_agent/main.py,sha256=56pLVy6v-3ZdyPCcWXdRkgbjmYsoIfC7zrA6B7sYivU,3334
7
7
  jarvis/jarvis_agent/output_handler.py,sha256=P7oWpXBGFfOsWq7cIhS_z9crkQ19ES7qU5pM92KKjAs,1172
8
8
  jarvis/jarvis_agent/prompt_builder.py,sha256=PH1fPDVa8z_RXkoXHJFNDf8PQjUoLNLYwkh2lC__p40,1705
@@ -12,7 +12,7 @@ jarvis/jarvis_agent/session_manager.py,sha256=DnvI9rWkVmkyO1XfKZyo9lTn4ajg4ccwzE
12
12
  jarvis/jarvis_agent/shell_input_handler.py,sha256=1IboqdxcJuoIqRpmDU10GugR9fWXUHyCEbVF4nIWbyo,1328
13
13
  jarvis/jarvis_agent/tool_executor.py,sha256=nIq-sPNgrtimtM-IHpN09cWmId8jDzWRdCFoRzXnnoo,1721
14
14
  jarvis/jarvis_code_agent/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
- jarvis/jarvis_code_agent/code_agent.py,sha256=C761NUsz3W_SLJLEj6Y_HB9ox-joOlnJQ9CsfE88IFk,29100
15
+ jarvis/jarvis_code_agent/code_agent.py,sha256=t1i9XPihW7xrsYn6I86_QvCocVgHgIg0ORQ-ADcheeM,29132
16
16
  jarvis/jarvis_code_agent/lint.py,sha256=LZPsfyZPMo7Wm7LN4osZocuNJwZx1ojacO3MlF870x8,4009
17
17
  jarvis/jarvis_code_analysis/code_review.py,sha256=TMov1pqDe1bg0vM1ndnYeW9ejHrRN_jMroo3T4L9yag,32368
18
18
  jarvis/jarvis_code_analysis/checklists/__init__.py,sha256=LIXAYa1sW3l7foP6kohLWnE98I_EQ0T7z5bYKHq6rJA,78
@@ -35,7 +35,7 @@ jarvis/jarvis_code_analysis/checklists/shell.py,sha256=aRFYhQQvTgbYd-uY5pc8UHIUA
35
35
  jarvis/jarvis_code_analysis/checklists/sql.py,sha256=vR0T6qC7b4dURjJVAd7kSVxyvZEQXPG1Jqc2sNTGp5c,2355
36
36
  jarvis/jarvis_code_analysis/checklists/swift.py,sha256=TPx4I6Gupvs6tSerRKmTSKEPQpOLEbH2Y7LXg1uBgxc,2566
37
37
  jarvis/jarvis_code_analysis/checklists/web.py,sha256=25gGD7pDadZQybNFvALYxWvK0VRjGQb1NVJQElwjyk0,3943
38
- jarvis/jarvis_data/config_schema.json,sha256=87zzs16tQOzbkkg1y1j1zQHYSw1Awsl8grYa02DwNs8,10116
38
+ jarvis/jarvis_data/config_schema.json,sha256=ml7Z3dhjh227K1RsBPVTGJYSXFDG3p3OR-nTb_cxK88,10317
39
39
  jarvis/jarvis_data/tiktoken/9b5ad71b2ce5302211f9c61530b329a4922fc6a4,sha256=Ijkht27pm96ZW3_3OFE-7xAPtR0YyTWXoRO8_-hlsqc,1681126
40
40
  jarvis/jarvis_git_squash/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
41
41
  jarvis/jarvis_git_squash/main.py,sha256=6PECdAbTbrsJBRLK1pXBh4hdJ_LADh-XXSic1xJi97E,2255
@@ -73,11 +73,12 @@ jarvis/jarvis_smart_shell/main.py,sha256=ReCC9bWPlgl84ylI0uvdzlE3J6fS0XzFSLOpQQy
73
73
  jarvis/jarvis_stats/__init__.py,sha256=jJzgP43nxzLbNGs8Do4Jfta1PNCJMf1Oq9YTPd6EnFM,342
74
74
  jarvis/jarvis_stats/cli.py,sha256=KqLH-9Kd_YlBJSke3QXY90XnFmiH2kYkRacL8ygtSsM,12649
75
75
  jarvis/jarvis_stats/stats.py,sha256=qLyOJvWAv0fgV7oohAUSQ2W2E1Hr4wWgEQXDOiI-4Cg,17674
76
- jarvis/jarvis_stats/storage.py,sha256=SI6PwmXFy-Ryot-XQwZGHbTOuyKnxCwrN6hpuD_P5wg,12687
76
+ jarvis/jarvis_stats/storage.py,sha256=0hs-TkmvWavsf6J2LLOLXyyZzVK8g77jecRnt89MzYE,12724
77
77
  jarvis/jarvis_stats/visualizer.py,sha256=ZIBmGELzs6c7qM01tQql1HF6eFKn6HDGVQfKXRUUIY0,8529
78
78
  jarvis/jarvis_tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
79
79
  jarvis/jarvis_tools/ask_user.py,sha256=M6DdLNryCE8y1JcdZHEifUgZkPUEPNKc-zDW5p0Mb1k,2029
80
80
  jarvis/jarvis_tools/base.py,sha256=tFVmK6ppsImW2BzHZmrNmMRiOJdW-4aZP6Me3VxdYcA,1194
81
+ jarvis/jarvis_tools/clear_memory.py,sha256=HQMK70UJhhDgPPHozGaTpYizzQblUzYRwPbvD1z3z6o,8730
81
82
  jarvis/jarvis_tools/edit_file.py,sha256=hM345E9rxS-EkqCZpwwizL6fmPdTadtB798tEO5Ce3g,10417
82
83
  jarvis/jarvis_tools/execute_script.py,sha256=gMarE5yCCSPU6Dp6HlcL2KT-2xCzR-1p-oQNlYOJK58,6157
83
84
  jarvis/jarvis_tools/file_analyzer.py,sha256=aVe1jBSp0YmlypihxrGADJpYrU_7CxDETxGUNySuSlI,4044
@@ -86,29 +87,29 @@ jarvis/jarvis_tools/methodology.py,sha256=_K4GIDUodGEma3SvNRo7Qs5rliijgNespVLyAP
86
87
  jarvis/jarvis_tools/read_code.py,sha256=EnI-R-5HyIQYhMD391nZWXHIuHHBF-OJIRE0QpLcPX4,6417
87
88
  jarvis/jarvis_tools/read_webpage.py,sha256=NmDUboVZd4CGHBPRFK6dp3uqVhuGopW1bOi3TcaLDF4,2092
88
89
  jarvis/jarvis_tools/registry.py,sha256=8qhZgmmGIXJsYwtpsp_Ls8woT0qmBrthF8lIuQqOu7c,28614
89
- jarvis/jarvis_tools/retrieve_memory.py,sha256=CKmNym5FxHjgdc0my4IlsJ3TB3E2m0aCNFSRWjH6fKs,8221
90
+ jarvis/jarvis_tools/retrieve_memory.py,sha256=zItXPSMGzaPyH-4p_CP3z24fBYFnCK3OQJctgVunZMI,8288
90
91
  jarvis/jarvis_tools/rewrite_file.py,sha256=eG_WKg6cVAXmuGwUqlWkcuyay5S8DOzEi8vZCmX3O8w,7255
91
- jarvis/jarvis_tools/save_memory.py,sha256=4KRJWiyeyaIfdjeCAflPrxJ5ZXA4gUL3tv9h6d6sFKc,5309
92
+ jarvis/jarvis_tools/save_memory.py,sha256=DjeFb38OtK9Y_RpWYHz8vL72JdauXZTlc_Y0FUQBtiM,7486
92
93
  jarvis/jarvis_tools/search_web.py,sha256=zh6EYLQPIQneoz27Hheh-fifMeMNhrTVldXKMSsMz2Y,5801
93
94
  jarvis/jarvis_tools/virtual_tty.py,sha256=LTsg1PlsPvgaLShUaxpAKwTpyjXRr0l0qSREI7Q-fBc,26349
94
95
  jarvis/jarvis_tools/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
95
96
  jarvis/jarvis_tools/cli/main.py,sha256=GsfZJ4OS4Hvxh0H2XiLkgbzm-ajBsb4c0LyjuIAAatE,7718
96
97
  jarvis/jarvis_utils/__init__.py,sha256=67h0ldisGlh3oK4DAeNEL2Bl_VsI3tSmfclasyVlueM,850
97
98
  jarvis/jarvis_utils/builtin_replace_map.py,sha256=4BurljGuiG_I93EBs7mlFlPm9wYC_4CmdTG5tQWpF6g,1712
98
- jarvis/jarvis_utils/config.py,sha256=-ZgqIkzsp8VP5qsXke9XAGuwtAB9XlOAo_k4L6jhcrE,12904
99
+ jarvis/jarvis_utils/config.py,sha256=ESvxmUxbxX8B80ft1H2qQsdyu73xAYvr3Dql7CYnzwI,13178
99
100
  jarvis/jarvis_utils/embedding.py,sha256=oEOEM2qf16DMYwPsQe6srET9BknyjOdY2ef0jsp3Or8,2714
100
101
  jarvis/jarvis_utils/file_processors.py,sha256=XiM248SHS7lLgQDCbORVFWqinbVDUawYxWDOsLXDxP8,3043
101
102
  jarvis/jarvis_utils/git_utils.py,sha256=dkC0HcUdm_rF5vXNoLByne3mGykZEviD3Lo_SYbwROU,21667
102
103
  jarvis/jarvis_utils/globals.py,sha256=lpS1lmWRnLgqOeoyhZlktdZK9SK8YMekc6XWamho0Jw,8561
103
104
  jarvis/jarvis_utils/http.py,sha256=eRhV3-GYuWmQ0ogq9di9WMlQkFcVb1zGCrySnOgT1x0,4392
104
- jarvis/jarvis_utils/input.py,sha256=g0Xa1TNZHxLaYduREV_Wc55iqHD6djN73YFJbR83gUg,9488
105
- jarvis/jarvis_utils/methodology.py,sha256=EqS9feS3HHgNcKYoGs-Dkhp4pGwylwXt96r2J61cxUo,9223
106
- jarvis/jarvis_utils/output.py,sha256=E_J_RYXtkOgRiDSHCRE9QPHY8WQmmhIotQtIQru8GZA,10888
105
+ jarvis/jarvis_utils/input.py,sha256=KMAbSubWHo2z-gACf5PyQSGozJKXQby-fwcfzobJld0,9598
106
+ jarvis/jarvis_utils/methodology.py,sha256=I8BTBijpApzUtRG20_Wu1Vuv0I0OoYOzshec6CMOPX8,10231
107
+ jarvis/jarvis_utils/output.py,sha256=QRLlKObQKT0KuRSeZRqYb7NlTQvsd1oZXZ41WxeWEuU,10894
107
108
  jarvis/jarvis_utils/tag.py,sha256=f211opbbbTcSyzCDwuIK_oCnKhXPNK-RknYyGzY1yD0,431
108
109
  jarvis/jarvis_utils/utils.py,sha256=b7t2BsoPEU_3ZhippcQW9qwQD0PO-sGkjjtZ4DvwI2s,37056
109
- jarvis_ai_assistant-0.2.6.dist-info/licenses/LICENSE,sha256=AGgVgQmTqFvaztRtCAXsAMryUymB18gZif7_l2e1XOg,1063
110
- jarvis_ai_assistant-0.2.6.dist-info/METADATA,sha256=4XIqrWX0_tWmBC88XLFVlZGa2nzIbijiUxVW4g0y20I,16807
111
- jarvis_ai_assistant-0.2.6.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
112
- jarvis_ai_assistant-0.2.6.dist-info/entry_points.txt,sha256=8cwi1VxZGU5UeSZMFiH-jG6NK95Asjukj5SBLBrGiGo,1257
113
- jarvis_ai_assistant-0.2.6.dist-info/top_level.txt,sha256=1BOxyWfzOP_ZXj8rVTDnNCJ92bBGB0rwq8N1PCpoMIs,7
114
- jarvis_ai_assistant-0.2.6.dist-info/RECORD,,
110
+ jarvis_ai_assistant-0.2.7.dist-info/licenses/LICENSE,sha256=AGgVgQmTqFvaztRtCAXsAMryUymB18gZif7_l2e1XOg,1063
111
+ jarvis_ai_assistant-0.2.7.dist-info/METADATA,sha256=NQFGWTpKdTU1HU9yjEjoFwSYz7DiAulBpVnB70WZDkg,16807
112
+ jarvis_ai_assistant-0.2.7.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
113
+ jarvis_ai_assistant-0.2.7.dist-info/entry_points.txt,sha256=8cwi1VxZGU5UeSZMFiH-jG6NK95Asjukj5SBLBrGiGo,1257
114
+ jarvis_ai_assistant-0.2.7.dist-info/top_level.txt,sha256=1BOxyWfzOP_ZXj8rVTDnNCJ92bBGB0rwq8N1PCpoMIs,7
115
+ jarvis_ai_assistant-0.2.7.dist-info/RECORD,,