auto-coder 0.1.306__py3-none-any.whl → 0.1.307__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.

Files changed (28) hide show
  1. {auto_coder-0.1.306.dist-info → auto_coder-0.1.307.dist-info}/METADATA +1 -1
  2. {auto_coder-0.1.306.dist-info → auto_coder-0.1.307.dist-info}/RECORD +28 -28
  3. autocoder/agent/auto_demand_organizer.py +13 -20
  4. autocoder/agent/auto_filegroup.py +10 -16
  5. autocoder/agent/auto_learn_from_commit.py +22 -32
  6. autocoder/agent/auto_review_commit.py +12 -63
  7. autocoder/auto_coder.py +3 -6
  8. autocoder/auto_coder_runner.py +40 -8
  9. autocoder/commands/auto_command.py +31 -7
  10. autocoder/common/__init__.py +2 -0
  11. autocoder/common/action_yml_file_manager.py +75 -37
  12. autocoder/common/code_auto_generate.py +2 -2
  13. autocoder/common/code_auto_generate_diff.py +2 -2
  14. autocoder/common/code_auto_generate_editblock.py +2 -2
  15. autocoder/common/code_auto_generate_strict_diff.py +2 -2
  16. autocoder/common/code_auto_merge.py +2 -2
  17. autocoder/common/code_auto_merge_diff.py +2 -2
  18. autocoder/common/code_auto_merge_editblock.py +2 -2
  19. autocoder/common/code_auto_merge_strict_diff.py +2 -2
  20. autocoder/common/code_modification_ranker.py +5 -1
  21. autocoder/common/git_utils.py +73 -56
  22. autocoder/common/stream_out_type.py +3 -0
  23. autocoder/memory/active_context_manager.py +16 -16
  24. autocoder/version.py +1 -1
  25. {auto_coder-0.1.306.dist-info → auto_coder-0.1.307.dist-info}/LICENSE +0 -0
  26. {auto_coder-0.1.306.dist-info → auto_coder-0.1.307.dist-info}/WHEEL +0 -0
  27. {auto_coder-0.1.306.dist-info → auto_coder-0.1.307.dist-info}/entry_points.txt +0 -0
  28. {auto_coder-0.1.306.dist-info → auto_coder-0.1.307.dist-info}/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: auto-coder
3
- Version: 0.1.306
3
+ Version: 0.1.307
4
4
  Summary: AutoCoder: AutoCoder
5
5
  Author: allwefantasy
6
6
  Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
@@ -1,10 +1,10 @@
1
1
  autocoder/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- autocoder/auto_coder.py,sha256=4NyaafKENW-yRRoaIQbbVac8CLRXQQ_0WFUmxpuIdf8,66047
2
+ autocoder/auto_coder.py,sha256=ifhdnd39tOIDu_4LdYTxjVCnwmpDoOC90RRwD8bhIKU,65983
3
3
  autocoder/auto_coder_lang.py,sha256=Rtupq6N3_HT7JRhDKdgCBcwRaiAnyCOR_Gsp4jUomrI,3229
4
4
  autocoder/auto_coder_rag.py,sha256=5TtAfbEBwyt-cB4WcI8eQ1G3AuKij0056wFYRViDhLs,34036
5
5
  autocoder/auto_coder_rag_client_mcp.py,sha256=QRxUbjc6A8UmDMQ8lXgZkjgqtq3lgKYeatJbDY6rSo0,6270
6
6
  autocoder/auto_coder_rag_mcp.py,sha256=-RrjNwFaS2e5v8XDIrKR-zlUNUE8UBaeOtojffBrvJo,8521
7
- autocoder/auto_coder_runner.py,sha256=bJKALThUBFKCmX-BB3Cjh9go9HRMnh9yZdKHZz8fAoc,104751
7
+ autocoder/auto_coder_runner.py,sha256=bvd1UXYzVT2L-I2ZCkdxy9Ap8P2Q6F2JD-F7QLvaIPc,106545
8
8
  autocoder/auto_coder_server.py,sha256=E3Z829TPSooRSNhuh3_x9yaZi0f5G0Lm0ntoZhjGaoQ,20576
9
9
  autocoder/benchmark.py,sha256=Ypomkdzd1T3GE6dRICY3Hj547dZ6_inqJbBJIp5QMco,4423
10
10
  autocoder/chat_auto_coder.py,sha256=Cp5_m3pCxEDcRrVG1uojTfD8xecdl9FvYtD948TvLsg,25223
@@ -14,13 +14,13 @@ autocoder/command_parser.py,sha256=fx1g9E6GaM273lGTcJqaFQ-hoksS_Ik2glBMnVltPCE,1
14
14
  autocoder/lang.py,sha256=U6AjVV8Rs1uLyjFCZ8sT6WWuNUxMBqkXXIOs4S120uk,14511
15
15
  autocoder/models.py,sha256=AyoZ-Pzy0oyYUmWCxOIRiOImsqboSfRET7LO9-UOuxI,11172
16
16
  autocoder/run_context.py,sha256=IUfSO6_gp2Wt1blFWAmOpN0b0nDrTTk4LmtCYUBIoro,1643
17
- autocoder/version.py,sha256=XqstJG2NUtLXeLrUSdvICbSPFolVEqrCnUeMEAadQlM,23
17
+ autocoder/version.py,sha256=SL-SjrRE3j5Bdry_MqdGwfa7ZysXlPPGduCZPe8eo98,23
18
18
  autocoder/agent/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
- autocoder/agent/auto_demand_organizer.py,sha256=NWSAEsEk94vT3lGjfo25kKLMwYdPcpy9e-i21txPasQ,6942
20
- autocoder/agent/auto_filegroup.py,sha256=CW7bqp0FW1GIEMnl-blyAc2UGT7O9Mom0q66ITz1ckM,6635
19
+ autocoder/agent/auto_demand_organizer.py,sha256=URAq0gSEiHeV_W4zwhOI_83kHz0Ryfj1gcfh5jwCv_w,6501
20
+ autocoder/agent/auto_filegroup.py,sha256=pBsAkBcpFTff-9L5OwI8xhf2xPKpl-aZwz-skF2B6dc,6296
21
21
  autocoder/agent/auto_guess_query.py,sha256=rDSdhpPHcOGE5MuDXvIrhCXAPR4ARS1LqpyoLsx2Jhw,11374
22
- autocoder/agent/auto_learn_from_commit.py,sha256=ssttfsB4Xxjez6bVz1Y1NQlkX8PwpDjUeIhMUitIW0g,11526
23
- autocoder/agent/auto_review_commit.py,sha256=PGlP_-dSWcT4sqMCaBpNAm7JHNrDYjJorwlO4mBbobo,13323
22
+ autocoder/agent/auto_learn_from_commit.py,sha256=edD4GQJyO2qvVnTKyldeswWoNeKe1Aaua6ieJzlGlFI,10662
23
+ autocoder/agent/auto_review_commit.py,sha256=mHi26sU4gJAsutqJyLyXfg7DfNZJNt-H7_2a-a8pLEQ,10563
24
24
  autocoder/agent/auto_tool.py,sha256=DBzip-P_T6ZtT2eHexPcusmKYD0h7ufzp7TLwXAY10E,11554
25
25
  autocoder/agent/coder.py,sha256=x6bdJwDuETGg9ebQnYlUWCxCtQcDGg73LtI6McpWslQ,72034
26
26
  autocoder/agent/designer.py,sha256=EpRbzO58Xym3GrnppIT1Z8ZFAlnNfgzHbIzZ3PX-Yv8,27037
@@ -28,13 +28,13 @@ autocoder/agent/planner.py,sha256=SZTSZHxHzDmuWZo3K5fs79RwvJLWurg-nbJRRNbX65o,91
28
28
  autocoder/agent/project_reader.py,sha256=tWLaPoLw1gI6kO_NzivQj28KbobU2ceOLuppHMbfGl8,18234
29
29
  autocoder/chat/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
30
30
  autocoder/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
31
- autocoder/commands/auto_command.py,sha256=mreV3kJTo1QwaM7kWY9kfol569pQTb8EHFq53B6BugI,62501
31
+ autocoder/commands/auto_command.py,sha256=fZ4TNU4YA9qdoVHmZX0rb6sV_N-DEcgSLaC5s3PGSHA,64200
32
32
  autocoder/commands/auto_web.py,sha256=Cc0eb6JN3SvFy3GD_lpSLvIqj7F1eFDTcwg1t-zDcKg,39024
33
33
  autocoder/commands/tools.py,sha256=uSPyEcx6o1upuw6clHdL4yeqoXbywnvCxAPo2mt5rJk,28117
34
34
  autocoder/common/JupyterClient.py,sha256=O-wi6pXeAEYhAY24kDa0BINrLYvKS6rKyWe98pDClS0,2816
