nexforge-cli 0.2.14__tar.gz → 0.2.16__tar.gz

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.
Files changed (200) hide show
  1. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/PKG-INFO +2 -2
  2. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/README.md +1 -1
  3. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/manual/01-/345/256/211/350/243/205/344/270/216/351/205/215/347/275/256.md +1 -1
  4. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/manual/02-/347/225/214/351/235/242/344/270/216/345/277/253/346/215/267/351/224/256.md +20 -4
  5. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/manual/03-/346/250/241/345/236/213/351/205/215/347/275/256.md +14 -1
  6. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/manual/README.md +1 -1
  7. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/pyproject.toml +1 -1
  8. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/__init__.py +1 -1
  9. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/core/goal_manager.py +7 -0
  10. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/features/mode_features.py +47 -11
  11. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/features/session_feature.py +22 -3
  12. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/i18n.py +20 -3
  13. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/provider/deepseek.py +22 -4
  14. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/ui/screens/chat.py +111 -9
  15. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/ui/screens/chat_actions.py +42 -0
  16. nexforge_cli-0.2.16/src/nexforge/ui/widgets/file_browser.py +163 -0
  17. nexforge_cli-0.2.16/src/nexforge/ui/widgets/file_menu.py +74 -0
  18. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/ui/widgets/multiline_prompt.py +4 -1
  19. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/tests/test_cross_platform.py +14 -1
  20. nexforge_cli-0.2.16/tests/test_deepseek_provider.py +54 -0
  21. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/tests/test_director_loop.py +22 -0
  22. nexforge_cli-0.2.16/tests/test_file_browser.py +197 -0
  23. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/tests/test_goal_confirm.py +74 -1
  24. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/tests/test_session_store.py +82 -0
  25. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/.gitignore +0 -0
  26. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/INSTALL.md +0 -0
  27. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/STATS_API.md +0 -0
  28. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/assets/icons/agent.svg +0 -0
  29. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/assets/icons/cost.svg +0 -0
  30. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/assets/icons/edit.svg +0 -0
  31. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/assets/icons/err.svg +0 -0
  32. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/assets/icons/file.svg +0 -0
  33. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/assets/icons/model.svg +0 -0
  34. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/assets/icons/ok.svg +0 -0
  35. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/assets/icons/read.svg +0 -0
  36. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/assets/icons/running.svg +0 -0
  37. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/assets/icons/search.svg +0 -0
  38. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/assets/icons/shell.svg +0 -0
  39. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/assets/icons/thinking.svg +0 -0
  40. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/assets/icons/web.svg +0 -0
  41. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/assets/icons/write.svg +0 -0
  42. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/config.example.toml +0 -0
  43. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/devlog/2026-07-07.md +0 -0
  44. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/doc//344/273/243/347/240/201/344/274/230/345/214/226/350/256/241/345/210/222.md" +0 -0
  45. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/doc//347/211/271/346/200/247/346/250/241/345/235/227/346/263/250/345/206/214/345/210/266-/345/256/236/346/226/275/350/256/241/345/210/222.md" +0 -0
  46. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/doc//351/241/271/347/233/256/346/200/273/347/233/221/345/276/252/347/216/257-/345/274/200/345/217/221/350/256/241/345/210/222.md" +0 -0
  47. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/docs/submission/README-/346/212/225/347/250/277/350/257/264/346/230/216.md" +0 -0
  48. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/docs/submission/nexforge.md +0 -0
  49. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/docs/submission/nexforge.zh-CN.md +0 -0
  50. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/manual/04-/345/267/245/345/205/267/345/217/202/350/200/203.md" +0 -0
  51. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/manual/05-/346/225/260/346/215/256/344/270/216/346/220/234/347/264/242.md" +0 -0
  52. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/manual/06-/346/235/203/351/231/220/344/270/216/346/250/241/345/274/217.md" +0 -0
  53. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/manual/07-/344/274/232/350/257/235/344/270/216/350/256/260/345/277/206.md" +0 -0
  54. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/manual/08-/350/207/252/345/256/232/344/271/211/351/205/215/347/275/256.md" +0 -0
  55. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/manual/09-/345/270/270/350/247/201/351/227/256/351/242/230.md" +0 -0
  56. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/manual/10-/346/217/222/344/273/266/345/274/200/345/217/221.md" +0 -0
  57. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/manual//351/231/204/345/275/225-/345/257/271/346/257/224/345/210/206/346/236/220.md" +0 -0
  58. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/plugin_gui_auto/README.md +0 -0
  59. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/plugin_gui_auto/guiauto/__init__.py +0 -0
  60. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/plugin_gui_auto/guiauto/action.py +0 -0
  61. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/plugin_gui_auto/guiauto/base.py +0 -0
  62. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/plugin_gui_auto/guiauto/tools.py +0 -0
  63. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/plugin_gui_auto/guiauto/vision.py +0 -0
  64. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/plugin_gui_auto/guiauto/window.py +0 -0
  65. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/plugin_gui_auto/install_plugin.py +0 -0
  66. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/plugin_gui_auto/requirements.txt +0 -0
  67. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/plugin_gui_auto/smoke_safe.py +0 -0
  68. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/plugin_gui_auto/verify_final.py +0 -0
  69. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/plugin_gui_auto/verify_host.py +0 -0
  70. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/plugin_gui_auto/verify_plugin.py +0 -0
  71. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/scripts/publish.sh +0 -0
  72. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/scripts/smoke_provider.py +0 -0
  73. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/scripts/smoke_tui.py +0 -0
  74. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/__main__.py +0 -0
  75. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/app.py +0 -0
  76. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/cli.py +0 -0
  77. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/config.py +0 -0
  78. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/core/__init__.py +0 -0
  79. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/core/agent.py +0 -0
  80. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/core/context.py +0 -0
  81. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/core/federation.py +0 -0
  82. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/core/jobs.py +0 -0
  83. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/core/knowledge_base.py +0 -0
  84. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/core/memory_engine.py +0 -0
  85. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/core/model_catalog.py +0 -0
  86. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/core/permissions.py +0 -0
  87. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/core/plugin.py +0 -0
  88. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/core/project_mapper.py +0 -0
  89. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/core/provider.py +0 -0
  90. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/core/router.py +0 -0
  91. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/core/session.py +0 -0
  92. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/core/summarizer.py +0 -0
  93. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/core/system_inject.py +0 -0
  94. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/core/tool_manifest.py +0 -0
  95. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/core/types.py +0 -0
  96. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/core/update_check.py +0 -0
  97. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/features/__init__.py +0 -0
  98. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/features/auto_skills.py +0 -0
  99. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/features/base.py +0 -0
  100. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/features/federation_feature.py +0 -0
  101. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/features/kb_feature.py +0 -0
  102. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/features/map_feature.py +0 -0
  103. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/features/memory_feature.py +0 -0
  104. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/features/plan_feature.py +0 -0
  105. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/features/registry.py +0 -0
  106. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/features/slash_feature.py +0 -0
  107. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/features/stats_feature.py +0 -0
  108. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/features/tool_manifest_feature.py +0 -0
  109. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/icons.py +0 -0
  110. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/mcp/__init__.py +0 -0
  111. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/provider/__init__.py +0 -0
  112. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/provider/_retry.py +0 -0
  113. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/provider/anthropic.py +0 -0
  114. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/provider/balance.py +0 -0
  115. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/provider/openai.py +0 -0
  116. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/provider/qianwen.py +0 -0
  117. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/skills/loader.py +0 -0
  118. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/stats/__init__.py +0 -0
  119. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/stats/heartbeat.py +0 -0
  120. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/store/__init__.py +0 -0
  121. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/store/director_store.py +0 -0
  122. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/store/plan_store.py +0 -0
  123. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/store/session_store.py +0 -0
  124. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/theme.tcss +0 -0
  125. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/tools/__init__.py +0 -0
  126. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/tools/_paths.py +0 -0
  127. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/tools/background.py +0 -0
  128. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/tools/base.py +0 -0
  129. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/tools/data_tools.py +0 -0
  130. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/tools/edit_file.py +0 -0
  131. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/tools/federation.py +0 -0
  132. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/tools/fs_ops.py +0 -0
  133. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/tools/git.py +0 -0
  134. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/tools/image_tools.py +0 -0
  135. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/tools/kb_tools.py +0 -0
  136. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/tools/list_dir.py +0 -0
  137. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/tools/multi_edit.py +0 -0
  138. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/tools/ops_tools.py +0 -0
  139. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/tools/read_file.py +0 -0
  140. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/tools/registry.py +0 -0
  141. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/tools/screenshot_tools.py +0 -0
  142. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/tools/search.py +0 -0
  143. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/tools/search_content.py +0 -0
  144. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/tools/search_files.py +0 -0
  145. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/tools/shell.py +0 -0
  146. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/tools/web.py +0 -0
  147. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/tools/write_file.py +0 -0
  148. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/ui/__init__.py +0 -0
  149. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/ui/clipboard_image.py +0 -0
  150. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/ui/screen_api.py +0 -0
  151. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/ui/screens/__init__.py +0 -0
  152. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/ui/screens/splash.py +0 -0
  153. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/ui/slash_handler.py +0 -0
  154. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/ui/widgets/__init__.py +0 -0
  155. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/ui/widgets/apikey_modal.py +0 -0
  156. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/ui/widgets/banner.py +0 -0
  157. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/ui/widgets/confirm_modal.py +0 -0
  158. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/ui/widgets/diff_modal.py +0 -0
  159. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/ui/widgets/image_sidebar.py +0 -0
  160. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/ui/widgets/messages.py +0 -0
  161. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/ui/widgets/mode_warning.py +0 -0
  162. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/ui/widgets/model_sidebar.py +0 -0
  163. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/ui/widgets/multiline_modal.py +0 -0
  164. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/ui/widgets/plan_sidebar.py +0 -0
  165. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/ui/widgets/sidebar.py +0 -0
  166. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/ui/widgets/statusbar.py +0 -0
  167. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/utils/__init__.py +0 -0
  168. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/utils/logging.py +0 -0
  169. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/web/__init__.py +0 -0
  170. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/web/server.py +0 -0
  171. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/src/nexforge/web/static/index.html +0 -0
  172. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/tests/conftest.py +0 -0
  173. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/tests/test_app_mouse.py +0 -0
  174. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/tests/test_approval.py +0 -0
  175. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/tests/test_config.py +0 -0
  176. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/tests/test_director_store.py +0 -0
  177. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/tests/test_dual_panel.py +0 -0
  178. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/tests/test_federation.py +0 -0
  179. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/tests/test_i18n.py +0 -0
  180. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/tests/test_icons.py +0 -0
  181. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/tests/test_lang_cmd.py +0 -0
  182. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/tests/test_m2_tools.py +0 -0
  183. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/tests/test_manual_consistency.py +0 -0
  184. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/tests/test_model_catalog.py +0 -0
  185. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/tests/test_p2_core.py +0 -0
  186. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/tests/test_plugin.py +0 -0
  187. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/tests/test_sanitize.py +0 -0
  188. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/tests/test_screenshot_tools.py +0 -0
  189. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/tests/test_session_repair.py +0 -0
  190. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/tests/test_sidebar.py +0 -0
  191. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/tests/test_sidebar_actions.py +0 -0
  192. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/tests/test_splash_modal.py +0 -0
  193. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/tests/test_statusbar.py +0 -0
  194. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/tests/test_system_inject.py +0 -0
  195. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/tests/test_tool_iters.py +0 -0
  196. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/tests/test_tools.py +0 -0
  197. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/tests/test_tui.py +0 -0
  198. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/tests/test_update_check.py +0 -0
  199. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/tests/test_vision_model.py +0 -0
  200. {nexforge_cli-0.2.14 → nexforge_cli-0.2.16}/tests/test_vision_tool_retention.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: nexforge-cli
