auto-coder 0.1.326__py3-none-any.whl → 0.1.328__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of auto-coder might be problematic. Click here for more details.

@@ -108,7 +108,7 @@ class ActiveContextManager:
108
108
  global_logger.configure(
109
109
  handlers=[
110
110
  # 移除控制台输出,只保留文件输出
111
- # 文件 Handler(仅处理 DirectoryMapper)
111
+ # 文件 Handler
112
112
  {
113
113
  "sink": log_file,
114
114
  "level": "INFO",
@@ -117,12 +117,12 @@ class ActiveContextManager:
117
117
  "format": "{time:YYYY-MM-DD HH:mm:ss} | {level} | {name} | {message}",
118
118
  "filter": lambda record: record["extra"].get("name") in ["DirectoryMapper", "ActiveContextManager","ActivePackage","AsyncProcessor"]
119
119
  },
120
- # 控制台 Handler(排除 DirectoryMapper)
120
+ # 控制台 Handler
121
121
  {
122
122
  "sink": sys.stdout,
123
123
  "level": "INFO",
124
124
  "format": "{time:YYYY-MM-DD HH:mm:ss} | {name} | {message}",
125
- "filter": lambda record: record["extra"].get("name") not in ["ActiveContextManager"]
125
+ "filter": lambda record: record["extra"].get("name") not in ["DirectoryMapper", "ActiveContextManager","ActivePackage","AsyncProcessor","TokenCostCalculator"]
126
126
  }
127
127
  ]
128
128
  )
@@ -192,7 +192,7 @@ class ActivePackage:
192
192
  end_time_current_change = time.monotonic()
193
193
 
194
194
  # 使用TokenCostCalculator跟踪token使用情况