35
35
  autocoder/common/ShellClient.py,sha256=fM1q8t_XMSbLBl2zkCNC2J9xuyKN3eXzGm6hHhqL2WY,2286
36
- autocoder/common/__init__.py,sha256=S8edUoKW4EV5L7kKfZ6MSuEI-hLwop1PKJa-parNhg0,14026
37
- autocoder/common/action_yml_file_manager.py,sha256=q0QBlptWtR9DhqguMBZJfHmmTP9fVGjI5DdDer29sis,16109
36
+ autocoder/common/__init__.py,sha256=sIbE0Gm30MkCmZ9ClQeKx1Hw-vjdcVF65hO5TschS1Y,14070
37
+ autocoder/common/action_yml_file_manager.py,sha256=w-422xnvOfhf3MFxNLCZuAEUDa8RrYX12ngHs-fsH0k,17358
38
38
  autocoder/common/anything2images.py,sha256=0ILBbWzY02M-CiWB-vzuomb_J1hVdxRcenAfIrAXq9M,25283
39
39
  autocoder/common/anything2img.py,sha256=iZQmg8srXlD7N5uGl5b_ONKJMBjYoW8kPmokkG6ISF0,10118
40
40
  autocoder/common/audio.py,sha256=Kn9nWKQddWnUrAz0a_ZUgjcu4VUU_IcZBigT7n3N3qc,7439
@@ -44,15 +44,15 @@ autocoder/common/buildin_tokenizer.py,sha256=L7d5t39ZFvUd6EoMPXUhYK1toD0FHlRH1jt
44
44
  autocoder/common/chunk_validation.py,sha256=BrR_ZWavW8IANuueEE7hS8NFAwEvm8TX34WnPx_1hs8,3030
45
45
  autocoder/common/cleaner.py,sha256=NU72i8C6o9m0vXExab7nao5bstBUsfJFcj11cXa9l4U,1089
46
46
  autocoder/common/code_auto_execute.py,sha256=4KXGmiGObr_B1d6tzV9dwS6MifCSc3Gm4j2d6ildBXQ,6867
47
- autocoder/common/code_auto_generate.py,sha256=lAuvMggASauLsWmynQxDcb0B0N1BZukrk2hwHVviwNk,17206
48
- autocoder/common/code_auto_generate_diff.py,sha256=KqmOq5BsmM18t4ZPc5o1WfmXabfL9fNhCw2x_mRVHm4,23073
49
- autocoder/common/code_auto_generate_editblock.py,sha256=rY-zqQ8qSgcui8FDsOXdcwqVZeTSPxI3LejKNkFkXIc,24966
50
- autocoder/common/code_auto_generate_strict_diff.py,sha256=Jz234Nk9G-e7wLojvWiwfLTEpWvJigo9GnEUXK-kCyE,21968
51
- autocoder/common/code_auto_merge.py,sha256=VWUWxubvbd1eF0ToupCGHP3uHD4wHUhQFOmeSLdCYYo,9941
52
- autocoder/common/code_auto_merge_diff.py,sha256=xg79LqyuEsuT2SVKGbYGTisIi1NUtSHXwSwazP315uM,19251
53
- autocoder/common/code_auto_merge_editblock.py,sha256=5m0q1047Ew02gINbB7ZHujW2D21R5st8RB2MenXq1I4,21745
54
- autocoder/common/code_auto_merge_strict_diff.py,sha256=VbpK-jB-58I0rxYGogcOTBkRYDnfaWVlDWF6GwGhhcg,13189
55
- autocoder/common/code_modification_ranker.py,sha256=i_ycikE9pE83vtb3oMGK8TLWMWkjzADxuzR-Zop7WLI,13246
47
+ autocoder/common/code_auto_generate.py,sha256=mxCPG-RC36q5POPMozPytjJcK0ExUeyau7b9ZWWScps,17228
48
+ autocoder/common/code_auto_generate_diff.py,sha256=lddReDQzJts25x55YDbFWJBJ_n9OzsLkyQ-cc1DFw1M,23095
49
+ autocoder/common/code_auto_generate_editblock.py,sha256=YfXuSYogJ5YoHTsx6LN5pPskZPop-noNViiDSRACOYU,24988
50
+ autocoder/common/code_auto_generate_strict_diff.py,sha256=cKCcDfVADSbkwPMWmHGhRjzKutieEw80NjFjwZbFudw,21990
51
+ autocoder/common/code_auto_merge.py,sha256=WaU-T-ZVn3QDaA_SrdkHciUPKDcTfVa-IbhHKBYEv5w,9961
52
+ autocoder/common/code_auto_merge_diff.py,sha256=DcljWrtlejq2cb9Gj-jBjvUQzRbCE2uMNGg8SBOhEnk,19271
53
+ autocoder/common/code_auto_merge_editblock.py,sha256=pLgs1HsS4mRkO2cm7T18u4w8T3PjSMxqNbJZ4LHnnWI,21765
54
+ autocoder/common/code_auto_merge_strict_diff.py,sha256=C35pFxhkgypsm50VDAFOBAT6YXMtzKTvIpEUH1GjZZg,13209
55
+ autocoder/common/code_modification_ranker.py,sha256=Ld3wtjV10zW7lN_L3bKEsvi7RvAleB8r1DD9eLmVnx4,13462
56
56
  autocoder/common/command_completer.py,sha256=jBFi8Y1xWmMcxwdDz6Gs7r8ADFPb7eRr9egWU0vLxLo,35787
57
57
  autocoder/common/command_generator.py,sha256=YwB_E818vx0fQDOpLD61GivsSgLnrIoxKrY22ka49JU,2797
58
58
  autocoder/common/command_templates.py,sha256=WAixVjue5QmCFAD13K4ElfcOEjdeGr8tFb0atDAbEoo,8658
@@ -63,7 +63,7 @@ autocoder/common/const.py,sha256=eTjhjh4Aj4CUzviJ81jaf3Y5cwqsLATySn2wJxaS6RQ,291
63
63
  autocoder/common/context_pruner.py,sha256=HlU5BmxpCX7uVTJUsTFLlXvkwcOQuidI9uCKZaFxh6s,19874
64
64
  autocoder/common/conversation_pruner.py,sha256=pzmrQEa7pFzA66eYSS_h7VqP6ZwUABeooDQzm0PGu0A,5770
65
65
  autocoder/common/files.py,sha256=nPiKcnUcYZbSUn3TskKeTVnAxCJRtuehPuB_5d2imX8,4618
66
- autocoder/common/git_utils.py,sha256=d2J5APFIw2LxxRHN8UMY3qwiLeYA1WIf74IO72t0Rbc,26587
66
+ autocoder/common/git_utils.py,sha256=yxV-eYYwsncNpVs07Nk61506FFpG2REC43l6ppFCYWM,27081
67
67
  autocoder/common/global_cancel.py,sha256=TyjYQPESwo04D1BOTmC9hH7IbkKDDM-b2zPacEHGIQ8,3264
68
68
  autocoder/common/image_to_page.py,sha256=yWiTJQ49Lm3j0FngiJhQ9u7qayqE_bOGb8Rk0TmSWx0,14123
69
69
  autocoder/common/index_import_export.py,sha256=h758AYY1df6JMTKUXYmMkSgxItfymDt82XT7O-ygEuw,4565
@@ -82,7 +82,7 @@ autocoder/common/search.py,sha256=245iPFgWhMldoUK3CqCP89ltaxZiNPK73evoG6Fp1h8,16
82
82
  autocoder/common/search_replace.py,sha256=GphFkc57Hb673CAwmbiocqTbw8vrV7TrZxtOhD0332g,22147
83
83
  autocoder/common/shells.py,sha256=elminFpNosnV0hsEUcsugDxlGO8NfH96uah-8bkaBvA,19929
84
84
  autocoder/common/stats_panel.py,sha256=wGl9O45pjVVDxhNumLv4_NfLYSlUP_18Tw4hcJSjw50,4596
85
- autocoder/common/stream_out_type.py,sha256=3caVTArt1mjKMxl-W3bMhBxqtXdr2SgPjOCroNJAWy8,265
85
+ autocoder/common/stream_out_type.py,sha256=uNHRg-1my6CxcPzQM6pACrYpTZbxOenVT8aLxnruv2w,333
86
86
  autocoder/common/sys_prompt.py,sha256=JlexfjZt554faqbgkCmzOJqYUzDHfbnxly5ugFfHfEE,26403
87
87
  autocoder/common/text.py,sha256=KGRQq314GHBmY4MWG8ossRoQi1_DTotvhxchpn78c-k,1003
88
88
  autocoder/common/types.py,sha256=PXTETrsTvhLE49jqAeUKGySvxBN9pjeyCgRHLDYdd9U,664