3
- Version: 0.2.14
3
+ Version: 0.2.16
4
4
  Summary: NexForgeCLI — 一个面向 DeepSeek V4 的终端智能体(TUI + CLI 双模式)
5
5
  Project-URL: Homepage, https://gitee.com/Lighthorn/nexforgecli
6
6
  Author: NexForge
@@ -51,7 +51,7 @@ Description-Content-Type: text/markdown
51
51
 
52
52
  NexForgeCLI 是一个运行在终端里的智能体助手,既支持 DeepSeek/千问等多模型,也是**插件宿主**(plugins 目录即插即用)。既能作为全屏 TUI 交互式对话/编码,也能作为 one-shot 命令行工具嵌进脚本和管道。
53
53
 
54
- > 当前版本:**v0.2.14** · PyPI: `pip install nexforge-cli`
54
+ > 当前版本:**v0.2.16** · PyPI: `pip install nexforge-cli`
55
55
 
56
56
  ---
57
57
 
@@ -8,7 +8,7 @@
8
8
 
9
9
  NexForgeCLI 是一个运行在终端里的智能体助手,既支持 DeepSeek/千问等多模型,也是**插件宿主**(plugins 目录即插即用)。既能作为全屏 TUI 交互式对话/编码,也能作为 one-shot 命令行工具嵌进脚本和管道。
