autocoder-nano 0.1.33__py3-none-any.whl → 0.1.34__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.
@@ -5,7 +5,6 @@ import os
5
5
  import json
6
6
  import shutil
7
7
  import subprocess
8
- import textwrap
9
8
  import time
10
9
  import uuid
11
10
 
@@ -18,6 +17,7 @@ from autocoder_nano.index.index_manager import IndexManager
18
17
  from autocoder_nano.index.symbols_utils import extract_symbols
19
18
  from autocoder_nano.llm_client import AutoLLM
20
19
  from autocoder_nano.rules.rules_learn import AutoRulesLearn
20
+ from autocoder_nano.utils.completer_utils import CommandCompleter
21
21
  from autocoder_nano.version import __version__
22
22
  from autocoder_nano.llm_types import *
23
23
  from autocoder_nano.llm_prompt import prompt, extract_code
@@ -29,23 +29,20 @@ from autocoder_nano.project import PyProject, SuffixProject
29
29
  from autocoder_nano.utils.printer_utils import Printer
30
30
 
31
31
  import yaml
32
- # import tabulate
33
32
  from jinja2 import Template
34
- # from loguru import logger
35
33
  from prompt_toolkit import prompt as _toolkit_prompt, PromptSession
36
- from prompt_toolkit.completion import Completer, Completion
37
34
  from prompt_toolkit.auto_suggest import AutoSuggestFromHistory
38
35
  from prompt_toolkit.formatted_text import FormattedText
39
36
  from prompt_toolkit.history import InMemoryHistory
40
37
  from prompt_toolkit.key_binding import KeyBindings
41
38
  from prompt_toolkit.shortcuts import confirm
42
39
  from prompt_toolkit.styles import Style
43
- from rich.console import Console
40
+ # from rich.console import Console
44
41
  from rich.live import Live
45
42
  from rich.markdown import Markdown
46
43
  from rich.panel import Panel
47
44
  from rich.syntax import Syntax
48
- from rich.table import Table
45
+ # from rich.table import Table
49
46
  from rich.text import Text
50
47
 
51
48
 
@@ -181,604 +178,6 @@ def find_files_in_project(patterns: List[str]) -> List[str]:
181
178
  return list(set(matched_files))
182
179
 
183
180
 