@@ -117,7 +117,7 @@ autocoder/index/filter/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3
117
117
  autocoder/index/filter/normal_filter.py,sha256=MI-8xdXCrniaxYCHVTLkq5tafvcUiauD3LN0b3ymRXI,8361
118
118
  autocoder/index/filter/quick_filter.py,sha256=ozESEgy506FQ5ecjOumyo4D_KMrterB1QLmnVtiyOiM,43264
119
119
  autocoder/memory/__init__.py,sha256=5FeGvsesRViYL4BkFiHw9SdlyHeWlqALpTyqOpfnBRw,179
120
- autocoder/memory/active_context_manager.py,sha256=yf_AiA8TUt7A7Tlg5TAlLm5iv9Jd86pOQrhXjOQsXT0,27111
120
+ autocoder/memory/active_context_manager.py,sha256=7f7DznnLXbXH6yptN8rOSSJcre0bSJ2dU9mM9L6bvno,27106
121
121
  autocoder/memory/active_package.py,sha256=CyfqZSgNucE5mGQH5JGHTE8lTU0KjFGkW2YN8vl9SoI,17480
122
122
  autocoder/memory/async_processor.py,sha256=htHzLGupw9IHQAEdLe2AEaALZSItPi3AltDt8FMTRHk,4643
123
123
  autocoder/memory/directory_mapper.py,sha256=BXHblOdRpeZb7URDECALp9uN5oi91KmkW9g_UaWFuZY,2513
@@ -198,9 +198,9 @@ autocoder/utils/types.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
198
198
  autocoder/utils/auto_coder_utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
199
199
  autocoder/utils/auto_coder_utils/chat_stream_out.py,sha256=xuBeWD0YOckqRo8JB1WkVIMOYH6c24m7JfV4svBfPDo,15113
200
200
  autocoder/utils/chat_auto_coder_utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
201
- auto_coder-0.1.306.dist-info/LICENSE,sha256=HrhfyXIkWY2tGFK11kg7vPCqhgh5DcxleloqdhrpyMY,11558
202
- auto_coder-0.1.306.dist-info/METADATA,sha256=dRRK97KSeiakq3KrfZwTZbUUAzwCb24dqfbcnA-OiGE,2721
203
- auto_coder-0.1.306.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
204
- auto_coder-0.1.306.dist-info/entry_points.txt,sha256=0nzHtHH4pNcM7xq4EBA2toS28Qelrvcbrr59GqD_0Ak,350
205
- auto_coder-0.1.306.dist-info/top_level.txt,sha256=Jqc0_uJSw2GwoFQAa9iJxYns-2mWla-9ok_Y3Gcznjk,10
206
- auto_coder-0.1.306.dist-info/RECORD,,
201
+ auto_coder-0.1.307.dist-info/LICENSE,sha256=HrhfyXIkWY2tGFK11kg7vPCqhgh5DcxleloqdhrpyMY,11558
202
+ auto_coder-0.1.307.dist-info/METADATA,sha256=3zdlrR3qOtRYS5giLuNFcXbUERBGTgE4hKVtz3CgZ7I,2721
203
+ auto_coder-0.1.307.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
204
+ auto_coder-0.1.307.dist-info/entry_points.txt,sha256=0nzHtHH4pNcM7xq4EBA2toS28Qelrvcbrr59GqD_0Ak,350
205
+ auto_coder-0.1.307.dist-info/top_level.txt,sha256=Jqc0_uJSw2GwoFQAa9iJxYns-2mWla-9ok_Y3Gcznjk,10
206
+ auto_coder-0.1.307.dist-info/RECORD,,
@@ -6,6 +6,8 @@ import byzerllm
6
6
  import pydantic
7
7
  import git
8
8
 
9
+ from autocoder.common.action_yml_file_manager import ActionYmlFileManager
10
+
9
11
 
10
12
  class DemandItem(pydantic.BaseModel):
11
13
  """单个需求项"""
@@ -44,6 +46,7 @@ class AutoDemandOrganizer:
44
46
  file_size_limit: 最多分析多少历史任务
45
47
  """
46
48
  self.project_dir = project_dir
49
+ self.action_file_manager = ActionYmlFileManager(project_dir)
47
50
  self.actions_dir = os.path.join(project_dir, "actions")
48
51
  self.llm = llm
49
52
  self.file_size_limit = file_size_limit
@@ -162,26 +165,16 @@ class AutoDemandOrganizer:
162
165
 
163
166
  if query and urls:
164
167
  commit_diff = ""
165
- if not self.skip_diff:
166
- # 计算文件的MD5用于匹配commit
167
- import hashlib
168
- file_md5 = hashlib.md5(open(yaml_path, 'rb').read()).hexdigest()
169
- response_id = f"auto_coder_{yaml_file}_{file_md5}"
170
- # 查找对应的commit
171
- try:
172
- for commit in repo.iter_commits():
173
- if response_id in commit.message:
174
- if commit.parents:
175
- parent = commit.parents[0]
176
- commit_diff = repo.git.diff(
177
- parent.hexsha, commit.hexsha)
178
- else:
179
- commit_diff = repo.git.show(commit.hexsha)
180
- break
181
- except git.exc.GitCommandError as e:
182
- logger.error(f"Git命令执行错误: {str(e)}")
183
- except Exception as e:
184
- logger.error(f"获取commit diff时出错: {str(e)}")
168
+ if not self.skip_diff:
169
+ commit_id = self.action_file_manager.get_commit_id_from_file(yaml_file)
170
+ commit = repo.commit(commit_id)
171
+ if commit:
172
+ if commit.parents:
173
+ parent = commit.parents[0]
174
+ commit_diff = repo.git.diff(
175
+ parent.hexsha, commit.hexsha)
176
+ else:
177
+ commit_diff = repo.git.show(commit.hexsha)
185
178
 
186
179
  querie_with_urls_and_diffs.append((query, urls, commit_diff))
187
180
 
@@ -4,6 +4,7 @@ import yaml
4
4
  from loguru import logger
5
5
  import byzerllm
6
6
  import pydantic
7
+ from autocoder.common.action_yml_file_manager import ActionYmlFileManager
7
8
 
8
9
 
9
10
  class FileGroup(pydantic.BaseModel):
@@ -40,6 +41,7 @@ class AutoFileGroup:
40
41
  actions_dir: 包含YAML文件的目录
41
42
  """
42
43
  self.project_dir = project_dir
44
+ self.action_manager = ActionYmlFileManager(project_dir)
43
45
  self.actions_dir = os.path.join(project_dir, "actions")
44
46
  self.llm = llm
45
47
  self.file_size_limit = file_size_limit
@@ -152,23 +154,15 @@ class AutoFileGroup:
152
154
  commit_diff = ""
153
155
  if not self.skip_diff:
154
156
  # 计算文件的MD5用于匹配commit
155
- file_md5 = hashlib.md5(open(yaml_path, 'rb').read()).hexdigest()
156
- response_id = f"auto_coder_{yaml_file}_{file_md5}"
157
+ commit_id = self.action_manager.get_commit_id_from_file(yaml_file)
158
+ commit = repo.commit(commit_id)
157
159
  # 查找对应的commit
158
- try:
159
- for commit in repo.iter_commits():
160
- if response_id in commit.message:
161
- if commit.parents:
162
- parent = commit.parents[0]
163
- commit_diff = repo.git.diff(
164
- parent.hexsha, commit.hexsha)
165
- else:
166
- commit_diff = repo.git.show(commit.hexsha)
167
- break
168
- except git.exc.GitCommandError as e:
169
- logger.error(f"Git命令执行错误: {str(e)}")
170
- except Exception as e:
171
- logger.error(f"获取commit diff时出错: {str(e)}")
160
+ if commit and commit.parents:
161
+ parent = commit.parents[0]
162
+ commit_diff = repo.git.diff(
163
+ parent.hexsha, commit.hexsha)
164
+ else:
165
+ commit_diff = repo.git.show(commit.hexsha)
172
166
 
173
167
  querie_with_urls_and_diffs.append((query, urls, commit_diff))
174
168
 
@@ -121,40 +121,30 @@ class AutoLearnFromCommit:
121
121
  if not self.skip_diff:
122
122
  # 使用 ActionManager 获取 commit ID
123
123
  commit_id = self.action_manager.get_commit_id_from_file(action_file)