10
10
 
11
- > 当前版本:**v0.2.14** · PyPI: `pip install nexforge-cli`
11
+ > 当前版本:**v0.2.16** · PyPI: `pip install nexforge-cli`
12
12
 
13
13
  ---
14
14
 
@@ -1,6 +1,6 @@
1
1
  # 安装与配置
2
2
 
3
- > NexForgeCLI v0.2.14
3
+ > NexForgeCLI v0.2.16
4
4
 
5
5
  ## 环境要求
6
6
 
@@ -9,10 +9,10 @@
9
9
  │ [用户消息] │
10
10
  │ [AI 回复 · 思考面板(可折叠) · 工具调用卡片] │
11
11
  │ │
12
- │ ── 本轮结束 · F4 复制最后回复 │
12
+ │ ── 本轮结束 · Ctrl+C 复制最后回复 │
13
13
  ├── 输入区 ────────────────────────────────────────────────┤
14
14
  │ > 请输入… │
15
- │ F1帮助 · F2模式 · F3编辑器 · F5会话 · F6模型 · F10快捷回复│
15
+ │ F1帮助 · F2模式 · F3编辑 · F4文件 · F5会话 · F6模型 · F10快捷回复│
16
16
  ├── 状态栏 ────────────────────────────────────────────────┤
17
17
  │ 🔥 全自动 · deepseek · flash · 思考:开 · 8k token · ¥0.00│
18
18
  └──────────────────────────────────────────────────────────┘
@@ -43,19 +43,35 @@
43
43
  | `F1` | 帮助 |
44
44
  | `F2` | 切换审批模式(审批/半自动/全自动) |
45
45
  | `F3` | 系统默认编辑器编辑文本 |
46
- | `F4` | 复制最后一条 AI 回复 |
46
+ | `F4` | 文件浏览器侧边栏(点击引用/右键编辑) |
47
47
  | `F5` | 会话侧边栏(切换/重命名/删除) |
48
48
  | `F6` | 模型选择侧边栏 |
49
49
  | `F7` | 切换运维模式 |
50
50
  | `F8` | 切换目标模式(总监循环) |
51
51
  | `F9` | 粘贴截图 |
52
52
  | `F10` | 激活快捷回复 |
53
- | `Ctrl+C` | 复制最后 AI 回复(备选键) |
53
+ | `Ctrl+C` | 复制最后 AI 回复 |
54
54
  | `Ctrl+L` | 清屏 |
