cecli-dev 0.93.1__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.
- cecli/__init__.py +20 -0
- cecli/__main__.py +4 -0
- cecli/_version.py +34 -0
- cecli/args.py +1092 -0
- cecli/args_formatter.py +228 -0
- cecli/change_tracker.py +133 -0
- cecli/coders/__init__.py +38 -0
- cecli/coders/agent_coder.py +1872 -0
- cecli/coders/architect_coder.py +63 -0
- cecli/coders/ask_coder.py +8 -0
- cecli/coders/base_coder.py +3993 -0
- cecli/coders/chat_chunks.py +116 -0
- cecli/coders/context_coder.py +52 -0
- cecli/coders/copypaste_coder.py +269 -0
- cecli/coders/editblock_coder.py +656 -0
- cecli/coders/editblock_fenced_coder.py +9 -0
- cecli/coders/editblock_func_coder.py +140 -0
- cecli/coders/editor_diff_fenced_coder.py +8 -0
- cecli/coders/editor_editblock_coder.py +8 -0
- cecli/coders/editor_whole_coder.py +8 -0
- cecli/coders/help_coder.py +15 -0
- cecli/coders/patch_coder.py +705 -0
- cecli/coders/search_replace.py +757 -0
- cecli/coders/shell.py +37 -0
- cecli/coders/single_wholefile_func_coder.py +101 -0
- cecli/coders/udiff_coder.py +428 -0
- cecli/coders/udiff_simple.py +12 -0
- cecli/coders/wholefile_coder.py +143 -0
- cecli/coders/wholefile_func_coder.py +133 -0
- cecli/commands/__init__.py +192 -0
- cecli/commands/add.py +226 -0
- cecli/commands/agent.py +51 -0
- cecli/commands/architect.py +46 -0
- cecli/commands/ask.py +44 -0
- cecli/commands/chat_mode.py +0 -0
- cecli/commands/clear.py +37 -0
- cecli/commands/code.py +46 -0
- cecli/commands/command_prefix.py +44 -0
- cecli/commands/commit.py +52 -0
- cecli/commands/context.py +47 -0
- cecli/commands/context_blocks.py +124 -0
- cecli/commands/context_management.py +51 -0
- cecli/commands/copy.py +62 -0
- cecli/commands/copy_context.py +81 -0
- cecli/commands/core.py +287 -0
- cecli/commands/diff.py +68 -0
- cecli/commands/drop.py +217 -0
- cecli/commands/editor.py +78 -0
- cecli/commands/exit.py +55 -0
- cecli/commands/git.py +57 -0
- cecli/commands/help.py +140 -0
- cecli/commands/history_search.py +40 -0
- cecli/commands/lint.py +109 -0
- cecli/commands/list_sessions.py +56 -0
- cecli/commands/load.py +85 -0
- cecli/commands/load_session.py +48 -0
- cecli/commands/load_skill.py +68 -0
- cecli/commands/ls.py +75 -0
- cecli/commands/map.py +37 -0
- cecli/commands/map_refresh.py +35 -0
- cecli/commands/model.py +118 -0
- cecli/commands/models.py +41 -0
- cecli/commands/multiline_mode.py +38 -0
- cecli/commands/paste.py +91 -0
- cecli/commands/quit.py +32 -0
- cecli/commands/read_only.py +267 -0
- cecli/commands/read_only_stub.py +270 -0
- cecli/commands/reasoning_effort.py +70 -0
- cecli/commands/remove_skill.py +68 -0
- cecli/commands/report.py +40 -0
- cecli/commands/reset.py +88 -0
- cecli/commands/run.py +99 -0
- cecli/commands/save.py +49 -0
- cecli/commands/save_session.py +43 -0
- cecli/commands/settings.py +69 -0
- cecli/commands/test.py +58 -0
- cecli/commands/think_tokens.py +74 -0
- cecli/commands/tokens.py +207 -0
- cecli/commands/undo.py +145 -0
- cecli/commands/utils/__init__.py +0 -0
- cecli/commands/utils/base_command.py +131 -0
- cecli/commands/utils/helpers.py +142 -0
- cecli/commands/utils/registry.py +53 -0
- cecli/commands/utils/save_load_manager.py +98 -0
- cecli/commands/voice.py +78 -0
- cecli/commands/weak_model.py +123 -0
- cecli/commands/web.py +87 -0
- cecli/deprecated_args.py +185 -0
- cecli/diffs.py +129 -0
- cecli/dump.py +29 -0
- cecli/editor.py +147 -0
- cecli/exceptions.py +115 -0
- cecli/format_settings.py +26 -0
- cecli/help.py +119 -0
- cecli/help_pats.py +19 -0
- cecli/helpers/__init__.py +9 -0
- cecli/helpers/copypaste.py +123 -0
- cecli/helpers/coroutines.py +8 -0
- cecli/helpers/file_searcher.py +142 -0
- cecli/helpers/model_providers.py +552 -0
- cecli/helpers/plugin_manager.py +81 -0
- cecli/helpers/profiler.py +162 -0
- cecli/helpers/requests.py +77 -0
- cecli/helpers/similarity.py +98 -0
- cecli/helpers/skills.py +577 -0
- cecli/history.py +186 -0
- cecli/io.py +1782 -0
- cecli/linter.py +304 -0
- cecli/llm.py +101 -0
- cecli/main.py +1280 -0
- cecli/mcp/__init__.py +154 -0
- cecli/mcp/oauth.py +250 -0
- cecli/mcp/server.py +278 -0
- cecli/mdstream.py +243 -0
- cecli/models.py +1255 -0
- cecli/onboarding.py +301 -0
- cecli/prompts/__init__.py +0 -0
- cecli/prompts/agent.yml +71 -0
- cecli/prompts/architect.yml +35 -0
- cecli/prompts/ask.yml +31 -0
- cecli/prompts/base.yml +99 -0
- cecli/prompts/context.yml +60 -0
- cecli/prompts/copypaste.yml +5 -0
- cecli/prompts/editblock.yml +143 -0
- cecli/prompts/editblock_fenced.yml +106 -0
- cecli/prompts/editblock_func.yml +25 -0
- cecli/prompts/editor_diff_fenced.yml +115 -0
- cecli/prompts/editor_editblock.yml +121 -0
- cecli/prompts/editor_whole.yml +46 -0
- cecli/prompts/help.yml +37 -0
- cecli/prompts/patch.yml +110 -0
- cecli/prompts/single_wholefile_func.yml +24 -0
- cecli/prompts/udiff.yml +106 -0
- cecli/prompts/udiff_simple.yml +13 -0
- cecli/prompts/utils/__init__.py +0 -0
- cecli/prompts/utils/prompt_registry.py +167 -0
- cecli/prompts/utils/system.py +56 -0
- cecli/prompts/wholefile.yml +50 -0
- cecli/prompts/wholefile_func.yml +24 -0
- cecli/queries/tree-sitter-language-pack/README.md +7 -0
- cecli/queries/tree-sitter-language-pack/arduino-tags.scm +5 -0
- cecli/queries/tree-sitter-language-pack/c-tags.scm +12 -0
- cecli/queries/tree-sitter-language-pack/chatito-tags.scm +16 -0
- cecli/queries/tree-sitter-language-pack/clojure-tags.scm +12 -0
- cecli/queries/tree-sitter-language-pack/commonlisp-tags.scm +127 -0
- cecli/queries/tree-sitter-language-pack/cpp-tags.scm +18 -0
- cecli/queries/tree-sitter-language-pack/csharp-tags.scm +32 -0
- cecli/queries/tree-sitter-language-pack/d-tags.scm +26 -0
- cecli/queries/tree-sitter-language-pack/dart-tags.scm +97 -0
- cecli/queries/tree-sitter-language-pack/elisp-tags.scm +5 -0
- cecli/queries/tree-sitter-language-pack/elixir-tags.scm +59 -0
- cecli/queries/tree-sitter-language-pack/elm-tags.scm +22 -0
- cecli/queries/tree-sitter-language-pack/gleam-tags.scm +41 -0
- cecli/queries/tree-sitter-language-pack/go-tags.scm +49 -0
- cecli/queries/tree-sitter-language-pack/java-tags.scm +26 -0
- cecli/queries/tree-sitter-language-pack/javascript-tags.scm +96 -0
- cecli/queries/tree-sitter-language-pack/lua-tags.scm +39 -0
- cecli/queries/tree-sitter-language-pack/matlab-tags.scm +10 -0
- cecli/queries/tree-sitter-language-pack/ocaml-tags.scm +115 -0
- cecli/queries/tree-sitter-language-pack/ocaml_interface-tags.scm +101 -0
- cecli/queries/tree-sitter-language-pack/pony-tags.scm +39 -0
- cecli/queries/tree-sitter-language-pack/properties-tags.scm +5 -0
- cecli/queries/tree-sitter-language-pack/python-tags.scm +24 -0
- cecli/queries/tree-sitter-language-pack/r-tags.scm +27 -0
- cecli/queries/tree-sitter-language-pack/racket-tags.scm +12 -0
- cecli/queries/tree-sitter-language-pack/ruby-tags.scm +69 -0
- cecli/queries/tree-sitter-language-pack/rust-tags.scm +63 -0
- cecli/queries/tree-sitter-language-pack/solidity-tags.scm +43 -0
- cecli/queries/tree-sitter-language-pack/swift-tags.scm +54 -0
- cecli/queries/tree-sitter-language-pack/udev-tags.scm +20 -0
- cecli/queries/tree-sitter-languages/README.md +24 -0
- cecli/queries/tree-sitter-languages/c-tags.scm +12 -0
- cecli/queries/tree-sitter-languages/c_sharp-tags.scm +52 -0
- cecli/queries/tree-sitter-languages/cpp-tags.scm +18 -0
- cecli/queries/tree-sitter-languages/dart-tags.scm +92 -0
- cecli/queries/tree-sitter-languages/elisp-tags.scm +8 -0
- cecli/queries/tree-sitter-languages/elixir-tags.scm +59 -0
- cecli/queries/tree-sitter-languages/elm-tags.scm +22 -0
- cecli/queries/tree-sitter-languages/fortran-tags.scm +18 -0
- cecli/queries/tree-sitter-languages/go-tags.scm +36 -0
- cecli/queries/tree-sitter-languages/haskell-tags.scm +5 -0
- cecli/queries/tree-sitter-languages/hcl-tags.scm +77 -0
- cecli/queries/tree-sitter-languages/java-tags.scm +26 -0
- cecli/queries/tree-sitter-languages/javascript-tags.scm +96 -0
- cecli/queries/tree-sitter-languages/julia-tags.scm +60 -0
- cecli/queries/tree-sitter-languages/kotlin-tags.scm +30 -0
- cecli/queries/tree-sitter-languages/matlab-tags.scm +10 -0
- cecli/queries/tree-sitter-languages/ocaml-tags.scm +115 -0
- cecli/queries/tree-sitter-languages/ocaml_interface-tags.scm +104 -0
- cecli/queries/tree-sitter-languages/php-tags.scm +32 -0
- cecli/queries/tree-sitter-languages/python-tags.scm +22 -0
- cecli/queries/tree-sitter-languages/ql-tags.scm +26 -0
- cecli/queries/tree-sitter-languages/ruby-tags.scm +69 -0
- cecli/queries/tree-sitter-languages/rust-tags.scm +63 -0
- cecli/queries/tree-sitter-languages/scala-tags.scm +64 -0
- cecli/queries/tree-sitter-languages/typescript-tags.scm +44 -0
- cecli/queries/tree-sitter-languages/zig-tags.scm +20 -0
- cecli/reasoning_tags.py +82 -0
- cecli/repo.py +626 -0
- cecli/repomap.py +1368 -0
- cecli/report.py +260 -0
- cecli/resources/__init__.py +3 -0
- cecli/resources/model-metadata.json +25751 -0
- cecli/resources/model-settings.yml +2394 -0
- cecli/resources/providers.json +67 -0
- cecli/run_cmd.py +143 -0
- cecli/scrape.py +295 -0
- cecli/sendchat.py +250 -0
- cecli/sessions.py +281 -0
- cecli/special.py +203 -0
- cecli/tools/__init__.py +72 -0
- cecli/tools/command.py +103 -0
- cecli/tools/command_interactive.py +113 -0
- cecli/tools/context_manager.py +175 -0
- cecli/tools/delete_block.py +154 -0
- cecli/tools/delete_line.py +120 -0
- cecli/tools/delete_lines.py +144 -0
- cecli/tools/extract_lines.py +281 -0
- cecli/tools/finished.py +35 -0
- cecli/tools/git_branch.py +132 -0
- cecli/tools/git_diff.py +49 -0
- cecli/tools/git_log.py +43 -0
- cecli/tools/git_remote.py +39 -0
- cecli/tools/git_show.py +37 -0
- cecli/tools/git_status.py +32 -0
- cecli/tools/grep.py +242 -0
- cecli/tools/indent_lines.py +195 -0
- cecli/tools/insert_block.py +263 -0
- cecli/tools/list_changes.py +71 -0
- cecli/tools/load_skill.py +51 -0
- cecli/tools/ls.py +77 -0
- cecli/tools/remove_skill.py +51 -0
- cecli/tools/replace_all.py +113 -0
- cecli/tools/replace_line.py +135 -0
- cecli/tools/replace_lines.py +180 -0
- cecli/tools/replace_text.py +186 -0
- cecli/tools/show_numbered_context.py +137 -0
- cecli/tools/thinking.py +52 -0
- cecli/tools/undo_change.py +82 -0
- cecli/tools/update_todo_list.py +148 -0
- cecli/tools/utils/base_tool.py +64 -0
- cecli/tools/utils/helpers.py +359 -0
- cecli/tools/utils/output.py +119 -0
- cecli/tools/utils/registry.py +145 -0
- cecli/tools/view_files_matching.py +138 -0
- cecli/tools/view_files_with_symbol.py +117 -0
- cecli/tui/__init__.py +83 -0
- cecli/tui/app.py +971 -0
- cecli/tui/io.py +566 -0
- cecli/tui/styles.tcss +117 -0
- cecli/tui/widgets/__init__.py +19 -0
- cecli/tui/widgets/completion_bar.py +331 -0
- cecli/tui/widgets/file_list.py +76 -0
- cecli/tui/widgets/footer.py +165 -0
- cecli/tui/widgets/input_area.py +320 -0
- cecli/tui/widgets/key_hints.py +16 -0
- cecli/tui/widgets/output.py +354 -0
- cecli/tui/widgets/status_bar.py +279 -0
- cecli/tui/worker.py +160 -0
- cecli/urls.py +16 -0
- cecli/utils.py +499 -0
- cecli/versioncheck.py +90 -0
- cecli/voice.py +90 -0
- cecli/waiting.py +38 -0
- cecli/watch.py +316 -0
- cecli/watch_prompts.py +12 -0
- cecli/website/Gemfile +8 -0
- cecli/website/_includes/blame.md +162 -0
- cecli/website/_includes/get-started.md +22 -0
- cecli/website/_includes/help-tip.md +5 -0
- cecli/website/_includes/help.md +24 -0
- cecli/website/_includes/install.md +5 -0
- cecli/website/_includes/keys.md +4 -0
- cecli/website/_includes/model-warnings.md +67 -0
- cecli/website/_includes/multi-line.md +22 -0
- cecli/website/_includes/python-m-aider.md +5 -0
- cecli/website/_includes/recording.css +228 -0
- cecli/website/_includes/recording.md +34 -0
- cecli/website/_includes/replit-pipx.md +9 -0
- cecli/website/_includes/works-best.md +1 -0
- cecli/website/_sass/custom/custom.scss +103 -0
- cecli/website/docs/config/adv-model-settings.md +2498 -0
- cecli/website/docs/config/agent-mode.md +320 -0
- cecli/website/docs/config/aider_conf.md +548 -0
- cecli/website/docs/config/api-keys.md +90 -0
- cecli/website/docs/config/custom-commands.md +187 -0
- cecli/website/docs/config/dotenv.md +493 -0
- cecli/website/docs/config/editor.md +127 -0
- cecli/website/docs/config/mcp.md +210 -0
- cecli/website/docs/config/model-aliases.md +173 -0
- cecli/website/docs/config/options.md +890 -0
- cecli/website/docs/config/reasoning.md +210 -0
- cecli/website/docs/config/skills.md +172 -0
- cecli/website/docs/config/tui.md +126 -0
- cecli/website/docs/config.md +44 -0
- cecli/website/docs/faq.md +379 -0
- cecli/website/docs/git.md +76 -0
- cecli/website/docs/index.md +47 -0
- cecli/website/docs/install/codespaces.md +39 -0
- cecli/website/docs/install/docker.md +48 -0
- cecli/website/docs/install/optional.md +100 -0
- cecli/website/docs/install/replit.md +8 -0
- cecli/website/docs/install.md +115 -0
- cecli/website/docs/languages.md +264 -0
- cecli/website/docs/legal/contributor-agreement.md +111 -0
- cecli/website/docs/legal/privacy.md +104 -0
- cecli/website/docs/llms/anthropic.md +77 -0
- cecli/website/docs/llms/azure.md +48 -0
- cecli/website/docs/llms/bedrock.md +132 -0
- cecli/website/docs/llms/cohere.md +34 -0
- cecli/website/docs/llms/deepseek.md +32 -0
- cecli/website/docs/llms/gemini.md +49 -0
- cecli/website/docs/llms/github.md +111 -0
- cecli/website/docs/llms/groq.md +36 -0
- cecli/website/docs/llms/lm-studio.md +39 -0
- cecli/website/docs/llms/ollama.md +75 -0
- cecli/website/docs/llms/openai-compat.md +39 -0
- cecli/website/docs/llms/openai.md +58 -0
- cecli/website/docs/llms/openrouter.md +78 -0
- cecli/website/docs/llms/other.md +117 -0
- cecli/website/docs/llms/vertex.md +50 -0
- cecli/website/docs/llms/warnings.md +10 -0
- cecli/website/docs/llms/xai.md +53 -0
- cecli/website/docs/llms.md +54 -0
- cecli/website/docs/more/analytics.md +127 -0
- cecli/website/docs/more/edit-formats.md +116 -0
- cecli/website/docs/more/infinite-output.md +192 -0
- cecli/website/docs/more-info.md +8 -0
- cecli/website/docs/recordings/auto-accept-architect.md +31 -0
- cecli/website/docs/recordings/dont-drop-original-read-files.md +35 -0
- cecli/website/docs/recordings/index.md +21 -0
- cecli/website/docs/recordings/model-accepts-settings.md +69 -0
- cecli/website/docs/recordings/tree-sitter-language-pack.md +80 -0
- cecli/website/docs/repomap.md +112 -0
- cecli/website/docs/scripting.md +100 -0
- cecli/website/docs/sessions.md +213 -0
- cecli/website/docs/troubleshooting/aider-not-found.md +24 -0
- cecli/website/docs/troubleshooting/edit-errors.md +76 -0
- cecli/website/docs/troubleshooting/imports.md +62 -0
- cecli/website/docs/troubleshooting/models-and-keys.md +54 -0
- cecli/website/docs/troubleshooting/support.md +79 -0
- cecli/website/docs/troubleshooting/token-limits.md +96 -0
- cecli/website/docs/troubleshooting/warnings.md +12 -0
- cecli/website/docs/troubleshooting.md +11 -0
- cecli/website/docs/usage/browser.md +57 -0
- cecli/website/docs/usage/caching.md +49 -0
- cecli/website/docs/usage/commands.md +133 -0
- cecli/website/docs/usage/conventions.md +119 -0
- cecli/website/docs/usage/copypaste.md +136 -0
- cecli/website/docs/usage/images-urls.md +48 -0
- cecli/website/docs/usage/lint-test.md +118 -0
- cecli/website/docs/usage/modes.md +211 -0
- cecli/website/docs/usage/not-code.md +179 -0
- cecli/website/docs/usage/notifications.md +87 -0
- cecli/website/docs/usage/tips.md +79 -0
- cecli/website/docs/usage/tutorials.md +30 -0
- cecli/website/docs/usage/voice.md +121 -0
- cecli/website/docs/usage/watch.md +294 -0
- cecli/website/docs/usage.md +102 -0
- cecli/website/share/index.md +101 -0
- cecli_dev-0.93.1.dist-info/METADATA +549 -0
- cecli_dev-0.93.1.dist-info/RECORD +366 -0
- cecli_dev-0.93.1.dist-info/WHEEL +5 -0
- cecli_dev-0.93.1.dist-info/entry_points.txt +4 -0
- cecli_dev-0.93.1.dist-info/licenses/LICENSE.txt +202 -0
- cecli_dev-0.93.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
from cecli import diffs
|
|
4
|
+
|
|
5
|
+
from ..dump import dump # noqa: F401
|
|
6
|
+
from .base_coder import Coder
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class WholeFileCoder(Coder):
|
|
10
|
+
"""A coder that operates on entire files for code modifications."""
|
|
11
|
+
|
|
12
|
+
edit_format = "whole"
|
|
13
|
+
prompt_format = "wholefile"
|
|
14
|
+
|
|
15
|
+
def render_incremental_response(self, final):
|
|
16
|
+
try:
|
|
17
|
+
return self.get_edits(mode="diff")
|
|
18
|
+
except ValueError:
|
|
19
|
+
return self.get_multi_response_content_in_progress()
|
|
20
|
+
|
|
21
|
+
def get_edits(self, mode="update"):
|
|
22
|
+
content = self.get_multi_response_content_in_progress()
|
|
23
|
+
|
|
24
|
+
chat_files = self.get_inchat_relative_files()
|
|
25
|
+
|
|
26
|
+
output = []
|
|
27
|
+
lines = content.splitlines(keepends=True)
|
|
28
|
+
|
|
29
|
+
edits = []
|
|
30
|
+
|
|
31
|
+
saw_fname = None
|
|
32
|
+
fname = None
|
|
33
|
+
fname_source = None
|
|
34
|
+
new_lines = []
|
|
35
|
+
for i, line in enumerate(lines):
|
|
36
|
+
if line.startswith(self.fence[0]) or line.startswith(self.fence[1]):
|
|
37
|
+
if fname is not None:
|
|
38
|
+
# ending an existing block
|
|
39
|
+
saw_fname = None
|
|
40
|
+
|
|
41
|
+
full_path = self.abs_root_path(fname)
|
|
42
|
+
|
|
43
|
+
if mode == "diff":
|
|
44
|
+
output += self.do_live_diff(full_path, new_lines, True)
|
|
45
|
+
else:
|
|
46
|
+
edits.append((fname, fname_source, new_lines))
|
|
47
|
+
|
|
48
|
+
fname = None
|
|
49
|
+
fname_source = None
|
|
50
|
+
new_lines = []
|
|
51
|
+
continue
|
|
52
|
+
|
|
53
|
+
# fname==None ... starting a new block
|
|
54
|
+
if i > 0:
|
|
55
|
+
fname_source = "block"
|
|
56
|
+
fname = lines[i - 1].strip()
|
|
57
|
+
fname = fname.strip("*") # handle **filename.py**
|
|
58
|
+
fname = fname.rstrip(":")
|
|
59
|
+
fname = fname.strip("`")
|
|
60
|
+
fname = fname.lstrip("#")
|
|
61
|
+
fname = fname.strip()
|
|
62
|
+
|
|
63
|
+
# Issue #1232
|
|
64
|
+
if len(fname) > 250:
|
|
65
|
+
fname = ""
|
|
66
|
+
|
|
67
|
+
# Did gpt prepend a bogus dir? It especially likes to
|
|
68
|
+
# include the path/to prefix from the one-shot example in
|
|
69
|
+
# the prompt.
|
|
70
|
+
if fname and fname not in chat_files and Path(fname).name in chat_files:
|
|
71
|
+
fname = Path(fname).name
|
|
72
|
+
if not fname: # blank line? or ``` was on first line i==0
|
|
73
|
+
if saw_fname:
|
|
74
|
+
fname = saw_fname
|
|
75
|
+
fname_source = "saw"
|
|
76
|
+
elif len(chat_files) == 1:
|
|
77
|
+
fname = chat_files[0]
|
|
78
|
+
fname_source = "chat"
|
|
79
|
+
else:
|
|
80
|
+
# TODO: sense which file it is by diff size
|
|
81
|
+
raise ValueError(
|
|
82
|
+
f"No filename provided before {self.fence[0]} in file listing"
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
elif fname is not None:
|
|
86
|
+
new_lines.append(line)
|
|
87
|
+
else:
|
|
88
|
+
for word in line.strip().split():
|
|
89
|
+
word = word.rstrip(".:,;!")
|
|
90
|
+
for chat_file in chat_files:
|
|
91
|
+
quoted_chat_file = f"`{chat_file}`"
|
|
92
|
+
if word == quoted_chat_file:
|
|
93
|
+
saw_fname = chat_file
|
|
94
|
+
|
|
95
|
+
output.append(line)
|
|
96
|
+
|
|
97
|
+
if mode == "diff":
|
|
98
|
+
if fname is not None:
|
|
99
|
+
# ending an existing block
|
|
100
|
+
full_path = (Path(self.root) / fname).absolute()
|
|
101
|
+
output += self.do_live_diff(full_path, new_lines, False)
|
|
102
|
+
return "\n".join(output)
|
|
103
|
+
|
|
104
|
+
if fname:
|
|
105
|
+
edits.append((fname, fname_source, new_lines))
|
|
106
|
+
|
|
107
|
+
seen = set()
|
|
108
|
+
refined_edits = []
|
|
109
|
+
# process from most reliable filename, to least reliable
|
|
110
|
+
for source in ("block", "saw", "chat"):
|
|
111
|
+
for fname, fname_source, new_lines in edits:
|
|
112
|
+
if fname_source != source:
|
|
113
|
+
continue
|
|
114
|
+
# if a higher priority source already edited the file, skip
|
|
115
|
+
if fname in seen:
|
|
116
|
+
continue
|
|
117
|
+
|
|
118
|
+
seen.add(fname)
|
|
119
|
+
refined_edits.append((fname, fname_source, new_lines))
|
|
120
|
+
|
|
121
|
+
return refined_edits
|
|
122
|
+
|
|
123
|
+
def apply_edits(self, edits):
|
|
124
|
+
for path, fname_source, new_lines in edits:
|
|
125
|
+
full_path = self.abs_root_path(path)
|
|
126
|
+
new_lines = "".join(new_lines)
|
|
127
|
+
self.io.write_text(full_path, new_lines)
|
|
128
|
+
|
|
129
|
+
def do_live_diff(self, full_path, new_lines, final):
|
|
130
|
+
if Path(full_path).exists():
|
|
131
|
+
orig_lines = self.io.read_text(full_path)
|
|
132
|
+
if orig_lines is not None:
|
|
133
|
+
orig_lines = orig_lines.splitlines(keepends=True)
|
|
134
|
+
|
|
135
|
+
show_diff = diffs.diff_partial_update(
|
|
136
|
+
orig_lines,
|
|
137
|
+
new_lines,
|
|
138
|
+
final=final,
|
|
139
|
+
).splitlines()
|
|
140
|
+
return show_diff
|
|
141
|
+
|
|
142
|
+
output = ["```"] + new_lines + ["```"]
|
|
143
|
+
return output
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
from cecli import diffs
|
|
2
|
+
|
|
3
|
+
from ..dump import dump # noqa: F401
|
|
4
|
+
from .base_coder import Coder
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class WholeFileFunctionCoder(Coder):
|
|
8
|
+
prompt_format = "wholefile_func"
|
|
9
|
+
functions = [
|
|
10
|
+
dict(
|
|
11
|
+
name="write_file",
|
|
12
|
+
description="create or update one or more files",
|
|
13
|
+
parameters=dict(
|
|
14
|
+
type="object",
|
|
15
|
+
required=["explanation", "files"],
|
|
16
|
+
properties=dict(
|
|
17
|
+
explanation=dict(
|
|
18
|
+
type="string",
|
|
19
|
+
description=(
|
|
20
|
+
"Step by step plan for the changes to be made to the code (future"
|
|
21
|
+
" tense, markdown format)"
|
|
22
|
+
),
|
|
23
|
+
),
|
|
24
|
+
files=dict(
|
|
25
|
+
type="array",
|
|
26
|
+
items=dict(
|
|
27
|
+
type="object",
|
|
28
|
+
required=["path", "content"],
|
|
29
|
+
properties=dict(
|
|
30
|
+
path=dict(
|
|
31
|
+
type="string",
|
|
32
|
+
description="Path of file to write",
|
|
33
|
+
),
|
|
34
|
+
content=dict(
|
|
35
|
+
type="string",
|
|
36
|
+
description="Content to write to the file",
|
|
37
|
+
),
|
|
38
|
+
),
|
|
39
|
+
),
|
|
40
|
+
),
|
|
41
|
+
),
|
|
42
|
+
),
|
|
43
|
+
),
|
|
44
|
+
]
|
|
45
|
+
|
|
46
|
+
def __init__(self, *args, **kwargs):
|
|
47
|
+
raise RuntimeError("Deprecated, needs to be refactored to support get_edits/apply_edits")
|
|
48
|
+
|
|
49
|
+
super().__init__(*args, **kwargs)
|
|
50
|
+
|
|
51
|
+
def add_assistant_reply_to_cur_messages(self, edited):
|
|
52
|
+
if edited:
|
|
53
|
+
self.cur_messages += [
|
|
54
|
+
dict(role="assistant", content=self.gpt_prompts.redacted_edit_message)
|
|
55
|
+
]
|
|
56
|
+
else:
|
|
57
|
+
self.cur_messages += [dict(role="assistant", content=self.partial_response_content)]
|
|
58
|
+
|
|
59
|
+
def render_incremental_response(self, final=False):
|
|
60
|
+
if self.partial_response_content:
|
|
61
|
+
return self.partial_response_content
|
|
62
|
+
|
|
63
|
+
args = self.parse_partial_args()
|
|
64
|
+
|
|
65
|
+
if not args:
|
|
66
|
+
return
|
|
67
|
+
|
|
68
|
+
explanation = args.get("explanation")
|
|
69
|
+
files = args.get("files", [])
|
|
70
|
+
|
|
71
|
+
res = ""
|
|
72
|
+
if explanation:
|
|
73
|
+
res += f"{explanation}\n\n"
|
|
74
|
+
|
|
75
|
+
for i, file_upd in enumerate(files):
|
|
76
|
+
path = file_upd.get("path")
|
|
77
|
+
if not path:
|
|
78
|
+
continue
|
|
79
|
+
content = file_upd.get("content")
|
|
80
|
+
if not content:
|
|
81
|
+
continue
|
|
82
|
+
|
|
83
|
+
this_final = (i < len(files) - 1) or final
|
|
84
|
+
res += self.live_diffs(path, content, this_final)
|
|
85
|
+
|
|
86
|
+
return res
|
|
87
|
+
|
|
88
|
+
def live_diffs(self, fname, content, final):
|
|
89
|
+
lines = content.splitlines(keepends=True)
|
|
90
|
+
|
|
91
|
+
# ending an existing block
|
|
92
|
+
full_path = self.abs_root_path(fname)
|
|
93
|
+
|
|
94
|
+
content = self.io.read_text(full_path)
|
|
95
|
+
if content is None:
|
|
96
|
+
orig_lines = []
|
|
97
|
+
else:
|
|
98
|
+
orig_lines = content.splitlines()
|
|
99
|
+
|
|
100
|
+
show_diff = diffs.diff_partial_update(
|
|
101
|
+
orig_lines,
|
|
102
|
+
lines,
|
|
103
|
+
final,
|
|
104
|
+
fname=fname,
|
|
105
|
+
).splitlines()
|
|
106
|
+
|
|
107
|
+
return "\n".join(show_diff)
|
|
108
|
+
|
|
109
|
+
async def _update_files(self):
|
|
110
|
+
name = self.partial_response_function_call.get("name")
|
|
111
|
+
if name and name != "write_file":
|
|
112
|
+
raise ValueError(f'Unknown function_call name="{name}", use name="write_file"')
|
|
113
|
+
|
|
114
|
+
args = self.parse_partial_args()
|
|
115
|
+
if not args:
|
|
116
|
+
return
|
|
117
|
+
|
|
118
|
+
files = args.get("files", [])
|
|
119
|
+
|
|
120
|
+
edited = set()
|
|
121
|
+
for file_upd in files:
|
|
122
|
+
path = file_upd.get("path")
|
|
123
|
+
if not path:
|
|
124
|
+
raise ValueError(f"Missing path parameter: {file_upd}")
|
|
125
|
+
|
|
126
|
+
content = file_upd.get("content")
|
|
127
|
+
if not content:
|
|
128
|
+
raise ValueError(f"Missing content parameter: {file_upd}")
|
|
129
|
+
|
|
130
|
+
if await self.allowed_to_edit(path, content):
|
|
131
|
+
edited.add(path)
|
|
132
|
+
|
|
133
|
+
return edited
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Command system for cecli.
|
|
3
|
+
|
|
4
|
+
This package contains individual command implementations that follow the
|
|
5
|
+
BaseCommand pattern for modular, testable command execution.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from .add import AddCommand
|
|
9
|
+
from .agent import AgentCommand
|
|
10
|
+
from .architect import ArchitectCommand
|
|
11
|
+
from .ask import AskCommand
|
|
12
|
+
from .clear import ClearCommand
|
|
13
|
+
from .code import CodeCommand
|
|
14
|
+
from .command_prefix import CommandPrefixCommand
|
|
15
|
+
from .commit import CommitCommand
|
|
16
|
+
from .context import ContextCommand
|
|
17
|
+
from .context_blocks import ContextBlocksCommand
|
|
18
|
+
from .context_management import ContextManagementCommand
|
|
19
|
+
from .copy import CopyCommand
|
|
20
|
+
from .copy_context import CopyContextCommand
|
|
21
|
+
from .core import Commands, SwitchCoderSignal
|
|
22
|
+
from .diff import DiffCommand
|
|
23
|
+
|
|
24
|
+
# Import and register commands
|
|
25
|
+
from .drop import DropCommand
|
|
26
|
+
from .editor import EditCommand, EditorCommand
|
|
27
|
+
from .exit import ExitCommand
|
|
28
|
+
from .git import GitCommand
|
|
29
|
+
from .help import HelpCommand
|
|
30
|
+
from .history_search import HistorySearchCommand
|
|
31
|
+
from .lint import LintCommand
|
|
32
|
+
from .list_sessions import ListSessionsCommand
|
|
33
|
+
from .load import LoadCommand
|
|
34
|
+
from .load_session import LoadSessionCommand
|
|
35
|
+
from .load_skill import LoadSkillCommand
|
|
36
|
+
from .ls import LsCommand
|
|
37
|
+
from .map import MapCommand
|
|
38
|
+
from .map_refresh import MapRefreshCommand
|
|
39
|
+
from .model import ModelCommand
|
|
40
|
+
from .models import ModelsCommand
|
|
41
|
+
from .multiline_mode import MultilineModeCommand
|
|
42
|
+
from .paste import PasteCommand
|
|
43
|
+
from .quit import QuitCommand
|
|
44
|
+
from .read_only import ReadOnlyCommand
|
|
45
|
+
from .read_only_stub import ReadOnlyStubCommand
|
|
46
|
+
from .reasoning_effort import ReasoningEffortCommand
|
|
47
|
+
from .remove_skill import RemoveSkillCommand
|
|
48
|
+
from .report import ReportCommand
|
|
49
|
+
from .reset import ResetCommand
|
|
50
|
+
from .run import RunCommand
|
|
51
|
+
from .save import SaveCommand
|
|
52
|
+
from .save_session import SaveSessionCommand
|
|
53
|
+
from .settings import SettingsCommand
|
|
54
|
+
from .test import TestCommand
|
|
55
|
+
from .think_tokens import ThinkTokensCommand
|
|
56
|
+
from .tokens import TokensCommand
|
|
57
|
+
from .undo import UndoCommand
|
|
58
|
+
from .utils.base_command import BaseCommand
|
|
59
|
+
from .utils.helpers import (
|
|
60
|
+
CommandError,
|
|
61
|
+
expand_subdir,
|
|
62
|
+
format_command_result,
|
|
63
|
+
get_available_files,
|
|
64
|
+
glob_filtered_to_repo,
|
|
65
|
+
parse_quoted_filenames,
|
|
66
|
+
quote_filename,
|
|
67
|
+
validate_file_access,
|
|
68
|
+
)
|
|
69
|
+
from .utils.registry import CommandRegistry
|
|
70
|
+
from .voice import VoiceCommand
|
|
71
|
+
from .weak_model import WeakModelCommand
|
|
72
|
+
from .web import WebCommand
|
|
73
|
+
|
|
74
|
+
# Register commands
|
|
75
|
+
CommandRegistry.register(DropCommand)
|
|
76
|
+
CommandRegistry.register(ClearCommand)
|
|
77
|
+
CommandRegistry.register(LsCommand)
|
|
78
|
+
CommandRegistry.register(DiffCommand)
|
|
79
|
+
CommandRegistry.register(ResetCommand)
|
|
80
|
+
CommandRegistry.register(CopyCommand)
|
|
81
|
+
CommandRegistry.register(PasteCommand)
|
|
82
|
+
CommandRegistry.register(SettingsCommand)
|
|
83
|
+
CommandRegistry.register(ReportCommand)
|
|
84
|
+
CommandRegistry.register(TokensCommand)
|
|
85
|
+
CommandRegistry.register(UndoCommand)
|
|
86
|
+
CommandRegistry.register(GitCommand)
|
|
87
|
+
CommandRegistry.register(RunCommand)
|
|
88
|
+
CommandRegistry.register(HelpCommand)
|
|
89
|
+
CommandRegistry.register(CommitCommand)
|
|
90
|
+
CommandRegistry.register(ModelsCommand)
|
|
91
|
+
CommandRegistry.register(ExitCommand)
|
|
92
|
+
CommandRegistry.register(QuitCommand)
|
|
93
|
+
CommandRegistry.register(VoiceCommand)
|
|
94
|
+
CommandRegistry.register(MapCommand)
|
|
95
|
+
CommandRegistry.register(MapRefreshCommand)
|
|
96
|
+
CommandRegistry.register(MultilineModeCommand)
|
|
97
|
+
CommandRegistry.register(EditorCommand)
|
|
98
|
+
CommandRegistry.register(EditCommand)
|
|
99
|
+
CommandRegistry.register(HistorySearchCommand)
|
|
100
|
+
CommandRegistry.register(ThinkTokensCommand)
|
|
101
|
+
CommandRegistry.register(LoadCommand)
|
|
102
|
+
CommandRegistry.register(SaveCommand)
|
|
103
|
+
CommandRegistry.register(ReasoningEffortCommand)
|
|
104
|
+
CommandRegistry.register(SaveSessionCommand)
|
|
105
|
+
CommandRegistry.register(ListSessionsCommand)
|
|
106
|
+
CommandRegistry.register(LoadSessionCommand)
|
|
107
|
+
CommandRegistry.register(ReadOnlyCommand)
|
|
108
|
+
CommandRegistry.register(ReadOnlyStubCommand)
|
|
109
|
+
CommandRegistry.register(AddCommand)
|
|
110
|
+
CommandRegistry.register(ModelCommand)
|
|
111
|
+
CommandRegistry.register(WeakModelCommand)
|
|
112
|
+
CommandRegistry.register(WebCommand)
|
|
113
|
+
CommandRegistry.register(LintCommand)
|
|
114
|
+
CommandRegistry.register(TestCommand)
|
|
115
|
+
CommandRegistry.register(ContextManagementCommand)
|
|
116
|
+
CommandRegistry.register(ContextBlocksCommand)
|
|
117
|
+
CommandRegistry.register(AskCommand)
|
|
118
|
+
CommandRegistry.register(CodeCommand)
|
|
119
|
+
CommandRegistry.register(ArchitectCommand)
|
|
120
|
+
CommandRegistry.register(ContextCommand)
|
|
121
|
+
CommandRegistry.register(AgentCommand)
|
|
122
|
+
CommandRegistry.register(CopyContextCommand)
|
|
123
|
+
CommandRegistry.register(CommandPrefixCommand)
|
|
124
|
+
CommandRegistry.register(LoadSkillCommand)
|
|
125
|
+
CommandRegistry.register(RemoveSkillCommand)
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
__all__ = [
|
|
129
|
+
"BaseCommand",
|
|
130
|
+
"CommandRegistry",
|
|
131
|
+
"CommandError",
|
|
132
|
+
"quote_filename",
|
|
133
|
+
"parse_quoted_filenames",
|
|
134
|
+
"glob_filtered_to_repo",
|
|
135
|
+
"validate_file_access",
|
|
136
|
+
"format_command_result",
|
|
137
|
+
"get_available_files",
|
|
138
|
+
"expand_subdir",
|
|
139
|
+
"DropCommand",
|
|
140
|
+
"ClearCommand",
|
|
141
|
+
"LsCommand",
|
|
142
|
+
"DiffCommand",
|
|
143
|
+
"ResetCommand",
|
|
144
|
+
"CopyCommand",
|
|
145
|
+
"PasteCommand",
|
|
146
|
+
"SettingsCommand",
|
|
147
|
+
"ReportCommand",
|
|
148
|
+
"TokensCommand",
|
|
149
|
+
"UndoCommand",
|
|
150
|
+
"GitCommand",
|
|
151
|
+
"RunCommand",
|
|
152
|
+
"HelpCommand",
|
|
153
|
+
"CommitCommand",
|
|
154
|
+
"ModelsCommand",
|
|
155
|
+
"ExitCommand",
|
|
156
|
+
"QuitCommand",
|
|
157
|
+
"VoiceCommand",
|
|
158
|
+
"MapCommand",
|
|
159
|
+
"MapRefreshCommand",
|
|
160
|
+
"MultilineModeCommand",
|
|
161
|
+
"EditorCommand",
|
|
162
|
+
"EditCommand",
|
|
163
|
+
"HistorySearchCommand",
|
|
164
|
+
"ThinkTokensCommand",
|
|
165
|
+
"LoadCommand",
|
|
166
|
+
"SaveCommand",
|
|
167
|
+
"ReasoningEffortCommand",
|
|
168
|
+
"SaveSessionCommand",
|
|
169
|
+
"ListSessionsCommand",
|
|
170
|
+
"LoadSessionCommand",
|
|
171
|
+
"ReadOnlyCommand",
|
|
172
|
+
"ReadOnlyStubCommand",
|
|
173
|
+
"AddCommand",
|
|
174
|
+
"ModelCommand",
|
|
175
|
+
"WeakModelCommand",
|
|
176
|
+
"WebCommand",
|
|
177
|
+
"LintCommand",
|
|
178
|
+
"TestCommand",
|
|
179
|
+
"ContextManagementCommand",
|
|
180
|
+
"ContextBlocksCommand",
|
|
181
|
+
"AskCommand",
|
|
182
|
+
"CodeCommand",
|
|
183
|
+
"ArchitectCommand",
|
|
184
|
+
"ContextCommand",
|
|
185
|
+
"AgentCommand",
|
|
186
|
+
"CopyContextCommand",
|
|
187
|
+
"CommandPrefixCommand",
|
|
188
|
+
"LoadSkillCommand",
|
|
189
|
+
"RemoveSkillCommand",
|
|
190
|
+
"SwitchCoderSignal",
|
|
191
|
+
"Commands",
|
|
192
|
+
]
|
cecli/commands/add.py
ADDED
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import re
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import List
|
|
5
|
+
|
|
6
|
+
from cecli.commands.utils.base_command import BaseCommand
|
|
7
|
+
from cecli.commands.utils.helpers import (
|
|
8
|
+
format_command_result,
|
|
9
|
+
parse_quoted_filenames,
|
|
10
|
+
quote_filename,
|
|
11
|
+
)
|
|
12
|
+
from cecli.utils import is_image_file, run_fzf
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class AddCommand(BaseCommand):
|
|
16
|
+
NORM_NAME = "add"
|
|
17
|
+
DESCRIPTION = "Add files to the chat so cecli can edit them or review them in detail"
|
|
18
|
+
|
|
19
|
+
@classmethod
|
|
20
|
+
async def execute(cls, io, coder, args, **kwargs):
|
|
21
|
+
"""Execute the add command with given parameters."""
|
|
22
|
+
if not args.strip():
|
|
23
|
+
all_files = coder.get_all_relative_files()
|
|
24
|
+
files_in_chat = coder.get_inchat_relative_files()
|
|
25
|
+
addable_files = sorted(set(all_files) - set(files_in_chat))
|
|
26
|
+
if not addable_files:
|
|
27
|
+
io.tool_output("No files available to add.")
|
|
28
|
+
return format_command_result(io, "add", "No files available to add")
|
|
29
|
+
selected_files = run_fzf(addable_files, multi=True, coder=coder)
|
|
30
|
+
if not selected_files:
|
|
31
|
+
return format_command_result(io, "add", "No files selected")
|
|
32
|
+
args = " ".join([quote_filename(f) for f in selected_files])
|
|
33
|
+
|
|
34
|
+
all_matched_files = set()
|
|
35
|
+
|
|
36
|
+
filenames = parse_quoted_filenames(args)
|
|
37
|
+
for word in filenames:
|
|
38
|
+
if Path(word).is_absolute():
|
|
39
|
+
fname = Path(word)
|
|
40
|
+
else:
|
|
41
|
+
fname = Path(coder.root) / word
|
|
42
|
+
|
|
43
|
+
if coder.repo and coder.repo.ignored_file(fname):
|
|
44
|
+
io.tool_warning(f"Skipping {fname} due to cecli.ignore or --subtree-only.")
|
|
45
|
+
continue
|
|
46
|
+
|
|
47
|
+
if fname.exists():
|
|
48
|
+
if fname.is_file():
|
|
49
|
+
all_matched_files.add(str(fname))
|
|
50
|
+
continue
|
|
51
|
+
# an existing dir, escape any special chars so they won't be globs
|
|
52
|
+
word = re.sub(r"([\*\?\[\]])", r"[\1]", word)
|
|
53
|
+
|
|
54
|
+
matched_files = cls.glob_filtered_to_repo(coder, word)
|
|
55
|
+
if matched_files:
|
|
56
|
+
all_matched_files.update(matched_files)
|
|
57
|
+
continue
|
|
58
|
+
|
|
59
|
+
if "*" in str(fname) or "?" in str(fname):
|
|
60
|
+
io.tool_error(f"No match, and cannot create file with wildcard characters: {fname}")
|
|
61
|
+
continue
|
|
62
|
+
|
|
63
|
+
if fname.exists() and fname.is_dir() and coder.repo:
|
|
64
|
+
io.tool_error(f"Directory {fname} is not in git.")
|
|
65
|
+
io.tool_output(f"You can add to git with: /git add {fname}")
|
|
66
|
+
continue
|
|
67
|
+
|
|
68
|
+
if await io.confirm_ask(f"No files matched '{word}'. Do you want to create {fname}?"):
|
|
69
|
+
try:
|
|
70
|
+
fname.parent.mkdir(parents=True, exist_ok=True)
|
|
71
|
+
fname.touch()
|
|
72
|
+
all_matched_files.add(str(fname))
|
|
73
|
+
except OSError as e:
|
|
74
|
+
io.tool_error(f"Error creating file {fname}: {e}")
|
|
75
|
+
|
|
76
|
+
for matched_file in sorted(all_matched_files):
|
|
77
|
+
abs_file_path = coder.abs_root_path(matched_file)
|
|
78
|
+
|
|
79
|
+
if not abs_file_path.startswith(coder.root) and not is_image_file(matched_file):
|
|
80
|
+
io.tool_error(f"Can not add {abs_file_path}, which is not within {coder.root}")
|
|
81
|
+
continue
|
|
82
|
+
|
|
83
|
+
if (
|
|
84
|
+
coder.repo
|
|
85
|
+
and coder.repo.git_ignored_file(matched_file)
|
|
86
|
+
and not coder.add_gitignore_files
|
|
87
|
+
):
|
|
88
|
+
io.tool_error(f"Can't add {matched_file} which is in gitignore")
|
|
89
|
+
continue
|
|
90
|
+
|
|
91
|
+
if abs_file_path in coder.abs_fnames:
|
|
92
|
+
io.tool_error(f"{matched_file} is already in the chat as an editable file")
|
|
93
|
+
continue
|
|
94
|
+
elif abs_file_path in coder.abs_read_only_stubs_fnames:
|
|
95
|
+
if coder.repo and coder.repo.path_in_repo(matched_file):
|
|
96
|
+
coder.abs_read_only_stubs_fnames.remove(abs_file_path)
|
|
97
|
+
coder.abs_fnames.add(abs_file_path)
|
|
98
|
+
io.tool_output(
|
|
99
|
+
f"Moved {matched_file} from read-only (stub) to editable files in the chat"
|
|
100
|
+
)
|
|
101
|
+
else:
|
|
102
|
+
io.tool_error(f"Cannot add {matched_file} as it's not part of the repository")
|
|
103
|
+
elif abs_file_path in coder.abs_read_only_fnames:
|
|
104
|
+
if coder.repo and coder.repo.path_in_repo(matched_file):
|
|
105
|
+
coder.abs_read_only_fnames.remove(abs_file_path)
|
|
106
|
+
coder.abs_fnames.add(abs_file_path)
|
|
107
|
+
io.tool_output(
|
|
108
|
+
f"Moved {matched_file} from read-only to editable files in the chat"
|
|
109
|
+
)
|
|
110
|
+
else:
|
|
111
|
+
io.tool_error(f"Cannot add {matched_file} as it's not part of the repository")
|
|
112
|
+
else:
|
|
113
|
+
if is_image_file(matched_file) and not coder.main_model.info.get("supports_vision"):
|
|
114
|
+
io.tool_error(
|
|
115
|
+
f"Cannot add image file {matched_file} as the"
|
|
116
|
+
f" {coder.main_model.name} does not support images."
|
|
117
|
+
)
|
|
118
|
+
continue
|
|
119
|
+
content = io.read_text(abs_file_path)
|
|
120
|
+
if content is None:
|
|
121
|
+
io.tool_error(f"Unable to read {matched_file}")
|
|
122
|
+
else:
|
|
123
|
+
coder.abs_fnames.add(abs_file_path)
|
|
124
|
+
fname = coder.get_rel_fname(abs_file_path)
|
|
125
|
+
io.tool_output(f"Added {fname} to the chat")
|
|
126
|
+
coder.check_added_files()
|
|
127
|
+
|
|
128
|
+
# Recalculate context block tokens if using agent mode
|
|
129
|
+
if hasattr(coder, "use_enhanced_context") and coder.use_enhanced_context:
|
|
130
|
+
if hasattr(coder, "_calculate_context_block_tokens"):
|
|
131
|
+
coder._calculate_context_block_tokens()
|
|
132
|
+
|
|
133
|
+
if coder.repo_map:
|
|
134
|
+
map_tokens = coder.repo_map.max_map_tokens
|
|
135
|
+
map_mul_no_files = coder.repo_map.map_mul_no_files
|
|
136
|
+
else:
|
|
137
|
+
map_tokens = 0
|
|
138
|
+
map_mul_no_files = 1
|
|
139
|
+
|
|
140
|
+
from cecli.commands import SwitchCoderSignal
|
|
141
|
+
|
|
142
|
+
raise SwitchCoderSignal(
|
|
143
|
+
edit_format=coder.edit_format,
|
|
144
|
+
summarize_from_coder=False,
|
|
145
|
+
from_coder=coder,
|
|
146
|
+
map_tokens=map_tokens,
|
|
147
|
+
map_mul_no_files=map_mul_no_files,
|
|
148
|
+
show_announcements=False,
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
@classmethod
|
|
152
|
+
def glob_filtered_to_repo(cls, coder, pattern: str) -> List[str]:
|
|
153
|
+
"""Glob pattern and filter results to repository files."""
|
|
154
|
+
if not pattern.strip():
|
|
155
|
+
return []
|
|
156
|
+
try:
|
|
157
|
+
if os.path.isabs(pattern):
|
|
158
|
+
# Handle absolute paths
|
|
159
|
+
raw_matched_files = [Path(pattern)]
|
|
160
|
+
else:
|
|
161
|
+
try:
|
|
162
|
+
raw_matched_files = list(Path(coder.root).glob(pattern))
|
|
163
|
+
except (IndexError, AttributeError):
|
|
164
|
+
raw_matched_files = []
|
|
165
|
+
except ValueError:
|
|
166
|
+
# This error will be handled by the caller
|
|
167
|
+
raw_matched_files = []
|
|
168
|
+
|
|
169
|
+
matched_files = []
|
|
170
|
+
for fn in raw_matched_files:
|
|
171
|
+
matched_files += cls.expand_subdir(fn)
|
|
172
|
+
|
|
173
|
+
matched_files = [
|
|
174
|
+
fn.relative_to(coder.root) for fn in matched_files if fn.is_relative_to(coder.root)
|
|
175
|
+
]
|
|
176
|
+
|
|
177
|
+
# if repo, filter against it
|
|
178
|
+
if coder.repo:
|
|
179
|
+
git_files = coder.repo.get_tracked_files()
|
|
180
|
+
matched_files = [fn for fn in matched_files if str(fn) in git_files]
|
|
181
|
+
|
|
182
|
+
return list(map(str, matched_files))
|
|
183
|
+
|
|
184
|
+
@staticmethod
|
|
185
|
+
def expand_subdir(file_path: Path) -> List[Path]:
|
|
186
|
+
"""Expand a directory path to all files within it."""
|
|
187
|
+
if file_path.is_file():
|
|
188
|
+
return [file_path]
|
|
189
|
+
|
|
190
|
+
if file_path.is_dir():
|
|
191
|
+
files = []
|
|
192
|
+
for file in file_path.rglob("*"):
|
|
193
|
+
if file.is_file():
|
|
194
|
+
files.append(file)
|
|
195
|
+
return files
|
|
196
|
+
|
|
197
|
+
return []
|
|
198
|
+
|
|
199
|
+
@classmethod
|
|
200
|
+
def get_completions(cls, io, coder, args) -> List[str]:
|
|
201
|
+
"""Get completion options for add command."""
|
|
202
|
+
files = set(coder.get_all_relative_files())
|
|
203
|
+
files = files - set(coder.get_inchat_relative_files())
|
|
204
|
+
files = [quote_filename(fn) for fn in files]
|
|
205
|
+
return files
|
|
206
|
+
|
|
207
|
+
@classmethod
|
|
208
|
+
def get_help(cls) -> str:
|
|
209
|
+
"""Get help text for the add command."""
|
|
210
|
+
help_text = super().get_help()
|
|
211
|
+
help_text += "\nUsage:\n"
|
|
212
|
+
help_text += " /add # Interactive file selection using fuzzy finder\n"
|
|
213
|
+
help_text += " /add <files> # Add specific files or glob patterns\n"
|
|
214
|
+
help_text += "\nExamples:\n"
|
|
215
|
+
help_text += " /add # Use fuzzy finder to select files\n"
|
|
216
|
+
help_text += " /add *.py # Add all Python files\n"
|
|
217
|
+
help_text += " /add main.py # Add main.py\n"
|
|
218
|
+
help_text += ' /add "file with spaces.py" # Add file with spaces\n'
|
|
219
|
+
help_text += (
|
|
220
|
+
"\nThis command adds files to the chat so cecli can edit them or review them in"
|
|
221
|
+
" detail.\n"
|
|
222
|
+
)
|
|
223
|
+
help_text += "If a file doesn't exist, you'll be asked if you want to create it.\n"
|
|
224
|
+
help_text += "Files can be moved from read-only to editable status.\n"
|
|
225
|
+
help_text += "Image files can be added if the model supports vision.\n"
|
|
226
|
+
return help_text
|