124
- if commit_id:
125
- try:
126
- for commit in repo.iter_commits():
127
- if commit_id in commit.message:
128
- if commit.parents:
129
- parent = commit.parents[0]
130
- # 获取所有文件的前后内容
131
- for diff_item in parent.diff(commit):
132
- file_path = diff_item.a_path if diff_item.a_path else diff_item.b_path
133
-
134
- # 获取变更前内容
135
- before_content = None
136
- try:
137
- if diff_item.a_blob:
138
- before_content = repo.git.show(f"{parent.hexsha}:{file_path}")
139
- except git.exc.GitCommandError:
140
- pass # 文件可能是新增的
124
+ commit = repo.commit(commit_id)
125
+ if commit and commit.parents:
126
+ parent = commit.parents[0]
127
+ # 获取所有文件的前后内容
128
+ for diff_item in parent.diff(commit):
129
+ file_path = diff_item.a_path if diff_item.a_path else diff_item.b_path
130
+
131
+ # 获取变更前内容
132
+ before_content = None
133
+ try:
134
+ if diff_item.a_blob:
135
+ before_content = repo.git.show(f"{parent.hexsha}:{file_path}")
136
+ except git.exc.GitCommandError:
137
+ pass # 文件可能是新增的
141
138
 
142
- # 获取变更后内容
143
- after_content = None
144
- try:
145
- if diff_item.b_blob:
146
- after_content = repo.git.show(f"{commit.hexsha}:{file_path}")
147
- except git.exc.GitCommandError:
148
- pass # 文件可能被删除
139
+ # 获取变更后内容
140
+ after_content = None
141
+ try:
142
+ if diff_item.b_blob:
143
+ after_content = repo.git.show(f"{commit.hexsha}:{file_path}")
144
+ except git.exc.GitCommandError:
145
+ pass # 文件可能被删除
149
146
 
150
- changes[file_path] = (before_content, after_content)
151
- break
152
- except git.exc.GitCommandError as e:
153
- printer = Printer()
154
- printer.print_in_terminal("git_command_error", style="red", error=str(e))
155
- except Exception as e:
156
- printer = Printer()
157
- printer.print_in_terminal("get_commit_changes_error", style="red", error=str(e))
147
+ changes[file_path] = (before_content, after_content)
158
148
 
159
149
  querie_with_urls_and_changes.append((query, urls, changes))
160
150
 
@@ -9,6 +9,7 @@ from autocoder.common.printer import Printer
9
9
  from autocoder.common import AutoCoderArgs
10
10
  from autocoder.common.utils_code_auto_generate import stream_chat_with_continue
11
11
  import hashlib
12
+ from autocoder.common.action_yml_file_manager import ActionYmlFileManager
12
13
 
13
14
 
14
15
  def load_yaml_config(yaml_file: str) -> Dict:
@@ -41,6 +42,7 @@ class AutoReviewCommit:
41
42
  self.llm = llm
42
43
  self.skip_diff = skip_diff
43
44
  self.console = console or Console()
45
+ self.action_yml_manager = ActionYmlFileManager(source_dir=args.source_dir)
44
46
 
45
47
  @byzerllm.prompt()
46
48
  def review(self, querie_with_urls_and_changes: List[Tuple[str, List[str], Dict[str, Tuple[str, str]]]], query: str) -> Generator[str,None,None]:
@@ -141,66 +143,16 @@ class AutoReviewCommit:
141
143
  action_files = sorted(action_files, key=get_seq)
142
144
  action_files.reverse()
143
145
 
146
+ if not action_files:
147
+ return []
148
+
144
149
  action_file = action_files[0]
145
-
146
150
  querie_with_urls_and_changes = []
147
- repo = git.Repo(self.project_dir)
148
-
149
- # 收集所有query、urls和对应的文件变化
150
- for yaml_file in [action_file]:
151
- yaml_path = os.path.join(self.actions_dir, yaml_file)
152
- config = load_yaml_config(yaml_path)
153
151
 
154
- if not config:
155
- continue
156
-
157
- query = config.get('query', '')
158
- urls = config.get('urls', [])
159
-
160
- if query:
161
- changes = {}
162
- if not self.skip_diff:
163
- # 计算文件的MD5用于匹配commit
164
- with open(yaml_path, 'r', encoding='utf-8') as f:
165
- yaml_content = f.read()
166
- file_md5 = hashlib.md5(yaml_content.encode("utf-8")).hexdigest()
167
- response_id = f"auto_coder_{yaml_file}_{file_md5}"
168
- # 查找对应的commit
169
- try:
170
- for commit in repo.iter_commits():
171
- if response_id in commit.message:
172
- if commit.parents:
173
- parent = commit.parents[0]
174
- # 获取所有文件的前后内容
175
- for diff_item in parent.diff(commit):
176
- file_path = diff_item.a_path if diff_item.a_path else diff_item.b_path
177
-
178
- # 获取变更前内容
179
- before_content = None
180
- try:
181
- if diff_item.a_blob:
182
- before_content = repo.git.show(f"{parent.hexsha}:{file_path}")
183
- except git.exc.GitCommandError:
184
- pass # 文件可能是新增的
185
-
186
- # 获取变更后内容
187
- after_content = None
188
- try:
189
- if diff_item.b_blob:
190
- after_content = repo.git.show(f"{commit.hexsha}:{file_path}")
191
- except git.exc.GitCommandError:
192
- pass # 文件可能被删除
193
-
194
- changes[file_path] = (before_content, after_content)
195
- break
196
- except git.exc.GitCommandError as e:
197
- printer = Printer()
198
- printer.print_in_terminal("git_command_error", style="red", error=str(e))
199
- except Exception as e:
200
- printer = Printer()
201
- printer.print_in_terminal("get_commit_changes_error", style="red", error=str(e))
202
-
203
- querie_with_urls_and_changes.append((query, urls, changes))
152
+ # 使用ActionYmlFileManager获取提交变更
153
+ changes = self.action_yml_manager.get_commit_changes(action_file)
154
+ if changes:
155
+ querie_with_urls_and_changes = changes
204
156
 
205
157
  return querie_with_urls_and_changes
206
158
 
@@ -289,14 +241,11 @@ class AutoReviewCommit:
289
241
  printer = Printer()
290
242
  commit_file_name = None
291
243
  if commit_id:
244
+ # 利用ActionYmlFileManager获取文件名
292
245
  repo = git.Repo(self.project_dir)
293
246
  commit = repo.commit(commit_id)
294
- # auto_coder_000000001926_chat_action.yml_88614d5bd4046a068786c252fbc39c13
295
- msg = commit.message
296
- commit_file_info = msg.split("_")[0]
297
- if commit_file_info.startswith("auto_coder_"):
298
- commit_file_name = "_".join(msg.split("_",)[0:-1])
299
-
247
+ commit_file_name = self.action_yml_manager.get_file_name_from_commit_msg(commit.message)
248
+
300
249
  if not commit_file_name:
301
250
  raise ValueError(printer.get_message_from_key_with_format("no_commit_file_name",commit_id=commit_id))
302
251
 
autocoder/auto_coder.py CHANGED
@@ -27,7 +27,6 @@ from autocoder.utils.rest import HttpDoc
27
27
  from byzerllm.apps.byzer_storage.env import get_latest_byzer_retrieval_lib
28
28
  from autocoder.command_args import parse_args
29
29
  from autocoder.rag.api_server import serve, ServerArgs
30
- from autocoder.utils import open_yaml_file_in_editor, get_last_yaml_file
31
30
  from autocoder.utils.request_queue import (
32
31
  request_queue,
33
32
  RequestValue,
@@ -120,12 +119,10 @@ def main(input_args: Optional[List[str]] = None):
120
119
  # args.request_id = str(uuid.uuid4())
121
120
 
122
121
  if raw_args.command == "revert":
123
- repo_path = args.source_dir
124
-
125
122
  file_name = os.path.basename(args.file)
126
- revert_result = git_utils.revert_changes(
127
- repo_path, f"auto_coder_{file_name}"
128
- )
123
+ action_file_manager = ActionYmlFileManager(source_dir=args.source_dir)
124
+ revert_result = action_file_manager.revert_file(file_name)
125
+
129
126
  if revert_result:
130
127
  print(f"Successfully reverted changes for {args.file}")
131
128
  else:
@@ -17,6 +17,7 @@ from contextlib import contextmanager
17
17
  from typing import List, Dict, Any, Optional
18
18
  from autocoder.common import AutoCoderArgs
19
19
  from pydantic import BaseModel
20
+ from autocoder.common.action_yml_file_manager import ActionYmlFileManager
20
21
  from autocoder.common.result_manager import ResultManager
21
22
  from autocoder.version import __version__
22
23
  from autocoder.auto_coder import main as auto_coder_main
@@ -695,9 +696,7 @@ def revert():
695
696
 
696
697
  if "Successfully reverted changes" in s:
697
698
  result_manager.append(content=s, meta={"action": "revert","success":False, "input":{
698
- }})
699
-
700
- os.remove(file_path)
699
+ }})
701
700
  else:
702
701
  result_manager.append(content=s, meta={"action": "revert","success":False, "input":{
703
702
  }})