55
55
  | `↑/↓` | 滚动对话 |
56
56
  | `PageUp/PageDown` | 上下翻页 |
57
57
  | `End` | 滚到底部 |
58
58
 
59
+ ## 文件浏览器
60
+
61
+ 按 `F4` 打开右侧文件浏览器。打开后**直接显示项目根下的文件与目录**(树形,自动隐藏 `.git`、`__pycache__`、`.nexforge` 等噪音目录):
62
+
63
+ | 操作 | 效果 |
64
+ |------|------|
65
+ | 点击文件 | 引用文件路径到输入框,可继续输入指令 |
66
+ | 点击目录 | 展开/折叠该目录 |
67
+ | 右键文件 | 弹出操作菜单(引用路径 / 打开编辑 / 复制路径 / 复制内容) |
68
+ | 右键目录 | 弹出操作菜单(引用目录路径 / 在系统文件管理器中打开 / 复制路径) |
69
+
70
+ - **引用**:把文件/目录路径(项目根内为相对路径)插入输入框,聚焦后可直接补充"分析这个文件"之类指令
71
+ - **打开编辑**:文件用系统默认程序打开(Windows 记事本/关联程序、macOS `open`、Linux `xdg-open`);目录则在系统文件管理器中打开
72
+ - **复制内容**:复制文件文本内容到剪贴板(超过 512KB 拒绝,防止卡顿)
73
+ - 键盘:F4 打开后焦点即在文件树,`↑/↓` 移动、`→` 展开、`Enter` 引用选中项、`Esc` 关闭并回输入框
74
+
59
75
  ## 斜杠命令
60
76
 
61
77
  **模型与账户**
@@ -4,7 +4,7 @@ NexForgeCLI 支持多模型:
4
4
 
5
5
  | 模型 | 支持方式 | 说明 |
6
6
  |------|----------|------|
7
- | **DeepSeek V4** | 内置 | flash/pro 双档位 + auto 自动路由,无需额外配置 |
7
+ | **DeepSeek V4** | 内置(models.toml 可扩展) | flash/pro/auto 档位开箱可用;也可在 models.toml 挂独立模型(如实验版) |
8
8
  | **千问(qianwen)** | 专用底座 | 思考链流式显示 / enable_thinking 开关 / 多模态贴图直发(与 DeepSeek 平级) |
9
9
  | **OpenAI 兼容** | models.toml | Kimi/GPT/通义千问/智谱等一切 OpenAI 兼容接口 |
10
10
  | **Anthropic** | models.toml | Claude 系列(需安装额外依赖) |
@@ -34,6 +34,19 @@ auto 模式下的升级触发条件:
34
34
  - 默认开启,实时显示 AI 的推理过程(可折叠面板)
35
35
  - `/think off` 关闭思考(速度更快、费用更低)
36
36
 
37
+ **挂独立模型**:DeepSeek 的 flash/pro 是内置档位,若想使用内置档位之外的模型(如限时实验版 `deepseek-v4.1-flash-expires-on-0910`),可在 `~/.nexforge/models.toml` 加一个 `type = "deepseek"` 的 provider:
38
+
39
+ ```toml
40
+ [[provider]]
41
+ name = "deepseek-exp"
42
+ type = "deepseek"
43
+ base_url = "https://api.deepseek.com"
44
+ models = ["deepseek-v4.1-flash-expires-on-0910"]
45
+ default_model = "deepseek-v4.1-flash-expires-on-0910"
46
+ ```
47
+
48
+ provider 的模型解析优先级:显式 `/model <名>` > `default_model` > `models` 第一个 > 内置 flash/pro。未配置 api_key 时自动回退全局 Key(`auth.toml`)。重启后在 F6 侧边栏点选即可切换。
49
+
37
50
  ## 千问(qianwen 专用底座)
38
51
 
39
52
  千问AI平台(platform.qianwenai.com / 阿里云百炼)有专用 provider,深度匹配千问特性:
@@ -1,4 +1,4 @@
1
- # NexForgeCLI 用户手册 · v0.2.14
1
+ # NexForgeCLI 用户手册 · v0.2.16
2
2
 
3
3
  ## 这是什么
4
4
 
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "nexforge-cli"
7
- version = "0.2.14"
7
+ version = "0.2.16"
8
8
  description = "NexForgeCLI — 一个面向 DeepSeek V4 的终端智能体(TUI + CLI 双模式)"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -3,5 +3,5 @@
3
3
  TUI(全屏交互)+ CLI(one-shot / 管道)双模式。
4
4
  """
5
5
 
6
- __version__ = "0.2.14"
6
+ __version__ = "0.2.16"
7
7
  __all__ = ["__version__"]
@@ -9,6 +9,13 @@ from datetime import datetime, timezone
9
9
 
10
10
  DEFAULT_MAX_ROUNDS = 100
11
11
 
12
+ # 目标/计划注入总监与开发者上下文的截断上限(字符)。
13
+ # 目标必须完整传递: 长目标下截断会丢失后半部分(任务边界/次要目标/灵活处理等),
14
+ # 导致总监/开发者做出错误判断(历史 bug: [:200]/[:300] 截断致目标信息丢失)。
15
+ # 计划是中间产物(总监会重新生成), 可适当截断以控制 token。
16
+ GOAL_CTX_LIMIT = 4000
17
+ PLAN_CTX_LIMIT = 2000
18
+
12
19
  DIRECTOR_PROMPT = """[总监模式]