184
- COMMANDS = {
185
- "/add_files": {
186
- "/group": {"/add": "", "/drop": "", "/reset": ""},
187
- "/refresh": "",
188
- },
189
- "/remove_files": {"/all": ""},
190
- "/coding": {"/apply": ""},
191
- "/chat": {"/history": "", "/new": "", "/review": ""},
192
- "/models": {"/add_model": "", "/remove": "", "/list": "", "/check": ""},
193
- "/help": {
194
- "/add_files": "",
195
- "/remove_files": "",
196
- "/chat": "",
197
- "/coding": "",
198
- "/commit": "",
199
- "/conf": "",
200
- "/mode": "",
201
- "/models": ""
202
- },
203
- "/exclude_files": {"/list": "", "/drop": ""},
204
- "/exclude_dirs": {},
205
- "/rules": {"/list": "", "/show": "", "/remove": "", "/analyze": "", "/commit": ""}
206
- }
207
-
208
-
209
- class CommandTextParser:
210
- def __init__(self, text: str, command: str):
211
- self.text = text
212
- self.pos = -1
213
- self.len = len(text)
214
- self.is_extracted = False
215
- self.current_word_start_pos = 0
216
- self.current_word_end_pos = 0
217
- self.in_current_sub_command = ""
218
- self.completions = []
219
- self.command = command
220
- self.current_hiararchy = COMMANDS[command]
221
- self.sub_commands = []
222
- self.tags = []
223
-
224
- def first_sub_command(self):
225
- if len(self.sub_commands) == 0:
226
- return None
227
- return self.sub_commands[0]
228
-
229
- def last_sub_command(self):
230
- if len(self.sub_commands) == 0:
231
- return None
232
- return self.sub_commands[-1]
233
-
234
- def peek(self):
235
- if self.pos + 1 < self.len:
236
- return self.text[self.pos + 1]
237
- return None
238
-
239
- def peek2(self):
240
- if self.pos + 2 < self.len:
241
- return self.text[self.pos + 2]
242
- return None
243
-
244
- def peek3(self):
245
- if self.pos + 3 < self.len:
246
- return self.text[self.pos + 3]
247
- return None
248
-
249
- def next(self):
250
- if self.pos < self.len - 1:
251
- self.pos += 1
252
- char = self.text[self.pos]
253
- return char
254
- return None
255
-
256
- def consume_blank(self):
257
- while self.peek() == "\n" or self.peek() == " " or self.peek() == "\t" or self.peek() == "\r":
258
- self.next()
259
-
260
- def is_blank(self) -> bool:
261
- return self.peek() == "\n" or self.peek() == " " or self.peek() == "\t" or self.peek() == "\r"
262
-
263
- def is_sub_command(self) -> bool | None:
264
- backup_pos = self.pos
265
- self.consume_blank()
266
- try:
267
- if self.peek() == "/":
268
- current_sub_command = ""
269
- while self.peek() is not None and self.peek() != " " and self.peek() != "\n":
270
- current_sub_command += self.next()
271
-
272
- if current_sub_command.count("/") > 1:
273
- self.pos = backup_pos
274
- return False
275
- return True
276
- return False
277
- finally:
278
- self.pos = backup_pos
279
-
280
- def consume_sub_command(self) -> str:
281
- # backup_pos = self.pos
282
- self.consume_blank()
283
- current_sub_command = ""
284
- while self.peek() is not None and self.peek() != " " and self.peek() != "\n":
285
- current_sub_command += self.next()
286
-
287
- if self.peek() is None:
288
- self.is_extracted = True
289
- self.current_word_end_pos = self.pos + 1
290
- self.current_word_start_pos = self.current_word_end_pos - len(
291
- current_sub_command
292
- )
293
- self.in_current_sub_command = current_sub_command
294
- else:
295
- if current_sub_command in self.current_hiararchy:
296
- self.current_hiararchy = self.current_hiararchy[current_sub_command]
297
- self.sub_commands.append(current_sub_command)
298
-
299
- return current_sub_command
300
-
301
- def consume_command_value(self):
302
- current_word = ""
303
- while self.peek() is not None:
304
- v = self.next()
305
- if v == " ":
306
- current_word = ""
307
- else:
308
- current_word += v
309
- self.is_extracted = True
310
- self.current_word_end_pos = self.pos + 1
311
- self.current_word_start_pos = self.current_word_end_pos - len(current_word)
312
-
313
- def previous(self):
314
- if self.pos > 1:
315
- return self.text[self.pos - 1]
316
- return None
317
-
318
- def is_start_tag(self) -> bool | None:
319
- backup_pos = self.pos
320
- tag = ""
321
- try:
322
- if self.peek() == "<" and self.peek2() != "/":
323
- while (
324
- self.peek() is not None
325
- and self.peek() != ">"
326
- and not self.is_blank()
327
- ):
328
- tag += self.next()
329
- if self.peek() == ">":
330
- tag += self.next()
331
- return True
332
- else:
333
- return False
334
- return False
335
- finally:
336
- self.pos = backup_pos
337
-
338
- def consume_tag(self):
339
- start_tag = ""
340
- content = ""
341
- end_tag = ""
342
-
343
- # consume start tag
344
- self.current_word_start_pos = self.pos + 1
345
- while self.peek() is not None and self.peek() != ">" and not self.is_blank():
346
- start_tag += self.next()
347
- if self.peek() == ">":
348
- start_tag += self.next()
349
- self.current_word_end_pos = self.pos + 1
350
- tag = Tag(start_tag=start_tag, content=content, end_tag=end_tag)
351
- self.tags.append(tag)
352
-
353
- # consume content
354
- self.current_word_start_pos = self.pos + 1
355
- while self.peek() is not None and not (
356
- self.peek() == "<" and self.peek2() == "/"
357
- ):
358
- content += self.next()
359
-
360
- tag.content = content
361
- self.current_word_end_pos = self.pos + 1
362
-
363
- # consume end tag
364
- self.current_word_start_pos = self.pos + 1
365
- if self.peek() == "<" and self.peek2() == "/":
366
- while (
367
- self.peek() is not None and self.peek() != ">" and not self.is_blank()
368
- ):
369
- end_tag += self.next()
370
- if self.peek() == ">":
371
- end_tag += self.next()
372
- tag.end_tag = end_tag
373
- self.current_word_end_pos = self.pos + 1
374
-
375
- # check is finished
376
- if self.peek() is None:
377
- self.is_extracted = True
378
-
379
- def consume_coding_value(self):
380
- current_word = ""
381
- while self.peek() is not None and not self.is_start_tag():
382
- v = self.next()
383
- if v == " ":
384
- current_word = ""
385
- else:
386
- current_word += v
387
- if self.peek() is None:
388
- self.is_extracted = True
389
-
390
- self.current_word_end_pos = self.pos + 1
391
- self.current_word_start_pos = self.current_word_end_pos - len(current_word)
392
-
393
- def current_word(self) -> str:
394
- return self.text[self.current_word_start_pos: self.current_word_end_pos]
395
-
396
- def get_current_word(self) -> str:
397
- return self.current_word()
398
-
399
- def get_sub_commands(self) -> list[str]:
400
- if self.get_current_word() and not self.get_current_word().startswith("/"):
401
- return []
402
-
403
- if isinstance(self.current_hiararchy, str):
404
- return []
405
-
406
- return [item for item in list(self.current_hiararchy.keys()) if item]
407
-
408
- def add_files(self):
409
- """
410
- for exmaple:
411
- /add_files file1 file2 file3
412
- /add_files /group/abc/cbd /group/abc/bc2
413
- /add_files /group1 /add xxxxx
414
- /add_files /group
415
- /add_files /group /add <groupname>
416
- /add_files /group /drop <groupname>
417
- /add_files /group <groupname>,<groupname>
418
- /add_files /refresh
419
- """
420
- while True:
421
- if self.pos == self.len - 1:
422
- break
423
- elif self.is_extracted:
424
- break
425
- elif self.is_sub_command():
426
- self.consume_sub_command()
427
- else:
428
- self.consume_command_value()
429
- return self
430
-
431
- def coding(self):
432
- while True:
433
- if self.pos == self.len - 1:
434
- break
435
- elif self.is_extracted:
436
- break
437
- elif self.is_sub_command():
438
- self.consume_sub_command()
439
- elif self.is_start_tag():
440
- self.consume_tag()
441
- else:
442
- self.consume_coding_value()
443
-
444
-
445
- class CommandCompleter(Completer):
446
- def __init__(self, _commands):
447
- self.commands = _commands
448
- self.all_file_names = get_all_file_names_in_project()
449
- self.all_files = get_all_file_in_project()
450
- self.all_dir_names = get_all_dir_names_in_project()
451
- self.all_files_with_dot = get_all_file_in_project_with_dot()
452
- self.symbol_list = get_symbol_list()
453
- self.current_file_names = []
454
-
455
- def get_completions(self, document, complete_event):
456
- text = document.text_before_cursor
457
- words = text.split()
458
-
459
- if len(words) > 0:
460
- if words[0] == "/mode":
461
- left_word = text[len("/mode"):]
462
- for mode in ["normal", "auto_detect"]:
463
- if mode.startswith(left_word.strip()):
464
- yield Completion(mode, start_position=-len(left_word.strip()))
465
-
466
- if words[0] == "/add_files":
467
- new_text = text[len("/add_files"):]
468
- parser = CommandTextParser(new_text, words[0])
469
- parser.add_files()
470
- current_word = parser.current_word()
471
-
472
- if parser.last_sub_command() == "/refresh":
473
- return
474
-
475
- for command in parser.get_sub_commands():
476
- if command.startswith(current_word):
477
- yield Completion(command, start_position=-len(current_word))
478
-
479
- if parser.first_sub_command() == "/group" and (
480
- parser.last_sub_command() == "/group"
481
- or parser.last_sub_command() == "/drop"
482
- ):
483
- group_names = memory["current_files"]["groups"].keys()
484
- if "," in current_word:
485
- current_word = current_word.split(",")[-1]
486
-
487
- for group_name in group_names:
488
- if group_name.startswith(current_word):
489
- yield Completion(
490
- group_name, start_position=-len(current_word)
491
- )
492
-
493
- if parser.first_sub_command() != "/group":
494
- if current_word and current_word.startswith("."):
495
- for file_name in self.all_files_with_dot:
496
- if file_name.startswith(current_word):
497
- yield Completion(file_name, start_position=-len(current_word))
498
- else:
499
- for file_name in self.all_file_names:
500
- if file_name.startswith(current_word):
501
- yield Completion(file_name, start_position=-len(current_word))
502
- for file_name in self.all_files:
503
- if current_word and current_word in file_name:
504
- yield Completion(file_name, start_position=-len(current_word))
505
-
506
- elif words[0] in ["/chat", "/coding"]:
507
- image_extensions = (
508
- ".png", ".jpg", ".jpeg", ".gif", ".bmp", ".tiff", ".tif", ".webp", ".svg", ".ico",
509
- ".heic", ".heif", ".raw", ".cr2", ".nef", ".arw", ".dng", ".orf", ".rw2", ".pef",
510
- ".srw", ".eps", ".ai", ".psd", ".xcf",
511
- )
512
- new_text = text[len(words[0]):]
513
- parser = CommandTextParser(new_text, words[0])
514
-
515
- parser.coding()
516
- current_word = parser.current_word()
517
-
518
- if len(new_text.strip()) == 0 or new_text.strip() == "/":
519
- for command in parser.get_sub_commands():
520
- if command.startswith(current_word):
521
- yield Completion(command, start_position=-len(current_word))
522
-
523
- all_tags = parser.tags
524
-
525
- if current_word.startswith("@"):
526
- name = current_word[1:]
527
- target_set = set()
528
-
529
- for file_name in self.current_file_names:
530
- base_file_name = os.path.basename(file_name)
531
- if name in base_file_name:
532
- target_set.add(base_file_name)
533
- path_parts = file_name.split(os.sep)
534
- display_name = (
535
- os.sep.join(path_parts[-3:])
536
- if len(path_parts) > 3
537
- else file_name
538
- )
539
- relative_path = os.path.relpath(
540
- file_name, project_root)
541
- yield Completion(
542
- relative_path,
543
- start_position=-len(name),
544
- display=f"{display_name} (in active files)",
545
- )
546
-
547
- for file_name in self.all_file_names:
548
- if file_name.startswith(name) and file_name not in target_set:
549
- target_set.add(file_name)
550
-
551
- path_parts = file_name.split(os.sep)
552
- display_name = (
553
- os.sep.join(path_parts[-3:])
554
- if len(path_parts) > 3
555
- else file_name
556
- )
557
- relative_path = os.path.relpath(
558
- file_name, project_root)
559
-
560
- yield Completion(
561
- relative_path,
562
- start_position=-len(name),
563
- display=f"{display_name}",
564
- )
565
-
566
- for file_name in self.all_files:
567
- if name in file_name and file_name not in target_set:
568
- path_parts = file_name.split(os.sep)
569
- display_name = (
570
- os.sep.join(path_parts[-3:])
571
- if len(path_parts) > 3
572
- else file_name
573
- )
574
- relative_path = os.path.relpath(
575
- file_name, project_root)
576
- yield Completion(
577
- relative_path,
578
- start_position=-len(name),
579
- display=f"{display_name}",
580
- )
581
-
582
- if current_word.startswith("@@"):
583
- name = current_word[2:]
584
- for symbol in self.symbol_list:
585
- if name in symbol.symbol_name:
586
- file_name = symbol.file_name
587
- path_parts = file_name.split(os.sep)
588
- display_name = (
589
- os.sep.join(path_parts[-3:])
590
- if len(path_parts) > 3
591
- else symbol.symbol_name
592
- )
593
- relative_path = os.path.relpath(
594
- file_name, project_root)
595
- yield Completion(
596
- f"{symbol.symbol_name}(location: {relative_path})",
597
- start_position=-len(name),
598
- display=f"{symbol.symbol_name} ({display_name}/{symbol.symbol_type})",
599
- )
600
-
601
- tags = [tag for tag in parser.tags]
602
-
603
- if current_word.startswith("<"):
604
- name = current_word[1:]
605
- for tag in ["<img>", "</img>"]:
606
- if all_tags and all_tags[-1].start_tag == "<img>":
607
- if tag.startswith(name):
608
- yield Completion(
609
- "</img>", start_position=-len(current_word)
610
- )
611
- elif tag.startswith(name):
612
- yield Completion(tag, start_position=-len(current_word))
613
-
614
- if tags and tags[-1].start_tag == "<img>" and tags[-1].end_tag == "":
615
- raw_file_name = tags[0].content
616
- file_name = raw_file_name.strip()
617
- parent_dir = os.path.dirname(file_name)
618
- file_basename = os.path.basename(file_name)
619
- search_dir = parent_dir if parent_dir else "."
620
- for root, dirs, files in os.walk(search_dir):
621
- # 只处理直接子目录
622
- if root != search_dir:
623
- continue
624
-
625
- # 补全子目录
626
- for _dir in dirs:
627
- full_path = os.path.join(root, _dir)
628
- if full_path.startswith(file_name):
629
- relative_path = os.path.relpath(full_path, search_dir)
630
- yield Completion(relative_path, start_position=-len(file_basename))
631
-
632
- # 补全文件
633
- for file in files:
634
- if file.lower().endswith(
635
- image_extensions
636
- ) and file.startswith(file_basename):
637
- full_path = os.path.join(root, file)
638
- relative_path = os.path.relpath(full_path, search_dir)
639
- yield Completion(
640
- relative_path,
641
- start_position=-len(file_basename),
642
- )
643
-
644
- # 只处理一层子目录,然后退出循环
645
- break
646
-
647
- elif words[0] == "/remove_files":
648
- new_words = text[len("/remove_files"):].strip().split(",")
649
-
650
- is_at_space = text[-1] == " "
651
- last_word = new_words[-2] if len(new_words) > 1 else ""
652
- current_word = new_words[-1] if new_words else ""
653
-
654
- if is_at_space:
655
- last_word = current_word
656
- current_word = ""
657
-
658
- # /remove_files /all [cursor] or /remove_files /all p[cursor]
659
- if not last_word and not current_word:
660
- if "/all".startswith(current_word):
661
- yield Completion("/all", start_position=-len(current_word))
662
- for file_name in self.current_file_names:
663
- yield Completion(file_name, start_position=-len(current_word))
664
-
665
- # /remove_files /a[cursor] or /remove_files p[cursor]
666
- if current_word:
667
- if "/all".startswith(current_word):
668
- yield Completion("/all", start_position=-len(current_word))
669
- for file_name in self.current_file_names:
670
- if current_word and current_word in file_name:
671
- yield Completion(
672
- file_name, start_position=-len(current_word)
673
- )
674
-
675
- elif words[0] == "/exclude_dirs":
676
- new_words = text[len("/exclude_dirs"):].strip().split(",")
677
- current_word = new_words[-1]
678
-
679
- for file_name in self.all_dir_names:
680
- if current_word and current_word in file_name:
681
- yield Completion(file_name, start_position=-len(current_word))
682
-
683
- elif words[0] == "/exclude_files":
684
- new_text = text[len("/exclude_files"):]
685
- parser = CommandTextParser(new_text, words[0])
686
- parser.add_files()
687
- current_word = parser.current_word()
688
- for command in parser.get_sub_commands():
689
- if command.startswith(current_word):
690
- yield Completion(command, start_position=-len(current_word))
691
-
692
- elif words[0] == "/models":
693
- new_text = text[len("/models"):]
694
- parser = CommandTextParser(new_text, words[0])
695
- parser.add_files()
696
- current_word = parser.current_word()
697
- for command in parser.get_sub_commands():
698
- if command.startswith(current_word):
699
- yield Completion(command, start_position=-len(current_word))
700
-
701
- elif words[0] == "/help":
702
- new_text = text[len("/help"):]
703
- parser = CommandTextParser(new_text, words[0])
704
- parser.add_files()
705
- current_word = parser.current_word()
706
- for command in parser.get_sub_commands():
707
- if command.startswith(current_word):
708
- yield Completion(command, start_position=-len(current_word))
709
-
710
- elif words[0] == "/rules":
711
- new_text = text[len("/rules"):]
712
- parser = CommandTextParser(new_text, words[0])
713
- parser.add_files()
714
- current_word = parser.current_word()
715
- for command in parser.get_sub_commands():
716
- if command.startswith(current_word):
717
- yield Completion(command, start_position=-len(current_word))
718
-
719
- elif words[0] == "/conf":
720
- new_words = text[len("/conf"):].strip().split()
721
- is_at_space = text[-1] == " "
722
- last_word = new_words[-2] if len(new_words) > 1 else ""
723
- current_word = new_words[-1] if new_words else ""
724
- completions = []
725
-
726
- if is_at_space:
727
- last_word = current_word
728
- current_word = ""
729
-
730
- # /conf /drop [curor] or /conf /drop p[cursor]
731
- if last_word == "/drop":
732
- completions = [
733
- field_name
734
- for field_name in memory["conf"].keys()
735
- if field_name.startswith(current_word)
736
- ]
737
- # /conf [curosr]
738
- elif not last_word and not current_word:
739
- completions = [
740
- "/drop"] if "/drop".startswith(current_word) else []
741
- completions += [
742
- field_name + ":"
743
- for field_name in AutoCoderArgs.model_fields.keys()
744
- if field_name.startswith(current_word)
745
- ]
746
- # /conf p[cursor]
747
- elif not last_word and current_word:
748
- completions = [
749
- "/drop"] if "/drop".startswith(current_word) else []
750
- completions += [
751
- field_name + ":"
752
- for field_name in AutoCoderArgs.model_fields.keys()
753
- if field_name.startswith(current_word)
754
- ]
755
-
756
- for completion in completions:
757
- yield Completion(completion, start_position=-len(current_word))
758
-
759
- else:
760
- for command in self.commands:
761
- if command.startswith(text):
762
- yield Completion(command, start_position=-len(text))
763
- else:
764
- for command in self.commands:
765
- if command.startswith(text):
766
- yield Completion(command, start_position=-len(text))
767
-
768
- def update_current_files(self, files):
769
- self.current_file_names = [f for f in files]
770
-
771
- def refresh_files(self):
772
- self.all_file_names = get_all_file_names_in_project()
773
- self.all_files = get_all_file_in_project()
774
- self.all_dir_names = get_all_dir_names_in_project()
775
- self.all_files_with_dot = get_all_file_in_project_with_dot()
776
- self.symbol_list = get_symbol_list()
777
-
778
-
779
- completer = CommandCompleter(commands)
780
-
781
-
782
181
  def save_memory():