@@ -1428,12 +1427,33 @@ def commit(query: str):
1428
1427
  with open(os.path.join(execute_file), "w",encoding="utf-8") as f:
1429
1428
  f.write(yaml_content)
1430
1429
 
1431
- file_content = open(execute_file).read()
1432
- md5 = hashlib.md5(file_content.encode("utf-8")).hexdigest()
1430
+ args.file = execute_file
1431
+
1433
1432
  file_name = os.path.basename(execute_file)
1434
1433
  commit_result = git_utils.commit_changes(
1435
1434
  ".", f"{commit_message}\nauto_coder_{file_name}"
1436
1435
  )
1436
+
1437
+ printer = Printer()
1438
+
1439
+ action_yml_file_manager = ActionYmlFileManager(args.source_dir)
1440
+ action_file_name = os.path.basename(args.file)
1441
+ add_updated_urls = []
1442
+ commit_result.changed_files
1443
+ for file in commit_result.changed_files:
1444
+ add_updated_urls.append(os.path.join(args.source_dir, file))
1445
+
1446
+ args.add_updated_urls = add_updated_urls
1447
+ update_yaml_success = action_yml_file_manager.update_yaml_field(action_file_name, "add_updated_urls", add_updated_urls)
1448
+ if not update_yaml_success:
1449
+ printer.print_in_terminal("yaml_save_error", style="red", yaml_file=action_file_name)
1450
+
1451
+ if args.enable_active_context:
1452
+ active_context_manager = ActiveContextManager(llm, args.source_dir)
1453
+ task_id = active_context_manager.process_changes(args)
1454
+ printer.print_in_terminal("active_context_background_task",
1455
+ style="blue",
1456
+ task_id=task_id)
1437
1457
  git_utils.print_commit_info(commit_result=commit_result)
1438
1458
  if commit_message:
1439
1459
  printer.print_in_terminal("commit_message", style="green", model_name=target_model, message=commit_message)
@@ -1800,17 +1820,29 @@ def active_context(query: str):
1800
1820
  if len(commands_infos) > 0:
1801
1821
  if "list" in commands_infos:
1802
1822
  command = "list"
1823
+ if "run" in commands_infos:
1824
+ command = "run"
1803
1825
 
1804
1826
  args = get_final_config()
1827
+ printer = Printer()
1805
1828
  # 获取LLM实例
1806
1829
  llm = get_single_llm(args.model,product_mode=args.product_mode)
1830
+ action_file_manager = ActionYmlFileManager(args.source_dir)
1807
1831
 
1808
1832
  # 获取配置和参数
1809
1833
 
1810
1834
 
1811
- # 获取ActiveContextManager单例
1812
- active_context_manager = ActiveContextManager(llm, args)
1813
-
1835
+ active_context_manager = ActiveContextManager(llm, args.source_dir)
1836
+ if command == "run":
1837
+ file_name = commands_infos["run"]["args"][-1]
1838
+ args.file = action_file_manager.get_full_path_by_file_name(file_name)
1839
+ ## 因为更新了args.file
1840
+ active_context_manager = ActiveContextManager(llm, args.source_dir)
1841
+ task_id = active_context_manager.process_changes(args)
1842
+ printer.print_in_terminal("active_context_background_task",
1843
+ style="blue",
1844
+ task_id=task_id)
1845
+
1814
1846
  # 处理不同的命令
1815
1847
  if command == "list":
1816
1848
  # 获取所有任务
@@ -213,6 +213,8 @@ class CommandAutoTuner:
213
213
  @byzerllm.prompt()
214
214
  def _analyze(self, request: AutoCommandRequest) -> str:
215
215
  """
216
+ 你是 auto-coder.chat 软件,帮助用户完成编程方面的需求。我们的目标是根据用户输入和当前上下文,组合多个函数来完成用户的需求。
217
+
216
218
  ## 当前用户环境信息如下:
217
219
  <os_info>
218
220
  操作系统: {{ env_info.os_name }} {{ env_info.os_version }}
@@ -234,10 +236,11 @@ class CommandAutoTuner:
234
236
  {%- endif %}
235
237
  </os_info>
236
238
 
237
- 我们的目标是根据用户输入和当前上下文,组合多个函数来完成用户的需求。
238
-
239
+ 当前项目根目录:
240
+ {{ current_project }}
241
+
239
242
  {% if current_files %}
240
- ## 当前活跃区文件列表:
243
+ ## 当前用户手动添加关注的文件列表:
241
244
  <current_files>
242
245
  {% for file in current_files %}
243
246
  - {{ file }}
@@ -246,7 +249,7 @@ class CommandAutoTuner:
246
249
  {% endif %}
247
250
 
248
251
 
249
- ## 当前用户的配置选项如下:
252
+ ## 这是用户对你的配置
250
253
  <current_conf>
251
254
  {{ current_conf }}
252
255
  </current_conf>
@@ -260,6 +263,24 @@ class CommandAutoTuner:
260
263
  ## 函数组合说明:
261
264
  {{ command_combination_readme }}
262
265
 
266
+ ## active-context 项目追踪文档系统
267
+
268
+ 在 {{ current_project }}/.auto-coder/active-context 下,我们提供了对该项目每个文件目录的追踪。
269
+ 具体逻辑为:假设我们在当前项目有 ./src/package1/py1.py, 那么相应的在 .auto-coder/active-context 会有一个 ./src/package1 目录,
270
+ 该目录下可能会有一个 active-context.md 文件,该文件记录了该目录下所有文件相关信息,可以帮你更好的理解这个目录下的文档,你可以通过 read_files 函数来读取
271
+ 这个文件。注意,这个文件不一定存在。如果读取失败也是正常的。
272
+
273
+ ## 变更记录文档系统
274
+
275
+ 在 {{ current_project }}/actions 目录下,会有格式类似 000000001201_chat_action.yml 的文件,该文件记录了最近10次对话,
276
+ 你可以通过 read_files 函数来读取这些文件,从而更好的理解用户的需求。
277
+
278
+ 下面是一些字段的简单介绍
279
+ - query: 用户需求
280
+ - urls: 用户提供的上下文文件列表
281
+ - dynamic_urls: auto-coder.chat 自动感知的一些文件列表
282
+ - add_updated_urls: 这次需求发生变更的文件列表
283
+
263
284
  {% if conversation_history %}
264
285
  ## 历史对话:
265
286
  <conversation_history>
@@ -314,7 +335,8 @@ class CommandAutoTuner:
314
335
  "conversation_safe_zone_tokens": self.args.conversation_prune_safe_zone_tokens,
315
336
  "os_distribution": shells.get_os_distribution(),
316
337
  "current_user": shells.get_current_username(),
317
- "command_combination_readme": self._command_combination_readme.prompt()
338
+ "command_combination_readme": self._command_combination_readme.prompt(),
339
+ "current_project": os.path.abspath(self.args.source_dir)
318
340
  }
319
341
 
320
342
  @byzerllm.prompt()
@@ -337,8 +359,10 @@ class CommandAutoTuner:
337
359
  尽可通过了解项目后,多用 @文件和@@符号,这样 chat 函数可以更清晰的理解你关注的代码,文档和意图。
338
360
 
339
361
  ### 调用 coding 函数应该注意的事项
340
- 调用 coding 函数的时候,尽可能多的 @文件和@@符号,让需求更加清晰明了,建议多描述具体怎么完成对应的需求。
341
- 对于代码需求设计,尽可能使用 chat 函数。如果成功执行了 coding 函数, 最好再调用一次 chat("/review /commit"),方便总结这次代码变更。
362
+ 调用 coding 函数的之前,你需要尽可能先了解用户需求,了解项目状态,包括读取 active_context 文件来了解项目。
363
+ 然后清晰的描述自己的需求,完整的实现步骤,以及尽可能对@文件和@@符号需要参考以及修改的文件和符号。
364
+ 对于比较复杂的需求,你还可以使用 chat 函数来进行讨论,从而获取一些有用的信息。
365
+ 如果成功执行了 coding 函数, 最好再调用一次 chat("/review /commit"),方便总结这次代码变更。
342
366
  注意,review 完后,需要询问用户是否要做啥调整不,如果用户说不用,那么就停止。否则根据意图进行后续操作。
343
367
 
344
368
  ### 关于对话大小的问题
@@ -416,6 +416,8 @@ class AutoCoderArgs(pydantic.BaseModel):
416
416
 
417
417
  generate_max_rounds: Optional[int] = 5
418
418
 
419
+ revert_commit_id: Optional[str] = None
420
+
419
421
  class Config:
420
422
  protected_namespaces = ()
421
423
 
@@ -4,8 +4,10 @@ import hashlib
4
4
  import git
5
5
  from typing import List, Dict, Tuple, Optional, Union, Any
6
6
  from loguru import logger
7
+ from autocoder.common.git_utils import get_repo
7
8
  from autocoder.common.printer import Printer
8
9
  import byzerllm
10
+ from autocoder.common import git_utils
9
11
 
10
12
  class ActionYmlFileManager:
11
13
  """