13
20
  你是项目总监,负责对整个开发周期做全局把控、目标分解和质量评审。
14
21
 
@@ -9,6 +9,7 @@ from nexforge.features.base import Feature
9
9
  from nexforge.config import save_config
10
10
  from nexforge.i18n import t
11
11
  from nexforge.core.context import get_project_root
12
+ from nexforge.core.goal_manager import GOAL_CTX_LIMIT, PLAN_CTX_LIMIT
12
13
  from nexforge.core.memory_engine import MemoryEngine, _MARKER_OPS, _MARKER_GOAL, clear_messages_by_marker, has_marker
13
14
 
14
15
  if TYPE_CHECKING:
@@ -106,6 +107,7 @@ class GoalModeFeature:
106
107
  if gm.rounds_dir.exists():
107
108
  shutil.rmtree(gm.rounds_dir, ignore_errors=True)
108
109
  gm.rounds_dir.mkdir(parents=True, exist_ok=True)
110
+ self._stop_director = False # 复位停止标志, 防残留导致恢复的循环立即退出
109
111
  self._turn_count = 1
110
112
  # 清理残留状态(防上次崩溃遗留 dev_done_flag 导致重复验收)
111
113
  self._dev_done_flag = False
@@ -246,14 +248,19 @@ class GoalModeFeature:
246
248
  from nexforge.core.goal_manager import GoalManager
247
249
  gm = GoalManager(get_project_root())
248
250
  goal = gm.read_goal()
249
- gm.set_goal(goal + "\n\n[用户补充意见]\n" + feedback[:500])
251
+ gm.set_goal(goal + "\n\n[用户补充意见]\n" + feedback[:2000])
250
252
  self._awaiting_plan_confirm = False
251
253
  self._turn_count = 0 # 让重新规划回复再次作为 turn=1(方案轮)
252
254
  s._note(t("note.goal_plan_amended"), scroll=True)
255
+ # 修复: 用户反馈必须完整传给总监, 不能依赖 read_goal()[:300] 截断。
256
+ # 旧实现把反馈追加到 goal 文件末尾后, 仅取前 300 字符作为 prompt,
257
+ # 而反馈永远位于 300 字符之后 → 总监收不到修改意见
258
+ # (表现为"传输截断/未检测到新的修改意见正文")。
253
259
  s._run_agent(
254
260
  "[项目总监] 用户对初步方案提出了修改意见, 请结合意见重新规划方案。"
255
261
  "注意: 先输出规划方案, 不要发布执行指令, 等待用户确认后再动工。\n"
256
- "目标:" + gm.read_goal()[:300]
262
+ "【原始目标】\n" + goal +
263
+ "\n\n【用户本次修改意见】\n" + feedback[:2000]
257
264
  )
258
265
 
259
266
  def _fed_members_prompt(self) -> str:
@@ -348,8 +355,8 @@ class GoalModeFeature:
348
355
  from nexforge.core.goal_manager import GoalManager
349
356
  s = self.screen
350
357
  gm = GoalManager(get_project_root())
351
- goal = gm.read_goal()[:300]
352
- plan = gm.read_plan()[:300]
358
+ goal = gm.read_goal()[:GOAL_CTX_LIMIT]
359
+ plan = gm.read_plan()[:PLAN_CTX_LIMIT]
353
360
  fed_members = self._fed_members_prompt() # 联结体成员(跨项目编排)