783
182
  with open(os.path.join(base_persist_dir, "nano-memory.json"), "w") as fp:
784
183
  json_str = json.dumps(memory, indent=2, ensure_ascii=False)
@@ -792,7 +191,28 @@ def load_memory():
792
191
  if os.path.exists(memory_path):
793
192
  with open(memory_path, "r") as f:
794
193
  memory = json.load(f)
795
- completer.update_current_files(memory["current_files"]["files"])
194
+
195
+
196
+ def get_memory():
197
+ load_memory()
198
+ return memory
199
+
200
+
201
+ completer = CommandCompleter(
202
+ commands=commands,
203
+ file_system_model=FileSystemModel(
204
+ project_root=project_root,
205
+ get_all_file_names_in_project=get_all_file_names_in_project,
206
+ get_all_file_in_project=get_all_file_in_project,
207
+ get_all_dir_names_in_project=get_all_dir_names_in_project,
208
+ get_all_file_in_project_with_dot=get_all_file_in_project_with_dot,
209
+ get_symbol_list=get_symbol_list
210
+ ),
211
+ memory_model=MemoryConfig(
212
+ get_memory_func=get_memory,
213
+ save_memory_func=save_memory
214
+ )
215
+ )
796
216
 
797
217
 
798
218
  def exclude_dirs(dir_names: List[str]):
@@ -938,22 +358,6 @@ def index_import(import_path: str):
938
358
  return False
939
359
 
940
360
 
941
- def wrap_text_in_table(data, max_width=60):
942
- """
943
- Wraps text in each cell of the table to a specified width.
944
-
945
- :param data: A list of lists, where each inner list represents a row in the table.
946
- :param max_width: The maximum width of text in each cell.
947
- :return: A new table data with wrapped text.
948
- """
949
- wrapped_data = []
950
- for row in data:
951
- wrapped_row = [textwrap.fill(str(cell), width=max_width) for cell in row]
952
- wrapped_data.append(wrapped_row)
953
-
954
- return wrapped_data
955
-
956
-
957
361
  def index_query_command(query: str, llm: AutoLLM):
958
362
  update_config_to_args(query=query, delete_execute_file=True)
959
363
 
@@ -2349,6 +1753,7 @@ def main():
2349
1753
 
2350
1754
  load_memory()
2351
1755
  is_old_version()
1756
+ completer.update_current_files(memory["current_files"]["files"])
2352
1757
 
2353
1758
  if len(memory["models"]) == 0:
2354
1759
  _model_pass = input(f" 是否跳过模型配置(y/n): ").strip().lower()