@@ -44,7 +46,7 @@ class ActionYmlFileManager:
44
46
  except Exception as e:
45
47
  logger.error(f"Failed to create actions directory: {e}")
46
48
  return False
47
- return True
49
+ return True
48
50
 
49
51
  def get_action_files(self, filter_prefix: Optional[str] = None) -> List[str]:
50
52
  """
@@ -183,16 +185,21 @@ class ActionYmlFileManager:
183
185
  Returns:
184
186
  Dict: YAML 内容,如果加载失败返回空字典
185
187
  """
186
- yaml_path = os.path.join(self.actions_dir, file_name)
187
-
188
+ yaml_path = os.path.join(self.actions_dir, file_name)
188
189
  try:
189
190
  with open(yaml_path, 'r', encoding='utf-8') as f:
190
191
  content = yaml.safe_load(f) or {}
191
192
  return content
192
- except Exception as e:
193
+ except Exception as e:
193
194
  self.printer.print_in_terminal("yaml_load_error", style="red",
194
195
  yaml_file=yaml_path, error=str(e))
195
196
  return {}
197
+
198
+ def get_full_path_by_file_name(self, file_name: str) -> str:
199
+ """
200
+ 根据文件名获取完整路径
201
+ """
202
+ return os.path.join(self.actions_dir, file_name)
196
203
 
197
204
  def save_yaml_content(self, file_name: str, content: Dict) -> bool:
198
205
  """
@@ -232,6 +239,26 @@ class ActionYmlFileManager:
232
239
  yaml_content = self.load_yaml_content(file_name)
233
240
  yaml_content[field] = value
234
241
  return self.save_yaml_content(file_name, yaml_content)
242
+
243
+ def get_all_commit_id_from_file(self,file_name:str):
244
+ '''
245
+ 会包含 revert 信息
246
+ '''
247
+ repo = get_repo(self.source_dir)
248
+ if repo is None:
249
+ logger.error("Repository is not initialized.")
250
+ return []
251
+
252
+ commit_hashes = []
253
+ for commit in repo.iter_commits():
254
+ lines = commit.message.strip().split('\n')
255
+ last_line = lines[-1]
256
+ if file_name in last_line or (commit.message.startswith("<revert>") and file_name in commit.message):
257
+ commit_hash = commit.hexsha
258
+ commit_hashes.append(commit_hash)
259
+
260
+ return commit_hashes
261
+
235
262
 
236
263
  def get_commit_id_from_file(self, file_name: str) -> Optional[str]:
237
264
  """
@@ -243,16 +270,19 @@ class ActionYmlFileManager:
243
270
  Returns:
244
271
  Optional[str]: commit ID,如果计算失败返回 None
245
272
  """
246
- yaml_path = os.path.join(self.actions_dir, file_name)
247
-
248
- try:
249
- with open(yaml_path, 'r', encoding='utf-8') as f:
250
- yaml_content = f.read()
251
- file_md5 = hashlib.md5(yaml_content.encode("utf-8")).hexdigest()
252
- return f"auto_coder_{file_name}"
253
- except Exception as e:
254
- logger.error(f"Failed to calculate commit ID: {e}")
273
+ repo = get_repo(self.source_dir)
274
+ if repo is None:
275
+ logger.error("Repository is not initialized.")
255
276
  return None
277
+
278
+ commit_hash = None
279
+ # 这里遍历从最新的commit 开始遍历
280
+ for commit in repo.iter_commits():
281
+ last_line = commit.message.strip().split('\n')[-1]
282
+ if file_name in last_line and not commit.message.startswith("<revert>"):
283
+ commit_hash = commit.hexsha
284
+ break
285
+ return commit_hash
256
286
 
257
287
  def get_file_name_from_commit_id(self, commit_id: str) -> Optional[str]:
258
288
  """
@@ -298,7 +328,7 @@ class ActionYmlFileManager:
298
328
  return self.get_file_name_from_commit_id(last_line)
299
329
  except Exception as e:
300
330
  logger.error(f"Failed to extract file name from commit message: {e}")
301
- return None
331
+ return None
302
332
 
303
333
  def get_commit_changes(self, file_name: Optional[str] = None) -> List[Tuple[str, List[str], Dict[str, Tuple[str, str]]]]:
304
334
  """
@@ -327,31 +357,30 @@ class ActionYmlFileManager:
327
357
  changes = {}
328
358
  try:
329
359
  repo = git.Repo(self.source_dir)
330
- for commit in repo.iter_commits():
331
- if commit_id in commit.message:
332
- if commit.parents:
333
- parent = commit.parents[0]
334
- # 获取所有文件的前后内容
335
- for diff_item in parent.diff(commit):
336
- file_path = diff_item.a_path if diff_item.a_path else diff_item.b_path
337
-
338
- # 获取变更前内容
339
- before_content = None
340
- try:
341
- if diff_item.a_blob:
342
- before_content = repo.git.show(f"{parent.hexsha}:{file_path}")
343
- except git.exc.GitCommandError:
344
- pass # 文件可能是新增的
360
+ commit =repo.commit(commit_id)
361
+ if commit.parents:
362
+ parent = commit.parents[0]
363
+ # 获取所有文件的前后内容
364
+ for diff_item in parent.diff(commit):
365
+ file_path = diff_item.a_path if diff_item.a_path else diff_item.b_path
366
+
367
+ # 获取变更前内容
368
+ before_content = None
369
+ try:
370
+ if diff_item.a_blob:
371
+ before_content = repo.git.show(f"{parent.hexsha}:{file_path}")
372
+ except git.exc.GitCommandError:
373
+ pass # 文件可能是新增的
345
374
 
346
- # 获取变更后内容
347
- after_content = None
348
- try:
349
- if diff_item.b_blob:
350
- after_content = repo.git.show(f"{commit.hexsha}:{file_path}")
351
- except git.exc.GitCommandError:
352
- pass # 文件可能被删除
375
+ # 获取变更后内容
376
+ after_content = None
377
+ try:
378
+ if diff_item.b_blob:
379
+ after_content = repo.git.show(f"{commit.hexsha}:{file_path}")
380
+ except git.exc.GitCommandError:
381
+ pass # 文件可能被删除
353
382
 
354
- changes[file_path] = (before_content, after_content)
383
+ changes[file_path] = (before_content, after_content)
355
384
  break
356
385
  except git.exc.GitCommandError as e:
357
386
  self.printer.print_in_terminal("git_command_error", style="red", error=str(e))
@@ -360,6 +389,15 @@ class ActionYmlFileManager:
360
389
 
361
390
  return [(query, urls, changes)]
362
391
 
392
+ def revert_file(self, file_name: str) -> bool:
393
+ revert_result = git_utils.revert_changes(
394
+ self.source_dir, f"auto_coder_{file_name}"
395
+ )
396
+ if revert_result:
397
+ self.update_yaml_field(file_name, "revert_commit_id", revert_result.get("new_commit_hash"))
398
+ return True
399
+ return False
400
+
363
401
  def parse_history_tasks(self, limit: int = 5) -> List[Dict]:
364
402
  """
365
403
  解析历史任务信息
@@ -211,7 +211,7 @@ class CodeAutoGenerate:
211
211
 
212
212
  if self.args.enable_active_context:
213
213
  # 初始化活动上下文管理器
214
- active_context_manager = ActiveContextManager(self.llm, self.args)
214
+ active_context_manager = ActiveContextManager(self.llm, self.args.source_dir)
215
215
  # 获取活动上下文信息