354
361
  ctx = (
355
362
  "[项目总监] 首次目标分解。\n"
@@ -387,13 +394,13 @@ class GoalModeFeature:
387
394
  max_r = s.config.ui.max_rounds
388
395
  cycle_n = self._turn_count - 1
389
396
  report = (report or self._dev_report)[:2000]
390
- goal = gm.read_goal()[:300]
391
- plan = gm.read_plan()[:300]
397
+ goal = gm.read_goal()[:GOAL_CTX_LIMIT]
398
+ plan = gm.read_plan()[:PLAN_CTX_LIMIT]
392
399
  fed_members = self._fed_members_prompt()
393
400
  ctx = (
394
401
  "[项目总监复盘] 第" + str(cycle_n) + "/" + str(max_r) + "轮。\n"
395
402
  "目标:" + goal + "\n"
396
- "计划:" + plan[:500] + "\n"
403
+ "计划:" + plan + "\n"
397
404
  "开发者报告:" + report + "\n\n"
398
405
  "请按审核框架复盘:\n"
399
406
  "1. 本轮完成了多少?(核心/重要/次要)\n"
@@ -435,7 +442,7 @@ class GoalModeFeature:
435
442
  from nexforge.core.goal_manager import GoalManager
436
443
  s = self.screen
437
444
  gm = GoalManager(get_project_root())
438
- goal = gm.read_goal()[:300]
445
+ goal = gm.read_goal()[:GOAL_CTX_LIMIT]
439
446
  dev_status = "开发者正在执行中" if self._dev_busy else "开发者空闲, 等待下一任务"
440
447
  ctx = (
441
448
  "[项目总监] 用户在工作进行中与你沟通(下栏沟通区)。\n"
@@ -461,15 +468,15 @@ class GoalModeFeature:
461
468
  s = self.screen
462
469
  gm = GoalManager(get_project_root())
463
470
  max_r = s.config.ui.max_rounds
464
- goal = gm.read_goal()[:300]
465
- plan = gm.read_plan()[:300]
471
+ goal = gm.read_goal()[:GOAL_CTX_LIMIT]
472
+ plan = gm.read_plan()[:PLAN_CTX_LIMIT]
466
473
  s.call_later(s._mount, DirectorNote(
467
474
  t("note.dev_round", n=str(round_n), mx=str(max_r))
468
475
  ), dev=True)
469
476
  msg = (
470
477
  "[开发者] 第" + str(round_n) + "/" + str(max_r) + "轮。\n"
471
478
  "目标:" + goal + "\n"
472
- "计划:" + plan[:500] + "\n"
479
+ "计划:" + plan + "\n"
473
480
  "总监指令:" + instruction[:2000] + "\n\n"
474
481
  "请按指令执行。完成后提交执行报告。"
475
482
  "若遇到不可逾越的障碍,在报告开头标注 [BLOCKED]。"
@@ -502,6 +509,35 @@ class GoalModeFeature:
502
509
  self._resume_director_loop()
503
510
  self._wake_event.set()
504
511
 
512
+ def reset_for_session_change(self) -> None:
513
+ """会话切换/新建时完整重置目标模式运行状态(不关闭 goal_mode 开关)。
514
+
515
+ 目标文件(goal/plan/rounds)是项目级跨会话共享,但总监循环是内存态。
516
+ 切换会话时旧循环被 cancel, 若运行状态(_turn_count/_dev_done_flag/
517
+ _stop_director 等)残留, 新会话输入会走错分支:
518
+ - _turn_count 残留 → 方案生成后 on_turn_done 跳过 turn=1 分支,
519
+ 方案不落盘、不进入待确认, 总监循环无法启动
520
+ - 旧目标文件残留 → 新会话输入被误判为"旧目标插话"入队, 循环又没恢复
521
+ 结果都表现为"AI 回复一段就停, 必须反复输入才能继续"。
522
+ 这里清零运行状态 + 清理目标文件, 让切换后输入作为全新目标进入正常流程。
523
+ """
524
+ self._stop_director = False # 关键: 复位停止标志, 否则恢复的循环立即退出
525
+ self._awaiting_plan_confirm = False
526
+ self._director_busy = False
527
+ self._turn_count = 0 # 关键: 复位轮次, 否则方案生成后 on_turn_done 走错分支
528
+ self._user_inbox.clear()
529
+ self._dev_busy = False
530
+ self._dev_done_flag = False
531
+ self._dev_report = ""
532
+ self._director_loop_task = None
533
+ self._wake_event.clear()
534
+ # 清理目标文件, 防旧目标残留被误判为插话
535
+ try:
536
+ from nexforge.core.goal_manager import GoalManager
537
+ GoalManager(get_project_root()).cleanup()
538
+ except Exception:
539
+ pass
540
+
505
541
  def _stop_director_loop(self) -> None:
506
542
  """停止总监循环。"""
507
543
  self._stop_director = True
@@ -109,7 +109,17 @@ class SessionFeature:
109
109
  s._director_session_id = ""
110
110
  s.session = Session()
111
111
  s.session.messages = list(msgs)
112
- if not msgs or msgs[0].get("role") != "system":
112
+ # 系统提示判断: 摘要 system 消息(历史压缩)不算完整系统提示,
113
+ # 必须注入 DEFAULT_SYSTEM(否则 AI 缺失行为规范, 只回复一段就停)。
114
+ # 与 _auto_resume 保持一致 —— 原判断只看首条 role, 而压缩过的会话
115
+ # 首条是 "[历史摘要" system 消息(save_session 只回显摘要 system),
116
+ # 会误判为已有 system 而漏注入 DEFAULT_SYSTEM。
117
+ has_full_sys = any(
118
+ m.get("role") == "system"
119
+ and str(m.get("content", "")).startswith("【推理要求】")
120
+ for m in s.session.messages
121
+ )
122
+ if not has_full_sys:
113
123
  s.session.messages.insert(0, {"role": "system", "content": DEFAULT_SYSTEM})
114
124
  s._session_id = info.id
115
125
  s._session_name = info.name
@@ -145,11 +155,20 @@ class SessionFeature:
145
155
  # ---- 自动保存 ----
146
156
 
147
157
  def _reset_goal_confirm(self) -> None:
148
- """切换/新建会话时重置目标模式方案确认状态(防残留误路由)。"""
158
+ """切换/新建会话时完整重置目标模式运行状态(防残留误路由)。
159
+
160
+ 修复: 之前只重置 _awaiting_plan_confirm, 而 _turn_count/_dev_done_flag/
161
+ _stop_director 等运行状态与旧目标文件(项目级)残留, 导致切换会话后
162
+ 总监循环无法正确恢复, 表现为"AI 回复一段就停, 必须反复输入才能继续"。
163
+ """
149
164
  try:
150
165
  gf = next((f for f in self.screen._features._features
151
166
  if getattr(f, 'name', '') == 'goal_mode'), None)
152
- if gf is not None:
167
+ if gf is None:
168
+ return
169
+ if hasattr(gf, 'reset_for_session_change'):
170
+ gf.reset_for_session_change()
171
+ else:
153
172
  gf._awaiting_plan_confirm = False
154
173
  except Exception:
155
174
  pass
@@ -50,8 +50,8 @@ def t(key: str, **fmt: str) -> str:
50
50
  MESSAGES: dict[str, dict[str, str]] = {
51
51
  # ---- 底部提示行 --------------------------------------------------------
52
52
  "ui.prompt_hint": {
53
- "zh": "F1帮助 · F2模式 · F3编辑 · F4复制 · F5会话 · F6模型 · F7运维 · F8目标 · F9粘贴截图 · F10快捷回复 · Ctrl+L清屏 · Esc退出 · Enter发送",
54
- "en": "F1 help · F2 mode · F3 editor · F4 copy · F5 sessions · F6 models · F7 ops · F8 goal · F9 paste image · F10 quick reply · Ctrl+L clear · Esc quit · Enter send",
53
+ "zh": "F1帮助 · F2模式 · F3编辑 · F4文件 · F5会话 · F6模型 · F7运维 · F8目标 · F9粘贴截图 · F10快捷回复 · Ctrl+L清屏 · Esc退出 · Enter发送",
54
+ "en": "F1 help · F2 mode · F3 editor · F4 files · F5 sessions · F6 models · F7 ops · F8 goal · F9 paste image · F10 quick reply · Ctrl+L clear · Esc quit · Enter send",
55
55
  },
56
56
  "ui.banner_sub": {
57
57
  "zh": "多模型终端智能体",
@@ -178,6 +178,14 @@ MESSAGES: dict[str, dict[str, str]] = {
178
178
  "note.lang_switched": {"zh": "界面语言已切换", "en": "UI language switched"},
179
179
  "note.copied_last": {"zh": "已复制最后一条 AI 回复。", "en": "Copied the last AI reply."},
180
180
  "note.no_reply": {"zh": "暂无 AI 回复。", "en": "No AI reply yet."},
181
+ "note.file_referenced": {"zh": "已引用 {path}", "en": "Referenced {path}"},
182
+ "note.file_opened": {"zh": "已打开 {path}", "en": "Opened {path}"},
183
+ "note.file_open_fail": {"zh": "打开失败: {exc}", "en": "Open failed: {exc}"},
184
+ "note.file_path_copied": {"zh": "路径已复制", "en": "Path copied"},
185
+ "note.file_content_copied": {"zh": "内容已复制", "en": "Content copied"},
186
+ "note.file_too_large": {"zh": "文件过大(>512KB), 未复制", "en": "File too large (>512KB), not copied"},
187
+ "note.file_read_fail": {"zh": "读取失败: {exc}", "en": "Read failed: {exc}"},
188
+ "note.copy_fail": {"zh": "复制失败(剪贴板不可用)", "en": "Copy failed (clipboard unavailable)"},
181
189
  "note.qr_not_set": {"zh": "未设置快捷回复。请先 /qr <文本>", "en": "Quick reply not set. Use /qr <text> first"},
182
190
  "note.qr_activated": {"zh": "快捷回复已激活({n}次): {txt}", "en": "Quick reply activated ({n}x): {txt}"},
183
191
  "note.director_stopped": {
@@ -187,7 +195,7 @@ MESSAGES: dict[str, dict[str, str]] = {
187
195
  "note.task_stopped": {"zh": "⏹ 已停止当前 AI 任务。", "en": "⏹ Current AI task stopped."},
188
196
  "note.turn_timeout": {"zh": "⏱ 单轮执行超时({n}秒),已中断。可调整 config.toml 的 turn_timeout。", "en": "⏱ Turn timeout ({n}s) reached, interrupted. Adjust turn_timeout in config.toml."},
189
197
  "note.output_truncated": {"zh": "✂️ 输出达到长度上限被截断, 输入\"继续\"可续写 (可调大 config.toml 的 max_output_tokens)", "en": "✂️ Output truncated by length limit. Type \"continue\" to resume (raise max_output_tokens in config.toml)."},
190
- "note.turn_done": {"zh": "── 本轮结束 · F4 复制最后回复", "en": "── Turn done · F4 to copy last reply"},
198
+ "note.turn_done": {"zh": "── 本轮结束 · Ctrl+C 复制最后回复", "en": "── Turn done · Ctrl+C to copy last reply"},
191
199
  "note.quick_reply_remaining": {"zh": "📨 快捷回复({n}次剩余): {txt}", "en": "📨 Quick reply ({n} left): {txt}"},
192
200
  "note.verifying_provider": {"zh": "正在验证 {name}...", "en": "Verifying {name}..."},
193
201
  "note.verifying_all": {"zh": "正在验证所有 provider...", "en": "Verifying all providers..."},
@@ -316,6 +324,15 @@ MESSAGES: dict[str, dict[str, str]] = {
316
324
  "img.clear": {"zh": "✖ 清空全部", "en": "✖ Clear all"},
317
325
  "img.empty": {"zh": "(无截图)", "en": "(no images)"},
318
326
 
327
+ # ---- 文件浏览器 ---------------------------------------------------------
328
+ "filebrowser.title": {"zh": "📂 文件浏览", "en": "📂 Files"},
329
+ "filebrowser.hint": {"zh": "点击文件=引用 · 右键=操作 · 点目录=展开", "en": "Click file=reference · Right-click=actions · Click dir=expand"},
330
+ "filebrowser.menu.title": {"zh": "文件操作", "en": "File actions"},
331
+ "filebrowser.menu.ref": {"zh": "引用路径到输入框", "en": "Reference path to prompt"},
332
+ "filebrowser.menu.edit": {"zh": "打开(系统程序)", "en": "Open with system app"},
333
+ "filebrowser.menu.copy_path": {"zh": "复制路径", "en": "Copy path"},
334
+ "filebrowser.menu.copy_content": {"zh": "复制文件内容", "en": "Copy file content"},
335
+
319
336
  # ---- 消息组件 ---------------------------------------------------------
320
337
  "msg.director_review": {"zh": "◆ 项目总监 · 审核意见", "en": "◆ Director · Review"},
321
338
  "msg.director_label": {"zh": "◆ 项目总监: ", "en": "◆ Director: "},
@@ -50,6 +50,7 @@ class DeepSeekProvider(BaseProvider):
50
50
  api_key: str | None = None,
51
51
  ):
52
52
  self.config = config
53
+ self.pdef = pdef
53
54
  key = api_key or config.resolve_api_key()
54
55
  if not key:
55
56
  raise ProviderError(
@@ -68,6 +69,7 @@ class DeepSeekProvider(BaseProvider):
68
69
  """用明确 key 创建 Provider(跳过 resolve_api_key 链)。"""
69
70
  inst = cls.__new__(cls)
70
71
  inst.config = config
72
+ inst.pdef = None
71
73
  inst._client = AsyncOpenAI(
72
74
  api_key=api_key,
73
75
  base_url=config.provider.base_url,
@@ -81,12 +83,27 @@ class DeepSeekProvider(BaseProvider):
81
83
 
82
84
  # ── 多模态(视觉模型) ────────────────────────────────────
83
85
 
86
+ def _resolve_model(self, tier: Tier | None, model: str | None = None) -> str:
87
+ """解析实际使用的模型名。
88
+
89
+ 优先级: 显式传入 model > pdef.default_model > pdef.models[0] > 内置 tier 映射。
90
+ 支持 models.toml 里以 type="deepseek" 注册独立模型(如实验版 flash 变体),
91
+ 不再被内置 config.models.flash/pro 写死。
92
+ """
93
+ if model:
94
+ return model
95
+ if self.pdef and self.pdef.default_model:
96
+ return self.pdef.default_model
97
+ if self.pdef and self.pdef.models:
98
+ return self.pdef.models[0]
99
+ return self.config.model_id(tier)
100
+
84
101
  @property
85
102
  def current_model(self) -> str:
86
103
  """当前使用的模型名(最近一次调用或默认档位)。"""
87
104
  if getattr(self, "_last_model", ""):
88
105
  return self._last_model
89
- return self.config.model_id(self.config.models.default_tier)
106
+ return self._resolve_model(self.config.models.default_tier)
90
107
 
91
108
  def supports_model_multimodal(self, model: str = "") -> bool:
92
109
  """视觉模型(模型名含 vision)支持图片输入。"""
@@ -157,6 +174,7 @@ class DeepSeekProvider(BaseProvider):
157
174
  tier: Tier | None,
158
175
  thinking: bool | None,
159
176
  effort: Effort | None,
177
+ model: str | None = None,
160
178
  ) -> dict:
161
179
  cfg = self.config
162
180
  think = cfg.models.thinking if thinking is None else thinking
@@ -169,7 +187,7 @@ class DeepSeekProvider(BaseProvider):
169
187
  extra_body["reasoning_effort"] = eff
170
188
 
171
189
  kwargs: dict[str, Any] = {
172
- "model": cfg.model_id(tier),
190
+ "model": self._resolve_model(tier, model),
173
191
  "messages": messages,
174
192
  "stream": True,
175
193
  "stream_options": {"include_usage": True},
@@ -215,14 +233,14 @@ class DeepSeekProvider(BaseProvider):
215
233
  thinking: bool | None = kwargs.get("thinking")
216
234
  effort: Effort | None = kwargs.get("effort")
217
235
  resolved_tier: Tier = tier or self.config.models.default_tier
218
- model_id = model or self.config.model_id(tier)
236
+ model_id = self._resolve_model(tier, model)
219
237
  self._last_model = model_id # 供 current_model / 多模态判断
220
238
  # 视觉模型: 强制 thinking off(exp 模型未确认支持思考模式, 避免 400)
221
239
  if "vision" in model_id.lower() and thinking is not False:
222
240
  thinking = False
223
241
  # 多模态: 把 [图片附件: 路径] 展开为 base64 image_url
224
242
  messages = self._expand_image_attachments(messages, model_id)
225
- bkwargs = self._build_kwargs(messages, tools, tier, thinking, effort)
243
+ bkwargs = self._build_kwargs(messages, tools, tier, thinking, effort, model_id)
226
244
 
227
245
  content = ""
228
246
  reasoning = ""