cecli-dev 0.95.5__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.95.5.dist-info/METADATA +549 -0
- cecli_dev-0.95.5.dist-info/RECORD +366 -0
- cecli_dev-0.95.5.dist-info/WHEEL +5 -0
- cecli_dev-0.95.5.dist-info/entry_points.txt +4 -0
- cecli_dev-0.95.5.dist-info/licenses/LICENSE.txt +202 -0
- cecli_dev-0.95.5.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from typing import List
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class SaveLoadManager:
|
|
7
|
+
"""Manager for saving and loading command files."""
|
|
8
|
+
|
|
9
|
+
def __init__(self, coder, io):
|
|
10
|
+
self.coder = coder
|
|
11
|
+
self.io = io
|
|
12
|
+
|
|
13
|
+
def get_saves_directory(self) -> Path:
|
|
14
|
+
"""Get the saves directory, creating it if necessary."""
|
|
15
|
+
saves_dir = Path(self.coder.abs_root_path(".cecli/saves"))
|
|
16
|
+
os.makedirs(saves_dir, exist_ok=True)
|
|
17
|
+
return saves_dir
|
|
18
|
+
|
|
19
|
+
def resolve_filepath(self, filename: str) -> Path:
|
|
20
|
+
"""Resolve a filename to an absolute path, using saves directory if needed."""
|
|
21
|
+
filepath = Path(filename)
|
|
22
|
+
|
|
23
|
+
# If it's a simple filename (no directory separators), save to .cecli/saves/
|
|
24
|
+
if not filepath.is_absolute() and str(filepath) == filepath.name:
|
|
25
|
+
saves_dir = self.get_saves_directory()
|
|
26
|
+
filepath = saves_dir / filepath
|
|
27
|
+
|
|
28
|
+
return filepath
|
|
29
|
+
|
|
30
|
+
def save_commands(self, filename: str) -> Path:
|
|
31
|
+
"""Save commands to reconstruct the current chat session to a file."""
|
|
32
|
+
filepath = self.resolve_filepath(filename)
|
|
33
|
+
|
|
34
|
+
try:
|
|
35
|
+
# Ensure parent directory exists
|
|
36
|
+
os.makedirs(filepath.parent, exist_ok=True)
|
|
37
|
+
|
|
38
|
+
with open(filepath, "w", encoding=self.io.encoding) as f:
|
|
39
|
+
f.write("/drop\n")
|
|
40
|
+
# Write commands to add editable files
|
|
41
|
+
for fname in sorted(self.coder.abs_fnames):
|
|
42
|
+
rel_fname = self.coder.get_rel_fname(fname)
|
|
43
|
+
f.write(f"/add {rel_fname}\n")
|
|
44
|
+
|
|
45
|
+
# Write commands to add read-only files
|
|
46
|
+
for fname in sorted(self.coder.abs_read_only_fnames):
|
|
47
|
+
# Use absolute path for files outside repo root, relative path for files inside
|
|
48
|
+
if Path(fname).is_relative_to(self.coder.root):
|
|
49
|
+
rel_fname = self.coder.get_rel_fname(fname)
|
|
50
|
+
f.write(f"/read-only {rel_fname}\n")
|
|
51
|
+
else:
|
|
52
|
+
f.write(f"/read-only {fname}\n")
|
|
53
|
+
# Write commands to add read-only stubs files
|
|
54
|
+
for fname in sorted(self.coder.abs_read_only_stubs_fnames):
|
|
55
|
+
# Use absolute path for files outside repo root, relative path for files inside
|
|
56
|
+
if Path(fname).is_relative_to(self.coder.root):
|
|
57
|
+
rel_fname = self.coder.get_rel_fname(fname)
|
|
58
|
+
f.write(f"/read-only-stub {rel_fname}\n")
|
|
59
|
+
else:
|
|
60
|
+
f.write(f"/read-only-stub {fname}\n")
|
|
61
|
+
|
|
62
|
+
return filepath
|
|
63
|
+
except Exception as e:
|
|
64
|
+
raise IOError(f"Error saving commands to file: {e}")
|
|
65
|
+
|
|
66
|
+
def load_commands(self, filename: str) -> List[str]:
|
|
67
|
+
"""Load commands from a file."""
|
|
68
|
+
filepath = self.resolve_filepath(filename)
|
|
69
|
+
|
|
70
|
+
try:
|
|
71
|
+
with open(filepath, "r", encoding=self.io.encoding, errors="replace") as f:
|
|
72
|
+
commands = f.readlines()
|
|
73
|
+
return [
|
|
74
|
+
cmd.strip() for cmd in commands if cmd.strip() and not cmd.strip().startswith("#")
|
|
75
|
+
]
|
|
76
|
+
except FileNotFoundError:
|
|
77
|
+
raise FileNotFoundError(f"File not found: {filepath}")
|
|
78
|
+
except Exception as e:
|
|
79
|
+
raise IOError(f"Error reading file: {e}")
|
|
80
|
+
|
|
81
|
+
def list_files(self) -> List[str]:
|
|
82
|
+
"""Return a list of all filenames (without extensions) in the saves directory.
|
|
83
|
+
|
|
84
|
+
Returns:
|
|
85
|
+
List[str]: List of filenames without extensions, sorted alphabetically
|
|
86
|
+
"""
|
|
87
|
+
try:
|
|
88
|
+
saves_dir = self.get_saves_directory()
|
|
89
|
+
|
|
90
|
+
if not saves_dir.exists():
|
|
91
|
+
return []
|
|
92
|
+
|
|
93
|
+
# Get all files (not directories) in the saves directory
|
|
94
|
+
save_files = [f.name for f in saves_dir.iterdir() if f.is_file()]
|
|
95
|
+
return sorted(save_files)
|
|
96
|
+
except Exception:
|
|
97
|
+
# Return empty list on any error
|
|
98
|
+
return []
|
cecli/commands/voice.py
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from typing import List
|
|
3
|
+
|
|
4
|
+
import cecli.voice as voice
|
|
5
|
+
from cecli.commands.utils.base_command import BaseCommand
|
|
6
|
+
from cecli.commands.utils.helpers import format_command_result
|
|
7
|
+
from cecli.llm import litellm
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class VoiceCommand(BaseCommand):
|
|
11
|
+
NORM_NAME = "voice"
|
|
12
|
+
DESCRIPTION = "Record and transcribe voice input"
|
|
13
|
+
|
|
14
|
+
@classmethod
|
|
15
|
+
async def execute(cls, io, coder, args, **kwargs):
|
|
16
|
+
"""Execute the voice command with given parameters."""
|
|
17
|
+
# Get voice parameters from kwargs or coder
|
|
18
|
+
voice_language = kwargs.get("voice_language") or getattr(coder, "voice_language", None)
|
|
19
|
+
voice_format = kwargs.get("voice_format") or getattr(coder, "voice_format", None)
|
|
20
|
+
voice_input_device = kwargs.get("voice_input_device") or getattr(
|
|
21
|
+
coder, "voice_input_device", None
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
# Get voice instance from kwargs or create new one
|
|
25
|
+
voice_instance = kwargs.get("voice_instance")
|
|
26
|
+
|
|
27
|
+
if not voice_instance:
|
|
28
|
+
if "OPENAI_API_KEY" not in os.environ:
|
|
29
|
+
io.tool_error("To use /voice you must provide an OpenAI API key.")
|
|
30
|
+
return format_command_result(io, "voice", "OpenAI API key required")
|
|
31
|
+
|
|
32
|
+
try:
|
|
33
|
+
voice_instance = voice.Voice(
|
|
34
|
+
audio_format=voice_format or "wav", device_name=voice_input_device
|
|
35
|
+
)
|
|
36
|
+
except voice.SoundDeviceError:
|
|
37
|
+
io.tool_error(
|
|
38
|
+
"Unable to import `sounddevice` and/or `soundfile`, is portaudio installed?"
|
|
39
|
+
)
|
|
40
|
+
return format_command_result(io, "voice", "Sound device error")
|
|
41
|
+
|
|
42
|
+
try:
|
|
43
|
+
io.update_spinner("Recording...")
|
|
44
|
+
text = await voice_instance.record_and_transcribe(None, language=voice_language)
|
|
45
|
+
except litellm.OpenAIError as err:
|
|
46
|
+
io.tool_error(f"Unable to use OpenAI whisper model: {err}")
|
|
47
|
+
return format_command_result(io, "voice", f"OpenAI error: {err}")
|
|
48
|
+
|
|
49
|
+
if text:
|
|
50
|
+
io.placeholder = text
|
|
51
|
+
|
|
52
|
+
if coder.tui and coder.tui():
|
|
53
|
+
coder.tui().set_input_value(text)
|
|
54
|
+
coder.tui().refresh()
|
|
55
|
+
|
|
56
|
+
return format_command_result(io, "voice", "Voice recorded and transcribed")
|
|
57
|
+
|
|
58
|
+
@classmethod
|
|
59
|
+
def get_completions(cls, io, coder, args) -> List[str]:
|
|
60
|
+
"""Get completion options for voice command."""
|
|
61
|
+
return []
|
|
62
|
+
|
|
63
|
+
@classmethod
|
|
64
|
+
def get_help(cls) -> str:
|
|
65
|
+
"""Get help text for the voice command."""
|
|
66
|
+
help_text = super().get_help()
|
|
67
|
+
help_text += "\nUsage:\n"
|
|
68
|
+
help_text += " /voice # Record and transcribe voice input\n"
|
|
69
|
+
help_text += (
|
|
70
|
+
"\nThis command records audio from your microphone and transcribes it using OpenAI's"
|
|
71
|
+
" Whisper model.\n"
|
|
72
|
+
)
|
|
73
|
+
help_text += "Requirements:\n"
|
|
74
|
+
help_text += " - OPENAI_API_KEY environment variable must be set\n"
|
|
75
|
+
help_text += " - PortAudio library installed (for sounddevice)\n"
|
|
76
|
+
help_text += " - sounddevice and soundfile Python packages\n"
|
|
77
|
+
help_text += "\nThe transcribed text will be placed in the input prompt for editing.\n"
|
|
78
|
+
return help_text
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
from typing import List
|
|
2
|
+
|
|
3
|
+
import cecli.models as models
|
|
4
|
+
from cecli.commands.utils.base_command import BaseCommand
|
|
5
|
+
from cecli.commands.utils.helpers import format_command_result
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class WeakModelCommand(BaseCommand):
|
|
9
|
+
NORM_NAME = "weak-model"
|
|
10
|
+
DESCRIPTION = "Switch the Weak Model to a new LLM"
|
|
11
|
+
|
|
12
|
+
@classmethod
|
|
13
|
+
async def execute(cls, io, coder, args, **kwargs):
|
|
14
|
+
"""Execute the weak_model command with given parameters."""
|
|
15
|
+
arg_split = args.split(" ", 1)
|
|
16
|
+
model_name = arg_split[0].strip()
|
|
17
|
+
if not model_name:
|
|
18
|
+
# If no model name provided, show current weak model
|
|
19
|
+
current_weak_model = coder.main_model.weak_model.name
|
|
20
|
+
io.tool_output(f"Current weak model: {current_weak_model}")
|
|
21
|
+
return format_command_result(
|
|
22
|
+
io, "weak-model", f"Displayed current weak model: {current_weak_model}"
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
# Create a new model with the same main model and editor model, but updated weak model
|
|
26
|
+
model = models.Model(
|
|
27
|
+
coder.main_model.name,
|
|
28
|
+
editor_model=coder.main_model.editor_model.name,
|
|
29
|
+
weak_model=model_name,
|
|
30
|
+
io=io,
|
|
31
|
+
)
|
|
32
|
+
await models.sanity_check_models(io, model)
|
|
33
|
+
|
|
34
|
+
if len(arg_split) > 1:
|
|
35
|
+
# implement architect coder-like generation call for weak model
|
|
36
|
+
message = arg_split[1].strip()
|
|
37
|
+
|
|
38
|
+
# Store the original model configuration
|
|
39
|
+
original_main_model = coder.main_model
|
|
40
|
+
original_edit_format = coder.edit_format
|
|
41
|
+
|
|
42
|
+
# Create a temporary coder with the new model
|
|
43
|
+
from cecli.coders import Coder
|
|
44
|
+
|
|
45
|
+
kwargs = dict()
|
|
46
|
+
kwargs["main_model"] = model
|
|
47
|
+
kwargs["edit_format"] = coder.edit_format # Keep the same edit format
|
|
48
|
+
kwargs["suggest_shell_commands"] = False
|
|
49
|
+
kwargs["total_cost"] = coder.total_cost
|
|
50
|
+
kwargs["num_cache_warming_pings"] = 0
|
|
51
|
+
kwargs["summarize_from_coder"] = False
|
|
52
|
+
|
|
53
|
+
new_kwargs = dict(io=io, from_coder=coder)
|
|
54
|
+
new_kwargs.update(kwargs)
|
|
55
|
+
|
|
56
|
+
temp_coder = await Coder.create(**new_kwargs)
|
|
57
|
+
temp_coder.cur_messages = []
|
|
58
|
+
temp_coder.done_messages = []
|
|
59
|
+
|
|
60
|
+
verbose = kwargs.get("verbose", False)
|
|
61
|
+
if verbose:
|
|
62
|
+
temp_coder.show_announcements()
|
|
63
|
+
|
|
64
|
+
try:
|
|
65
|
+
await temp_coder.generate(user_message=message, preproc=False)
|
|
66
|
+
coder.move_back_cur_messages(
|
|
67
|
+
f"Weak model {model_name} made those changes to the files."
|
|
68
|
+
)
|
|
69
|
+
coder.total_cost = temp_coder.total_cost
|
|
70
|
+
coder.coder_commit_hashes = temp_coder.coder_commit_hashes
|
|
71
|
+
|
|
72
|
+
# Restore the original model configuration
|
|
73
|
+
from cecli.commands import SwitchCoderSignal
|
|
74
|
+
|
|
75
|
+
raise SwitchCoderSignal(
|
|
76
|
+
main_model=original_main_model, edit_format=original_edit_format
|
|
77
|
+
)
|
|
78
|
+
except Exception as e:
|
|
79
|
+
# If there's an error, still restore the original model
|
|
80
|
+
if not isinstance(e, SwitchCoderSignal):
|
|
81
|
+
io.tool_error(str(e))
|
|
82
|
+
raise SwitchCoderSignal(
|
|
83
|
+
main_model=original_main_model, edit_format=original_edit_format
|
|
84
|
+
)
|
|
85
|
+
else:
|
|
86
|
+
# Re-raise SwitchCoderSignal if that's what was thrown
|
|
87
|
+
raise
|
|
88
|
+
else:
|
|
89
|
+
from cecli.commands import SwitchCoderSignal
|
|
90
|
+
|
|
91
|
+
raise SwitchCoderSignal(main_model=model, edit_format=coder.edit_format)
|
|
92
|
+
|
|
93
|
+
@classmethod
|
|
94
|
+
def get_completions(cls, io, coder, args) -> List[str]:
|
|
95
|
+
"""Get completion options for weak_model command."""
|
|
96
|
+
return models.get_chat_model_names()
|
|
97
|
+
|
|
98
|
+
@classmethod
|
|
99
|
+
def get_help(cls) -> str:
|
|
100
|
+
"""Get help text for the weak_model command."""
|
|
101
|
+
help_text = super().get_help()
|
|
102
|
+
help_text += "\nUsage:\n"
|
|
103
|
+
help_text += " /weak_model <model-name> # Switch to a new weak model\n"
|
|
104
|
+
help_text += (
|
|
105
|
+
" /weak_model <model-name> <prompt> # Use a specific weak model for a single"
|
|
106
|
+
" prompt\n"
|
|
107
|
+
)
|
|
108
|
+
help_text += "\nExamples:\n"
|
|
109
|
+
help_text += (
|
|
110
|
+
" /weak_model gpt-4o-mini # Switch to GPT-4o Mini as weak model\n"
|
|
111
|
+
)
|
|
112
|
+
help_text += (
|
|
113
|
+
" /weak_model claude-3-haiku # Switch to Claude 3 Haiku as weak model\n"
|
|
114
|
+
)
|
|
115
|
+
help_text += ' /weak_model o1-mini "review this code" # Use o1-mini to review code\n'
|
|
116
|
+
help_text += (
|
|
117
|
+
"\nWhen switching weak models, the main model and editor model remain unchanged.\n"
|
|
118
|
+
)
|
|
119
|
+
help_text += (
|
|
120
|
+
"\nIf you provide a prompt after the model name, that weak model will be used\n"
|
|
121
|
+
)
|
|
122
|
+
help_text += "just for that prompt, then you'll return to your original weak model.\n"
|
|
123
|
+
return help_text
|
cecli/commands/web.py
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
from typing import List
|
|
2
|
+
|
|
3
|
+
from cecli.commands.utils.base_command import BaseCommand
|
|
4
|
+
from cecli.commands.utils.helpers import format_command_result
|
|
5
|
+
from cecli.scrape import Scraper, install_playwright
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class WebCommand(BaseCommand):
|
|
9
|
+
NORM_NAME = "web"
|
|
10
|
+
DESCRIPTION = "Scrape a webpage, convert to markdown and send in a message"
|
|
11
|
+
|
|
12
|
+
@classmethod
|
|
13
|
+
async def execute(cls, io, coder, args, **kwargs):
|
|
14
|
+
"""Execute the web command with given parameters."""
|
|
15
|
+
url = args.strip()
|
|
16
|
+
if not url:
|
|
17
|
+
io.tool_error("Please provide a URL to scrape.")
|
|
18
|
+
return format_command_result(io, "web", "No URL provided")
|
|
19
|
+
|
|
20
|
+
io.tool_output(f"Scraping {url}...")
|
|
21
|
+
|
|
22
|
+
# Get scraper instance from kwargs or create new one
|
|
23
|
+
scraper = kwargs.get("scraper")
|
|
24
|
+
|
|
25
|
+
if not scraper:
|
|
26
|
+
# Get disable_playwright from coder args
|
|
27
|
+
disable_playwright = (
|
|
28
|
+
getattr(coder.args, "disable_playwright", False) if coder and coder.args else False
|
|
29
|
+
)
|
|
30
|
+
if disable_playwright:
|
|
31
|
+
res = False
|
|
32
|
+
else:
|
|
33
|
+
try:
|
|
34
|
+
res = await install_playwright(io)
|
|
35
|
+
if not res:
|
|
36
|
+
io.tool_warning("Unable to initialize playwright.")
|
|
37
|
+
except Exception:
|
|
38
|
+
io.tool_warning("Unable to initialize playwright.")
|
|
39
|
+
res = False
|
|
40
|
+
|
|
41
|
+
# Get verify_ssl from kwargs or use default
|
|
42
|
+
verify_ssl = kwargs.get("verify_ssl", True)
|
|
43
|
+
|
|
44
|
+
scraper = Scraper(
|
|
45
|
+
print_error=io.tool_error,
|
|
46
|
+
playwright_available=res,
|
|
47
|
+
verify_ssl=verify_ssl,
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
content = await scraper.scrape(url) or ""
|
|
51
|
+
content = f"Here is the content of {url}:\n\n" + content
|
|
52
|
+
|
|
53
|
+
return_content = kwargs.get("return_content", False)
|
|
54
|
+
if return_content:
|
|
55
|
+
return content
|
|
56
|
+
|
|
57
|
+
io.tool_output("... added to chat.")
|
|
58
|
+
|
|
59
|
+
coder.cur_messages += [
|
|
60
|
+
dict(role="user", content=content),
|
|
61
|
+
dict(role="assistant", content="Ok."),
|
|
62
|
+
]
|
|
63
|
+
|
|
64
|
+
return format_command_result(io, "web", f"Scraped and added content from {url} to chat")
|
|
65
|
+
|
|
66
|
+
@classmethod
|
|
67
|
+
def get_completions(cls, io, coder, args) -> List[str]:
|
|
68
|
+
"""Get completion options for web command."""
|
|
69
|
+
# For web command, we could return recent URLs or common patterns
|
|
70
|
+
# For now, return empty list
|
|
71
|
+
return []
|
|
72
|
+
|
|
73
|
+
@classmethod
|
|
74
|
+
def get_help(cls) -> str:
|
|
75
|
+
"""Get help text for the web command."""
|
|
76
|
+
help_text = super().get_help()
|
|
77
|
+
help_text += "\nUsage:\n"
|
|
78
|
+
help_text += " /web <url> # Scrape a webpage and add its content to the chat\n"
|
|
79
|
+
help_text += "\nExamples:\n"
|
|
80
|
+
help_text += " /web https://example.com # Scrape example.com\n"
|
|
81
|
+
help_text += " /web https://github.com/dwash96/aider-ce # Scrape cecli GitHub page\n"
|
|
82
|
+
help_text += (
|
|
83
|
+
"\nThis command scrapes a webpage, converts it to markdown, and adds it to the chat.\n"
|
|
84
|
+
)
|
|
85
|
+
help_text += "It uses Playwright for JavaScript-rendered pages when available.\n"
|
|
86
|
+
help_text += "Use --disable-playwright to disable Playwright and use simpler scraping.\n"
|
|
87
|
+
return help_text
|
cecli/deprecated_args.py
ADDED
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def add_deprecated_model_args(parser, group):
|
|
5
|
+
"""Add deprecated model shortcut arguments to the argparse parser."""
|
|
6
|
+
|
|
7
|
+
group.add_argument(
|
|
8
|
+
"--opus",
|
|
9
|
+
action="store_true",
|
|
10
|
+
help=argparse.SUPPRESS,
|
|
11
|
+
default=False,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
group.add_argument(
|
|
15
|
+
"--sonnet",
|
|
16
|
+
action="store_true",
|
|
17
|
+
help=argparse.SUPPRESS,
|
|
18
|
+
default=False,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
group.add_argument(
|
|
22
|
+
"--haiku",
|
|
23
|
+
action="store_true",
|
|
24
|
+
help=argparse.SUPPRESS,
|
|
25
|
+
default=False,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
group.add_argument(
|
|
29
|
+
"--4",
|
|
30
|
+
"-4",
|
|
31
|
+
action="store_true",
|
|
32
|
+
help=argparse.SUPPRESS,
|
|
33
|
+
default=False,
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
group.add_argument(
|
|
37
|
+
"--4o",
|
|
38
|
+
action="store_true",
|
|
39
|
+
help=argparse.SUPPRESS,
|
|
40
|
+
default=False,
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
group.add_argument(
|
|
44
|
+
"--mini",
|
|
45
|
+
action="store_true",
|
|
46
|
+
help=argparse.SUPPRESS,
|
|
47
|
+
default=False,
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
group.add_argument(
|
|
51
|
+
"--4-turbo",
|
|
52
|
+
action="store_true",
|
|
53
|
+
help=argparse.SUPPRESS,
|
|
54
|
+
default=False,
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
group.add_argument(
|
|
58
|
+
"--35turbo",
|
|
59
|
+
"--35-turbo",
|
|
60
|
+
"--3",
|
|
61
|
+
"-3",
|
|
62
|
+
action="store_true",
|
|
63
|
+
help=argparse.SUPPRESS,
|
|
64
|
+
default=False,
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
group.add_argument(
|
|
68
|
+
"--deepseek",
|
|
69
|
+
action="store_true",
|
|
70
|
+
help=argparse.SUPPRESS,
|
|
71
|
+
default=False,
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
group.add_argument(
|
|
75
|
+
"--o1-mini",
|
|
76
|
+
action="store_true",
|
|
77
|
+
help=argparse.SUPPRESS,
|
|
78
|
+
default=False,
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
group.add_argument(
|
|
82
|
+
"--o1-preview",
|
|
83
|
+
action="store_true",
|
|
84
|
+
help=argparse.SUPPRESS,
|
|
85
|
+
default=False,
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
#########
|
|
89
|
+
group = parser.add_argument_group("API Keys and Settings (Deprecated)")
|
|
90
|
+
group.add_argument(
|
|
91
|
+
"--openai-api-type",
|
|
92
|
+
help=argparse.SUPPRESS,
|
|
93
|
+
)
|
|
94
|
+
group.add_argument(
|
|
95
|
+
"--openai-api-version",
|
|
96
|
+
help=argparse.SUPPRESS,
|
|
97
|
+
)
|
|
98
|
+
group.add_argument(
|
|
99
|
+
"--openai-api-deployment-id",
|
|
100
|
+
help=argparse.SUPPRESS,
|
|
101
|
+
)
|
|
102
|
+
group.add_argument(
|
|
103
|
+
"--openai-organization-id",
|
|
104
|
+
help=argparse.SUPPRESS,
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
#########
|
|
108
|
+
group = parser.add_argument_group("History Files (Deprecated)")
|
|
109
|
+
group.add_argument(
|
|
110
|
+
"--llm-history-file",
|
|
111
|
+
help=argparse.SUPPRESS,
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
##########
|
|
115
|
+
group = parser.add_argument_group("Analytics")
|
|
116
|
+
group.add_argument(
|
|
117
|
+
"--analytics",
|
|
118
|
+
action=argparse.BooleanOptionalAction,
|
|
119
|
+
default=None,
|
|
120
|
+
help=argparse.SUPPRESS,
|
|
121
|
+
)
|
|
122
|
+
group.add_argument(
|
|
123
|
+
"--analytics-log",
|
|
124
|
+
metavar="ANALYTICS_LOG_FILE",
|
|
125
|
+
help=argparse.SUPPRESS,
|
|
126
|
+
)
|
|
127
|
+
group.add_argument(
|
|
128
|
+
"--analytics-disable",
|
|
129
|
+
action="store_true",
|
|
130
|
+
help=argparse.SUPPRESS,
|
|
131
|
+
default=False,
|
|
132
|
+
)
|
|
133
|
+
group.add_argument(
|
|
134
|
+
"--analytics-posthog-host",
|
|
135
|
+
metavar="ANALYTICS_POSTHOG_HOST",
|
|
136
|
+
help=argparse.SUPPRESS,
|
|
137
|
+
)
|
|
138
|
+
group.add_argument(
|
|
139
|
+
"--analytics-posthog-project-api-key",
|
|
140
|
+
metavar="ANALYTICS_POSTHOG_PROJECT_API_KEY",
|
|
141
|
+
help=argparse.SUPPRESS,
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def handle_deprecated_model_args(args, io):
|
|
146
|
+
"""Handle deprecated model shortcut arguments and provide appropriate warnings."""
|
|
147
|
+
# Define model mapping
|
|
148
|
+
model_map = {
|
|
149
|
+
"opus": "claude-3-opus-20240229",
|
|
150
|
+
"sonnet": "anthropic/claude-3-7-sonnet-20250219",
|
|
151
|
+
"haiku": "claude-3-5-haiku-20241022",
|
|
152
|
+
"4": "gpt-4-0613",
|
|
153
|
+
"4o": "gpt-4o",
|
|
154
|
+
"mini": "gpt-4o-mini",
|
|
155
|
+
"4_turbo": "gpt-4-1106-preview",
|
|
156
|
+
"35turbo": "gpt-3.5-turbo",
|
|
157
|
+
"deepseek": "deepseek/deepseek-chat",
|
|
158
|
+
"o1_mini": "o1-mini",
|
|
159
|
+
"o1_preview": "o1-preview",
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
# Check if any deprecated args are used
|
|
163
|
+
for arg_name, model_name in model_map.items():
|
|
164
|
+
arg_name_clean = arg_name.replace("-", "_")
|
|
165
|
+
if hasattr(args, arg_name_clean) and getattr(args, arg_name_clean):
|
|
166
|
+
# Find preferred name to display in warning
|
|
167
|
+
from cecli.models import MODEL_ALIASES
|
|
168
|
+
|
|
169
|
+
display_name = model_name
|
|
170
|
+
# Check if there's a shorter alias for this model
|
|
171
|
+
for alias, full_name in MODEL_ALIASES.items():
|
|
172
|
+
if full_name == model_name:
|
|
173
|
+
display_name = alias
|
|
174
|
+
break
|
|
175
|
+
|
|
176
|
+
# Show the warning
|
|
177
|
+
io.tool_warning(
|
|
178
|
+
f"The --{arg_name.replace('_', '-')} flag is deprecated and will be removed in a"
|
|
179
|
+
f" future version. Please use --model {display_name} instead."
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
# Set the model
|
|
183
|
+
if not args.model:
|
|
184
|
+
args.model = model_name
|
|
185
|
+
break
|
cecli/diffs.py
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import difflib
|
|
2
|
+
import sys
|
|
3
|
+
|
|
4
|
+
from .dump import dump # noqa: F401
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def main():
|
|
8
|
+
if len(sys.argv) != 3:
|
|
9
|
+
print("Usage: python diffs.py file1 file")
|
|
10
|
+
sys.exit(1)
|
|
11
|
+
|
|
12
|
+
file_orig, file_updated = sys.argv[1], sys.argv[2]
|
|
13
|
+
|
|
14
|
+
with open(file_orig, "r", encoding="utf-8") as f:
|
|
15
|
+
lines_orig = f.readlines()
|
|
16
|
+
|
|
17
|
+
with open(file_updated, "r", encoding="utf-8") as f:
|
|
18
|
+
lines_updated = f.readlines()
|
|
19
|
+
|
|
20
|
+
for i in range(len(file_updated)):
|
|
21
|
+
res = diff_partial_update(lines_orig, lines_updated[:i])
|
|
22
|
+
print(res)
|
|
23
|
+
input()
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def create_progress_bar(percentage):
|
|
27
|
+
block = "█"
|
|
28
|
+
empty = "░"
|
|
29
|
+
total_blocks = 30
|
|
30
|
+
filled_blocks = int(total_blocks * percentage // 100)
|
|
31
|
+
empty_blocks = total_blocks - filled_blocks
|
|
32
|
+
bar = block * filled_blocks + empty * empty_blocks
|
|
33
|
+
return bar
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def assert_newlines(lines):
|
|
37
|
+
if not lines:
|
|
38
|
+
return
|
|
39
|
+
for line in lines[:-1]:
|
|
40
|
+
assert line and line[-1] == "\n", line
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def diff_partial_update(lines_orig, lines_updated, final=False, fname=None):
|
|
44
|
+
"""
|
|
45
|
+
Given only the first part of an updated file, show the diff while
|
|
46
|
+
ignoring the block of "deleted" lines that are past the end of the
|
|
47
|
+
partially complete update.
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
# dump(lines_orig)
|
|
51
|
+
# dump(lines_updated)
|
|
52
|
+
|
|
53
|
+
assert_newlines(lines_orig)
|
|
54
|
+
|
|
55
|
+
num_orig_lines = len(lines_orig)
|
|
56
|
+
|
|
57
|
+
if final:
|
|
58
|
+
last_non_deleted = num_orig_lines
|
|
59
|
+
else:
|
|
60
|
+
last_non_deleted = find_last_non_deleted(lines_orig, lines_updated)
|
|
61
|
+
|
|
62
|
+
# dump(last_non_deleted)
|
|
63
|
+
if last_non_deleted is None:
|
|
64
|
+
return ""
|
|
65
|
+
|
|
66
|
+
# if num_orig_lines:
|
|
67
|
+
# pct = last_non_deleted * 100 / num_orig_lines
|
|
68
|
+
# else:
|
|
69
|
+
# pct = 50
|
|
70
|
+
# bar = create_progress_bar(pct)
|
|
71
|
+
# bar = f" {last_non_deleted:3d} / {num_orig_lines:3d} lines [{bar}] {pct:3.0f}%\n"
|
|
72
|
+
|
|
73
|
+
lines_orig = lines_orig[:last_non_deleted]
|
|
74
|
+
|
|
75
|
+
if not final:
|
|
76
|
+
# lines_updated = lines_updated[:-1] + [bar]
|
|
77
|
+
lines_updated = lines_updated[:-1]
|
|
78
|
+
|
|
79
|
+
diff = difflib.unified_diff(lines_orig, lines_updated, n=5)
|
|
80
|
+
|
|
81
|
+
diff = list(diff)[2:]
|
|
82
|
+
|
|
83
|
+
diff = "".join(diff)
|
|
84
|
+
if not diff.endswith("\n"):
|
|
85
|
+
diff += "\n"
|
|
86
|
+
|
|
87
|
+
for i in range(3, 10):
|
|
88
|
+
backticks = "`" * i
|
|
89
|
+
if backticks not in diff:
|
|
90
|
+
break
|
|
91
|
+
|
|
92
|
+
show = "diff\n"
|
|
93
|
+
if fname:
|
|
94
|
+
show += f"--- {fname} original\n"
|
|
95
|
+
show += f"+++ {fname} updated\n"
|
|
96
|
+
|
|
97
|
+
show += diff
|
|
98
|
+
|
|
99
|
+
show += "\n\n"
|
|
100
|
+
|
|
101
|
+
# print(diff)
|
|
102
|
+
|
|
103
|
+
return show
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def find_last_non_deleted(lines_orig, lines_updated):
|
|
107
|
+
diff = list(difflib.ndiff(lines_orig, lines_updated))
|
|
108
|
+
|
|
109
|
+
num_orig = 0
|
|
110
|
+
last_non_deleted_orig = None
|
|
111
|
+
|
|
112
|
+
for line in diff:
|
|
113
|
+
# print(f"{num_orig:2d} {num_updated:2d} {line}", end="")
|
|
114
|
+
code = line[0]
|
|
115
|
+
if code == " ":
|
|
116
|
+
num_orig += 1
|
|
117
|
+
last_non_deleted_orig = num_orig
|
|
118
|
+
elif code == "-":
|
|
119
|
+
# line only in orig
|
|
120
|
+
num_orig += 1
|
|
121
|
+
elif code == "+":
|
|
122
|
+
# line only in updated
|
|
123
|
+
pass
|
|
124
|
+
|
|
125
|
+
return last_non_deleted_orig
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
if __name__ == "__main__":
|
|
129
|
+
main()
|