216
216
  result = active_context_manager.load_active_contexts_for_files(
217
217
  [source.module_name for source in source_code_list.sources]
@@ -361,7 +361,7 @@ class CodeAutoGenerate:
361
361
 
362
362
  if self.args.enable_active_context:
363
363
  # 初始化活动上下文管理器
364
- active_context_manager = ActiveContextManager(self.llm, self.args)
364
+ active_context_manager = ActiveContextManager(self.llm, self.args.source_dir)
365
365
  # 获取活动上下文信息
366
366
  result = active_context_manager.load_active_contexts_for_files(
367
367
  [source.module_name for source in source_code_list.sources]
@@ -331,7 +331,7 @@ class CodeAutoGenerateDiff:
331
331
 
332
332
  if self.args.enable_active_context:
333
333
  # 初始化活动上下文管理器
334
- active_context_manager = ActiveContextManager(self.llm, self.args)
334
+ active_context_manager = ActiveContextManager(self.llm, self.args.source_dir)
335
335
  # 获取活动上下文信息
336
336
  result = active_context_manager.load_active_contexts_for_files(
337
337
  [source.module_name for source in source_code_list.sources]
@@ -508,7 +508,7 @@ class CodeAutoGenerateDiff:
508
508
 
509
509
  if self.args.enable_active_context:
510
510
  # 初始化活动上下文管理器
511
- active_context_manager = ActiveContextManager(self.llm, self.args)
511
+ active_context_manager = ActiveContextManager(self.llm, self.args.source_dir)
512
512
  # 获取活动上下文信息
513
513
  result = active_context_manager.load_active_contexts_for_files(
514
514
  [source.module_name for source in source_code_list.sources]
@@ -432,7 +432,7 @@ class CodeAutoGenerateEditBlock:
432
432
 
433
433
  source_content = source_code_list.to_str()
434
434
 
435
- active_context_manager = ActiveContextManager(self.llm, self.args)
435
+ active_context_manager = ActiveContextManager(self.llm, self.args.source_dir)
436
436
 
437
437
  if self.args.template == "common":
438
438
  # 获取包上下文信息
@@ -605,7 +605,7 @@ class CodeAutoGenerateEditBlock:
605
605
 
606
606
  if self.args.enable_active_context:
607
607
  # 初始化活动上下文管理器
608
- active_context_manager = ActiveContextManager(self.llm, self.args)
608
+ active_context_manager = ActiveContextManager(self.llm, self.args.source_dir)
609
609
  # 获取活动上下文信息
610
610
  result = active_context_manager.load_active_contexts_for_files(
611
611
  [source.module_name for source in source_code_list.sources]
@@ -301,7 +301,7 @@ class CodeAutoGenerateStrictDiff:
301
301
 
302
302
  if self.args.enable_active_context:
303
303
  # 初始化活动上下文管理器
304
- active_context_manager = ActiveContextManager(self.llm, self.args)
304
+ active_context_manager = ActiveContextManager(self.llm, self.args.source_dir)
305
305
  # 获取活动上下文信息
306
306
  result = active_context_manager.load_active_contexts_for_files(
307
307
  [source.module_name for source in source_code_list.sources]
@@ -480,7 +480,7 @@ class CodeAutoGenerateStrictDiff:
480
480
 
481
481
  if self.args.enable_active_context:
482
482
  # 初始化活动上下文管理器
483
- active_context_manager = ActiveContextManager(self.llm, self.args)
483
+ active_context_manager = ActiveContextManager(self.llm, self.args.source_dir)
484
484
  # 获取活动上下文信息
485
485
  result = active_context_manager.load_active_contexts_for_files(
486
486
  [source.module_name for source in source_code_list.sources]
@@ -213,8 +213,8 @@ class CodeAutoMerge:
213
213
  self.printer.print_in_terminal("yaml_save_error", style="red", yaml_file=action_file_name)
214
214
 
215
215
  if self.args.enable_active_context:
216
- active_context_manager = ActiveContextManager(self.llm, self.args)
217
- task_id = active_context_manager.process_changes()
216
+ active_context_manager = ActiveContextManager(self.llm, self.args.source_dir)
217
+ task_id = active_context_manager.process_changes(self.args)
218
218
  self.printer.print_in_terminal("active_context_background_task",
219
219
  style="blue",
220
220
  task_id=task_id)
@@ -596,8 +596,8 @@ class CodeAutoMergeDiff:
596
596
  self.printer.print_in_terminal("yaml_save_error", style="red", yaml_file=action_file_name)
597
597
 
598
598
  if self.args.enable_active_context:
599
- active_context_manager = ActiveContextManager(self.llm, self.args)
600
- task_id = active_context_manager.process_changes()
599
+ active_context_manager = ActiveContextManager(self.llm, self.args.source_dir)
600
+ task_id = active_context_manager.process_changes(self.args)
601
601
  self.printer.print_in_terminal("active_context_background_task",
602
602
  style="blue",
603
603
  task_id=task_id)
@@ -444,8 +444,8 @@ class CodeAutoMergeEditBlock:
444
444
  self.printer.print_in_terminal("yaml_save_error", style="red", yaml_file=action_file_name)
445
445
 
446
446
  if self.args.enable_active_context:
447
- active_context_manager = ActiveContextManager(self.llm, self.args)
448
- task_id = active_context_manager.process_changes()
447
+ active_context_manager = ActiveContextManager(self.llm, self.args.source_dir)
448
+ task_id = active_context_manager.process_changes(self.args)
449
449
  self.printer.print_in_terminal("active_context_background_task",
450
450
  style="blue",
451
451
  task_id=task_id)
@@ -303,8 +303,8 @@ class CodeAutoMergeStrictDiff:
303
303
  self.printer.print_in_terminal("yaml_save_error", style="red", yaml_file=action_file_name)
304
304
 
305
305
  if self.args.enable_active_context:
306
- active_context_manager = ActiveContextManager(self.llm, self.args)
307
- active_context_manager.process_changes()
306
+ active_context_manager = ActiveContextManager(self.llm, self.args.source_dir)
307
+ active_context_manager.process_changes(self.args)
308
308
 
309
309
  git_utils.print_commit_info(commit_result=commit_result)
310
310
  else:
@@ -13,6 +13,7 @@ from autocoder.utils.llms import get_llm_names, get_model_info
13
13
  from autocoder.common.types import CodeGenerateResult, MergeCodeWithoutEffect
14
14
  import os
15
15
  from autocoder.rag.token_counter import count_tokens
16
+ from autocoder.common.stream_out_type import CodeRankStreamOutType
16
17
 
17
18
  class RankResult(BaseModel):
18
19
  rank_result: List[int]
@@ -211,7 +212,10 @@ class CodeModificationRanker:
211
212
  model_name=model_name,
212
213
  title=self.printer.get_message_from_key_with_format(
213
214
  "rank_code_modification_title", model_name=model_name),
214
- args=self.args
215
+ args=self.args,
216
+ extra_meta={
217
+ "stream_out_type": CodeRankStreamOutType.CODE_RANK.value
218
+ }
215
219
  )
216
220
 
217
221
  if last_meta:
@@ -1,7 +1,8 @@
1
1
  import os
2
2
  from git import Repo, GitCommandError
3
+ import git
3
4
  from loguru import logger
4
- from typing import List, Optional, Dict
5
+ from typing import List, Optional, Dict, Any
5
6
  from pydantic import BaseModel
6
7
  import byzerllm
7
8
  from rich.console import Console
@@ -103,68 +104,84 @@ def get_current_branch(repo_path: str) -> str:
103
104
  return branch
104
105
 
105
106
 
106
- def revert_changes(repo_path: str, message: str) -> bool:
107
+ def revert_changes(repo_path: str, action_file_path: str) -> Optional[Any]:
108
+ '''
109
+ file_path 类似: auto_coder_000000002009_chat_action.yml 或者 000000002009_chat_action.yml
110
+ '''
107
111
  repo = get_repo(repo_path)
108
112
  if repo is None:
109
113
  logger.error("Repository is not initialized.")
110
114
  return False
111
-
115
+
116
+ commit_hash = None
117
+ # 这里遍历从最新的commit 开始遍历
118
+ for commit in repo.iter_commits():
119
+ if action_file_path in commit.message and not commit.message.startswith("<revert>"):
120
+ commit_hash = commit.hexsha
121
+ break
122
+
123
+ if commit_hash is None:
124
+ raise ValueError(f"File {action_file_path} not found in any commit")
125
+
126
+ # 尝试获取指定的提交
112
127
  try:
113
- # 检查当前工作目录是否有未提交的更改
114
- if repo.is_dirty():
115
- logger.warning(
116
- "Working directory is dirty. please commit or stash your changes before reverting."
117
- )
118
- return False
119
-
120
- # 通过message定位到commit_hash
121
- # --grep 默认只搜索第一行 -F 参数将搜索模式视为固定字符串而非正则表达式
122
- commit = repo.git.log("--all", f"--grep={message}", "-F", "--format=%H", "-n", "1")
123
- if not commit:
124
- logger.warning(f"No commit found with message: {message}")
125
- return False
126
-
127
- commit_hash = commit
128
-
129
- # 获取从指定commit到HEAD的所有提交
130
- commits = list(repo.iter_commits(f"{commit_hash}..HEAD"))
131
-
132
- if not commits:
133
- repo.git.revert(commit, no_edit=True)
134
- logger.info(f"Reverted single commit: {commit}")
128
+ commit = repo.commit(commit_hash)
129
+ except ValueError:
130
+ # 如果是短哈希,尝试匹配
131
+ matching_commits = [c for c in repo.iter_commits() if c.hexsha.startswith(commit_hash)]
132
+ if not matching_commits:
133
+ raise ValueError(f"Commit {commit_hash} not found")
134
+ commit = matching_commits[0]
135
+
136
+ # 检查工作目录是否干净
137
+ if repo.is_dirty():
138
+ raise ValueError("Working directory is dirty. please commit or stash your changes before reverting.")
139
+
140
+ try:
141
+ # 执行 git revert
142
+ # 使用 -n 选项不自动创建提交,而是让我们手动提交
143
+ repo.git.revert(commit.hexsha, no_commit=True)
144
+
145
+ # 创建带有信息的 revert 提交
146
+ revert_message = f"<revert>{commit.message.strip()}\n{commit.hexsha}"
147
+ new_commit = repo.index.commit(
148
+ revert_message,
149
+ author=repo.active_branch.commit.author,
150
+ committer=repo.active_branch.commit.committer
151
+ )
152
+
153
+ # 构建新提交的信息
154
+ stats = new_commit.stats.total
155
+ new_commit_info = {
156
+ "new_commit_hash": new_commit.hexsha,
157
+ "new_commit_short_hash": new_commit.hexsha[:7],
158
+ "reverted_commit": {
159
+ "hash": commit.hexsha,
160
+ "short_hash": commit.hexsha[:7],
161
+ "message": commit.message.strip()
162
+ },
163
+ "stats": {
164
+ "insertions": stats["insertions"],
165
+ "deletions": stats["deletions"],
166
+ "files_changed": stats["files"]
167
+ }
168
+ }
169
+
170
+ return new_commit_info
171
+
172
+ except git.GitCommandError as e:
173
+ # 如果发生 Git 命令错误,尝试恢复工作目录
174
+ try:
175
+ repo.git.reset("--hard", "HEAD")
176
+ except:
177
+ pass # 如果恢复失败,继续抛出原始错误
178
+
179
+ if "patch does not apply" in str(e):
180
+ raise Exception("Cannot revert: patch does not apply (likely due to conflicts)")
135
181
  else:
136
- # 从最新的提交开始,逐个回滚
137
- for commit in reversed(commits):
138
- try:
139
- repo.git.revert(commit.hexsha, no_commit=True)
140
- logger.info(f"Reverted changes from commit: {commit.hexsha}")
141
- except GitCommandError as e:
142
- logger.error(f"Error reverting commit {commit.hexsha}: {e}")
143
- repo.git.revert("--abort")
144
- return False
145
-
146
- # 提交所有的回滚更改
147
- repo.git.commit(message=f"Reverted all changes up to {commit_hash}")
148
-
149
- logger.info(f"Successfully reverted changes up to {commit_hash}")
150
-
151
- ## this is a mark, chat_auto_coder.py need this
152
- print(f"Successfully reverted changes", flush=True)
153
-
154
- # # 如果之前有stash,现在应用它
155
- # if stashed:
156
- # try:
157
- # repo.git.stash('pop')
158
- # logger.info("Applied stashed changes.")
159
- # except GitCommandError as e:
160
- # logger.error(f"Error applying stashed changes: {e}")
161
- # logger.info("Please manually apply the stashed changes.")
162
-
163
- return True
182
+ raise Exception(f"Git error during revert: {str(e)}")
164
183
 
165
- except GitCommandError as e:
166
- logger.error(f"Error during revert operation: {e}")
167
- return False
184
+ return None
168
185
 
169
186
 
170
187
  def revert_change(repo_path: str, message: str) -> bool:
@@ -9,3 +9,6 @@ class IndexFilterStreamOutType(Enum):
9
9
 
10
10
  class CodeGenerateStreamOutType(Enum):
11
11
  CODE_GENERATE = "code_generate"
12
+
13
+ class CodeRankStreamOutType(Enum):
14
+ CODE_RANK = "code_rank"
@@ -89,20 +89,19 @@ class ActiveContextManager:
89
89
  cls._instance._is_initialized = False
90
90
  return cls._instance
91
91
 
92
- def __init__(self, llm: byzerllm.ByzerLLM, args: AutoCoderArgs):
92
+ def __init__(self, llm: byzerllm.ByzerLLM,source_dir:str):
93
93
  """
94
94
  初始化活动上下文管理器
95
95
 
96
96
  Args:
97
- llm: ByzerLLM实例,用于生成文档内容
98
- args: AutoCoderArgs实例,包含配置信息
97
+ llm: ByzerLLM实例,用于生成文档内容
99
98
  """
100
99
  # 如果已经初始化过,则直接返回
101
100
  if self._is_initialized:
102
101
  return
103
-
102
+ self.source_dir = source_dir
104
103
  # 设置日志目录和文件
105
- log_dir = os.path.join(args.source_dir, ".auto-coder", "active-context")
104
+ log_dir = os.path.join(source_dir, ".auto-coder", "active-context")
106
105
  os.makedirs(log_dir, exist_ok=True)
107
106
  log_file = os.path.join(log_dir, "active.log")
108
107
 
@@ -119,12 +118,11 @@ class ActiveContextManager:
119
118
  self.logger = global_logger.bind(name="ActiveContextManager")
120
119
  self.logger.info(f"初始化 ActiveContextManager,日志输出到 {log_file}")
121
120
 
122
- self.llm = llm
123
- self.args = args
121
+ self.llm = llm
124
122
  self.directory_mapper = DirectoryMapper()
125
123
  self.active_package = ActivePackage(llm)
126
124
  self.async_processor = AsyncProcessor()
127
- self.yml_manager = ActionYmlFileManager(args.source_dir)
125
+ self.yml_manager = ActionYmlFileManager(source_dir)
128
126
  self.tasks = {} # 用于跟踪任务状态
129
127
  self.printer = Printer()
130
128
 
@@ -137,7 +135,7 @@ class ActiveContextManager:
137
135
  self.__class__._queue_thread.start()
138
136
 
139
137
  # 标记为已初始化
140
- self._is_initialized = True
138
+ self._is_initialized = True
141
139
 
142
140
  def _process_queue(self):
143
141
  """
@@ -177,20 +175,22 @@ class ActiveContextManager:
177
175
  with self._queue_lock:
178
176
  self.__class__._is_processing = False
179
177
 
180
- def process_changes(self, file_name: Optional[str] = None) -> str:
178
+ def process_changes(self, args:AutoCoderArgs) -> str:
181
179
  """
182
180
  处理代码变更,创建活动上下文(非阻塞)
183
181
 
184
182
  Args:
185
- file_name: YAML文件名,如果为None则使用args.file
183
+ args: AutoCoderArgs实例,包含配置信息
186
184
 
187
185
  Returns:
188
186
  str: 任务ID,可用于后续查询任务状态
189
187
  """
190
188
  try:
191
189
  # 使用参数中的文件或者指定的文件
192
- file_name = file_name or os.path.basename(self.args.file)
190
+ if not args.file:
191
+ raise ValueError("action file is required")
193
192
 
193
+ file_name = os.path.basename(args.file)
194
194
  # 从YAML文件加载数据
195
195
  yaml_content = self.yml_manager.load_yaml_content(file_name)
196
196
 
@@ -298,7 +298,7 @@ class ActiveContextManager:
298
298
  # 1. 映射目录
299
299
  self.logger.info("开始映射目录结构...")
300
300
  directory_contexts = self.directory_mapper.map_directories(
301
- self.args.source_dir, changed_urls, current_urls
301
+ self.source_dir, changed_urls, current_urls
302
302
  )
303
303
  self.logger.info(f"目录映射完成,找到 {len(directory_contexts)} 个相关目录")
304
304
 
@@ -460,7 +460,7 @@ class ActiveContextManager:
460
460
  # 构建日志文件路径
461
461
  log_file_path = None
462
462
  if 'file_name' in task:
463
- log_dir = os.path.join(self.args.source_dir, '.auto-coder', 'active-context', 'logs')
463
+ log_dir = os.path.join(self.source_dir, '.auto-coder', 'active-context', 'logs')
464
464
  log_file_path = os.path.join(log_dir, f'{task_id}.log')
465
465
  if not os.path.exists(log_file_path):
466
466
  log_file_path = None
@@ -644,6 +644,6 @@ class ActiveContextManager:
644
644
  Returns:
645
645
  str: 活动上下文中对应的目录路径
646
646
  """
647
- relative_path = os.path.relpath(directory_path, self.args.source_dir)
648
- return os.path.join(self.args.source_dir, ".auto-coder", "active-context", relative_path)
647
+ relative_path = os.path.relpath(directory_path, self.source_dir)
648
+ return os.path.join(self.source_dir, ".auto-coder", "active-context", relative_path)
649
649
 
autocoder/version.py CHANGED
@@ -1 +1 @@
1
- __version__ = "0.1.306"
1
+ __version__ = "0.1.307"