195
- token_calculator = TokenCostCalculator(logger_name="ActivePackage.TokenCost",args=args)
195
+ token_calculator = TokenCostCalculator(logger_name="ActivePackage",args=args)
196
196
  current_change_stats: TokenUsageStats = token_calculator.track_token_usage(
197
197
  llm=self.llm,
198
198
  meta_holder=meta_holder_current_change,
@@ -316,7 +316,7 @@ class ActivePackage:
316
316
  end_time_current_change = time.monotonic()
317
317
 
318
318
  # 使用TokenCostCalculator跟踪token使用情况
319
- token_calculator = TokenCostCalculator(logger_name="ActivePackage.TokenCost",args=args)
319
+ token_calculator = TokenCostCalculator(logger_name="ActivePackage",args=args)
320
320
  update_current_change_stats: TokenUsageStats = token_calculator.track_token_usage(
321
321
  llm=self.llm,
322
322
  meta_holder=meta_holder_current_change,
@@ -10,16 +10,18 @@ class ShadowManager:
10
10
  如果提供了event_file_id,则影子文件存储在<source_dir>/.auto-coder/shadows/<event_file_id>/中。
11
11
  """
12
12
 
13
- def __init__(self, source_dir, event_file_id=None):
13
+ def __init__(self, source_dir, event_file_id=None, ignore_clean_shadows=False):
14
14
  """
15
15
  使用项目根目录初始化。
16
16
 
17
17
  参数:
18
18
  source_dir (str): 项目根目录的绝对路径。
19
19
  event_file_id (str, optional): 事件文件ID,用于创建特定的影子目录。
20
+ ignore_clean_shadows (bool, optional): 是否忽略清理影子目录。
20
21
  """
21
22
  self.source_dir = os.path.abspath(source_dir)
22
-
23
+ self.ignore_clean_shadows = ignore_clean_shadows
24
+ self.event_file_id = None
23
25
  # # 根据是否提供了event_file_id来确定shadows_dir的路径
24
26
  # if event_file_id:
25
27
  # print("======" + event_file_id)
@@ -28,12 +30,25 @@ class ShadowManager:
28
30
 
29
31
  if event_file_id:
30
32
  event_file_id = self.get_event_file_id_from_path(event_file_id)
33
+ self.event_file_id = event_file_id
31
34
  self.shadows_dir = os.path.join(self.source_dir, '.auto-coder', 'shadows', event_file_id)
32
35
  else:
33
36
  self.shadows_dir = os.path.join(self.source_dir, '.auto-coder', 'shadows')
34
-
37
+
35
38
  # 确保影子目录存在
36
39
  os.makedirs(self.shadows_dir, exist_ok=True)
40
+
41
+ # 确保链接项目目录存在
42
+ link_projects_dir = os.path.join(self.source_dir, '.auto-coder', 'shadows', 'link_projects')
43
+ source_basename = os.path.basename(self.source_dir)
44
+ os.makedirs(link_projects_dir, exist_ok=True)
45
+ if self.event_file_id:
46
+ self.link_projects_dir = os.path.join(link_projects_dir, self.event_file_id, source_basename)
47
+ else:
48
+ self.link_projects_dir = os.path.join(link_projects_dir, source_basename)
49
+
50
+ os.makedirs(self.link_projects_dir, exist_ok=True)
51
+
37
52
 
38
53
  def get_event_file_id_from_path(self, path):
39
54
  """
@@ -222,6 +237,9 @@ class ShadowManager:
222
237
  返回:
223
238
  bool: 操作成功则为True,否则为False
224
239
  """
240
+ if self.ignore_clean_shadows:
241
+ return True
242
+
225
243
  if not os.path.exists(self.shadows_dir):
226
244
  return True
227
245
 
@@ -237,4 +255,233 @@ class ShadowManager:
237
255
  return True
238
256
  except Exception as e:
239
257
  print(f"清理影子目录时出错: {str(e)}")
240
- return False
258
+ return False
259
+
260
+ def create_link_project(self):
261
+ """
262
+ 创建链接项目,该项目是源目录的一个特殊副本,
263
+ 其中优先使用影子目录中的文件,如果影子目录中不存在则使用源目录中的文件。
264
+
265
+ 返回:
266
+ str: 链接项目的路径
267
+ """
268
+ # 清理链接项目目录
269
+ self._clean_link_project_dir()
270
+ # 创建链接项目
271
+ self._create_links(self.source_dir, self.link_projects_dir)
272
+ return self.link_projects_dir
273
+
274
+ def _clean_link_project_dir(self):
275
+ """
276
+ 清理链接项目目录中的所有内容,但保留目录本身。
277
+
278
+ 返回:
279
+ bool: 操作成功则为True,否则为False
280
+ """
281
+ if not os.path.exists(self.link_projects_dir):
282
+ return True
283
+
284
+ try:
285
+ # 删除链接项目目录中的所有内容
286
+ for item in os.listdir(self.link_projects_dir):
287
+ item_path = os.path.join(self.link_projects_dir, item)
288
+ if os.path.isfile(item_path):
289
+ os.unlink(item_path)
290
+ elif os.path.isdir(item_path):
291
+ shutil.rmtree(item_path)
292
+
293
+ return True
294
+ except Exception as e:
295
+ print(f"清理链接项目目录时出错: {str(e)}")
296
+ return False
297
+
298
+ def _create_links(self, source_path, link_path, rel_path=''):
299
+ """
300
+ 递归创建从源目录到链接项目目录的链接
301
+
302
+ 参数:
303
+ source_path: 当前处理的源目录路径
304
+ link_path: 对应的链接项目目录路径
305
+ rel_path: 相对于根源目录的相对路径
306
+ """
307
+ # 获取源目录中的所有项目
308
+ for item in os.listdir(source_path):
309
+ # 跳过.auto-coder目录
310
+ if item in ['.auto-coder', '.git']:
311
+ continue
312
+
313
+ source_item_path = os.path.join(source_path, item)
314
+ link_item_path = os.path.join(link_path, item)
315
+ current_rel_path = os.path.join(rel_path, item) if rel_path else item
316
+
317
+ # 我们相当于遍历了所有目录,遇到 shadow_dir 和 source_dir 同时存在:
318
+ # 则创建目录,遍历里面的文件,如果文件出现在shadow_dir里,则软链到shadow_dir,否则软链到source_dir里。
319
+ # 如果目录不同时存在,则直接创建到 source_dir的软链。这样就能确保 link_project 和 source_dir 的结构完全一致。
320
+ if os.path.isdir(source_item_path):
321
+ # 构建在shadows_dir中可能存在的对应路径
322
+ shadow_dir_path = os.path.join(self.shadows_dir, current_rel_path)
323
+
324
+ # 2.1 如果目录在shadows_dir中存在
325
+ if os.path.exists(shadow_dir_path) and os.path.isdir(shadow_dir_path):
326
+ # 创建对应的目录结构
327
+ os.makedirs(link_item_path, exist_ok=True)
328
+
329
+ # 遍历源目录中的文件
330
+ for file_item in os.listdir(source_item_path):
331
+ source_file_path = os.path.join(source_item_path, file_item)
332
+ link_file_path = os.path.join(link_item_path, file_item)
333
+ shadow_file_path = os.path.join(shadow_dir_path, file_item)
334
+
335
+ # 只处理文件,不处理子目录
336
+ if os.path.isfile(source_file_path):
337
+ # 检查链接文件是否已存在,如果存在则删除
338
+ if os.path.exists(link_file_path):
339
+ os.remove(link_file_path)
340
+
341
+ # 如果文件在shadows_dir中存在,链接到shadows_dir中的文件
342
+ if os.path.exists(shadow_file_path) and os.path.isfile(shadow_file_path):
343
+ os.symlink(shadow_file_path, link_file_path)
344
+ # 否则链接到源目录中的文件
345
+ else:
346
+ os.symlink(source_file_path, link_file_path)
347
+
348
+ # 递归处理子目录
349
+ self._create_links(source_item_path, link_item_path, current_rel_path)
350
+
351
+ # 2.2 如果目录在shadows_dir中不存在,直接创建软链接
352
+ else:
353
+ # 检查链接是否已存在,如果存在则删除
354
+ if os.path.exists(link_item_path):
355
+ if os.path.islink(link_item_path):
356
+ os.unlink(link_item_path)
357
+ elif os.path.isdir(link_item_path):
358
+ shutil.rmtree(link_item_path)
359
+ else:
360
+ os.remove(link_item_path)
361
+
362
+ os.symlink(source_item_path, link_item_path)
363
+
364
+ # 处理第一层级文件
365
+ elif os.path.isfile(source_item_path):
366
+ # 构建在shadows_dir中可能存在的对应文件路径
367
+ shadow_file_path = os.path.join(self.shadows_dir, current_rel_path)
368
+
369
+ # 检查链接文件是否已存在,如果存在则删除
370
+ if os.path.exists(link_item_path):
371
+ os.remove(link_item_path)
372
+
373
+ # 如果文件在shadows_dir中存在,链接到shadows_dir中的文件
374
+ if os.path.exists(shadow_file_path) and os.path.isfile(shadow_file_path):
375
+ os.symlink(shadow_file_path, link_item_path)
376
+ # 否则链接到源目录中的文件
377
+ else:
378
+ os.symlink(source_item_path, link_item_path)
379
+
380
+ def compare_directories(self):
381
+ """
382
+ 比较源目录和链接项目目录之间的差异,并打印出来。
383
+
384
+ 返回:
385
+ tuple: (源目录独有的文件/目录列表, 链接项目独有的文件/目录列表, 同时存在但类型不同的项目列表)
386
+ """
387
+ # 确保链接项目目录存在
388
+ if not os.path.exists(self.link_projects_dir):
389
+ print(f"链接项目目录不存在: {self.link_projects_dir}")
390
+ return [], [], []
391
+
392
+ source_only = [] # 只在源目录存在的文件/目录
393
+ link_only = [] # 只在链接项目存在的文件/目录
394
+ type_diff = [] # 类型不同的文件/目录
395
+
396
+ # 递归比较目录结构
397
+ self._compare_dir_recursive(self.source_dir, self.link_projects_dir, "", source_only, link_only, type_diff)
398
+
399
+ # 打印差异结果
400
+ print("\n目录结构比较结果:")
401
+
402
+ if not (source_only or link_only or type_diff):
403
+ print("✅ 目录结构完全一致")
404
+ return source_only, link_only, type_diff
405
+
406
+ if source_only:
407
+ print("\n🔍 仅在源目录中存在:")
408
+ for item in source_only:
409
+ print(f" - {item}")
410
+
411
+ if link_only:
412
+ print("\n🔍 仅在链接项目中存在:")
413
+ for item in link_only:
414
+ print(f" - {item}")
415
+
416
+ if type_diff:
417
+ print("\n🔍 同名但类型不同的项目:")
418
+ for item in type_diff:
419
+ print(f" - {item}")
420
+
421
+ return source_only, link_only, type_diff
422
+
423
+ def _compare_dir_recursive(self, source_path, link_path, rel_path, source_only, link_only, type_diff):
424
+ """
425
+ 递归比较两个目录的差异
426
+
427
+ 参数:
428
+ source_path: 源目录路径
429
+ link_path: 链接项目目录路径
430
+ rel_path: 当前处理的相对路径
431
+ source_only: 仅在源目录存在的项目列表
432
+ link_only: 仅在链接项目存在的项目列表
433
+ type_diff: 同名但类型不同的项目列表
434
+ """
435
+ # 获取源目录和链接项目目录中的所有项目
436
+ try:
437
+ source_items = set(os.listdir(source_path))
438
+ except (FileNotFoundError, PermissionError):
439
+ source_items = set()
440
+
441
+ try:
442
+ link_items = set(os.listdir(link_path))
443
+ except (FileNotFoundError, PermissionError):
444
+ link_items = set()
445
+
446
+ # 忽略特定目录
447
+ ignored_dirs = ['.auto-coder', '.git']
448
+ source_items = {item for item in source_items if item not in ignored_dirs}
449
+ link_items = {item for item in link_items if item not in ignored_dirs}
450
+
451
+ # 找出仅在源目录中存在的项目
452
+ for item in source_items - link_items:
453
+ item_rel_path = os.path.join(rel_path, item) if rel_path else item
454
+ source_only.append(item_rel_path)
455
+
456
+ # 找出仅在链接项目中存在的项目
457
+ for item in link_items - source_items:
458
+ item_rel_path = os.path.join(rel_path, item) if rel_path else item
459
+ link_only.append(item_rel_path)
460
+
461
+ # 比较同时存在的项目
462
+ for item in source_items.intersection(link_items):
463
+ # 跳过忽略的目录
464
+ if item in ignored_dirs:
465
+ continue
466
+
467
+ source_item_path = os.path.join(source_path, item)
468
+ link_item_path = os.path.join(link_path, item)
469
+ item_rel_path = os.path.join(rel_path, item) if rel_path else item
470
+
471
+ # 如果类型不同(一个是文件,一个是目录)
472
+ source_is_dir = os.path.isdir(source_item_path)
473
+ link_is_dir = os.path.isdir(link_item_path)
474
+
475
+ if source_is_dir != link_is_dir:
476
+ type_description = f"{item_rel_path} (源: {'目录' if source_is_dir else '文件'}, 链接: {'目录' if link_is_dir else '文件'})"
477
+ type_diff.append(type_description)
478
+ elif source_is_dir and link_is_dir:
479
+ # 如果都是目录,递归比较
480
+ self._compare_dir_recursive(
481
+ source_item_path,
482
+ link_item_path,
483
+ item_rel_path,
484
+ source_only,
485
+ link_only,
486
+ type_diff
487
+ )
autocoder/version.py CHANGED
@@ -1 +1 @@
1
- __version__ = "0.1.326"
1
+ __version__ = "0.1.328"