diff --git a/.github/workflows/check_pypi_version.yml b/.github/workflows/check_pypi_version.yml new file mode 100644 index 000000000..ba99404d3 --- /dev/null +++ b/.github/workflows/check_pypi_version.yml @@ -0,0 +1,86 @@ +name: Check PyPI Version + +# Check to be sure `pip install aider-chat` installs the most recently published version. +# If dependencies get yanked, it may render the latest version uninstallable. +# See https://github.com/Aider-AI/aider/issues/3699 for example. + +on: + schedule: + # Run once a day at midnight UTC + - cron: '0 0 * * *' + workflow_dispatch: # Allows manual triggering + +jobs: + check_version: + runs-on: ubuntu-latest + strategy: + matrix: + python-version: ["3.9", "3.10", "3.11", "3.12"] + + steps: + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + + - name: Install aider-chat + run: pip install aider-chat + + - name: Get installed aider version + id: installed_version + run: | + set -x # Enable debugging output + aider_version_output=$(aider --version) + if [ $? -ne 0 ]; then + echo "Error: 'aider --version' command failed." + exit 1 + fi + echo "Raw aider --version output: $aider_version_output" + + # Extract version number (format X.Y.Z) + version_num=$(echo "$aider_version_output" | grep -oP '\d+\.\d+\.\d+') + + # Check if grep found anything + if [ -z "$version_num" ]; then + echo "Error: Could not extract version number using grep -oP '\d+\.\d+\.\d+' from output: $aider_version_output" + exit 1 + fi + + echo "Extracted version number: $version_num" + echo "version=$version_num" >> $GITHUB_OUTPUT + + - name: Check out code + uses: actions/checkout@v4 + with: + fetch-depth: 0 # Fetch all history for all tags + + - name: Get latest tag + id: latest_tag + run: | + set -x # Enable debugging output + # Fetch all tags from remote just in case + git fetch --tags origin main + # Get the latest tag that strictly matches vX.Y.Z (no suffixes like .dev) + # List all tags, sort by version descending, filter for exact pattern, take the first one + latest_tag=$(git tag --sort=-v:refname | grep -P '^v\d+\.\d+\.\d+$' | head -n 1) + + if [ -z "$latest_tag" ]; then + echo "Error: Could not find any tags matching the pattern '^v\d+\.\d+\.\d+$'" + exit 1 + fi + + echo "Latest non-dev tag: $latest_tag" + # Remove 'v' prefix for comparison + tag_num=${latest_tag#v} + echo "Extracted tag number: $tag_num" + echo "tag=$tag_num" >> $GITHUB_OUTPUT + + - name: Compare versions + run: | + echo "Installed version: ${{ steps.installed_version.outputs.version }}" + echo "Latest tag version: ${{ steps.latest_tag.outputs.tag }}" + if [ "${{ steps.installed_version.outputs.version }}" != "${{ steps.latest_tag.outputs.tag }}" ]; then + echo "Error: Installed aider version (${{ steps.installed_version.outputs.version }}) does not match the latest tag (${{ steps.latest_tag.outputs.tag }})." + exit 1 + fi + echo "Versions match." diff --git a/.github/workflows/windows_check_pypi_version.yml b/.github/workflows/windows_check_pypi_version.yml new file mode 100644 index 000000000..960241326 --- /dev/null +++ b/.github/workflows/windows_check_pypi_version.yml @@ -0,0 +1,90 @@ +name: Windows Check PyPI Version + +# Check to be sure `pip install aider-chat` installs the most recently published version on Windows. +# If dependencies get yanked, it may render the latest version uninstallable. +# See https://github.com/Aider-AI/aider/issues/3699 for example. + +on: + schedule: + # Run once a day at 1 AM UTC (offset from Ubuntu check) + - cron: '0 1 * * *' + workflow_dispatch: # Allows manual triggering + +jobs: + check_version: + runs-on: windows-latest + strategy: + matrix: + python-version: ["3.9", "3.10", "3.11", "3.12"] + defaults: + run: + shell: pwsh # Use PowerShell for all run steps + + steps: + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + + - name: Install aider-chat + run: pip install aider-chat + + - name: Get installed aider version + id: installed_version + run: | + Write-Host "Running 'aider --version'..." + $aider_version_output = aider --version + if ($LASTEXITCODE -ne 0) { + Write-Error "Error: 'aider --version' command failed." + exit 1 + } + Write-Host "Raw aider --version output: $aider_version_output" + + # Extract version number (format X.Y.Z) using PowerShell regex + $match = [regex]::Match($aider_version_output, '\d+\.\d+\.\d+') + + if (-not $match.Success) { + Write-Error "Error: Could not extract version number using regex '\d+\.\d+\.\d+' from output: $aider_version_output" + exit 1 + } + $version_num = $match.Value + + Write-Host "Extracted version number: $version_num" + echo "version=$version_num" >> $env:GITHUB_OUTPUT + + - name: Check out code + uses: actions/checkout@v4 + with: + fetch-depth: 0 # Fetch all history for all tags + + - name: Get latest tag + id: latest_tag + run: | + Write-Host "Fetching tags..." + # Fetch all tags from remote just in case + git fetch --tags origin main + Write-Host "Getting latest non-dev tag..." + # Get the latest tag that strictly matches vX.Y.Z (no suffixes like .dev) + # List all tags, sort by version descending, filter for exact pattern, take the first one + $latest_tag = (git tag --sort=-v:refname | Select-String -Pattern '^v\d+\.\d+\.\d+$' | Select-Object -First 1).Line + + if (-not $latest_tag) { + Write-Error "Error: Could not find any tags matching the pattern '^v\d+\.\d+\.\d+$'" + exit 1 + } + + Write-Host "Latest non-dev tag: $latest_tag" + # Remove 'v' prefix for comparison + $tag_num = $latest_tag.Substring(1) + Write-Host "Extracted tag number: $tag_num" + echo "tag=$tag_num" >> $env:GITHUB_OUTPUT + + - name: Compare versions + run: | + Write-Host "Installed version: ${{ steps.installed_version.outputs.version }}" + Write-Host "Latest tag version: ${{ steps.latest_tag.outputs.tag }}" + if ("${{ steps.installed_version.outputs.version }}" -ne "${{ steps.latest_tag.outputs.tag }}") { + Write-Error "Error: Installed aider version (${{ steps.installed_version.outputs.version }}) does not match the latest tag (${{ steps.latest_tag.outputs.tag }})." + exit 1 + } + Write-Host "Versions match." diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 2a62f51e1..ca81b8a73 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -18,5 +18,6 @@ repos: rev: v2.2.6 hooks: - id: codespell + args: ["--skip", "aider/website/docs/languages.md"] additional_dependencies: - tomli diff --git a/HISTORY.md b/HISTORY.md index a4c224abb..337a05e6c 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -2,8 +2,213 @@ ### main branch +- Add support for `gemini-2.5-flash-preview-04-17` models. +- Improved "diff" format for Gemini 2.5 Flash by accepting filenames provided on the same line as the opening fence. +- Add new `udiff-simple` edit format, for Gemini 2.5 Pro. +- Update default weak/editor models for Gemini 2.5 Pro models to use `gemini-2.5-flash-preview-04-17`. +- Aider wrote 69% of the code in this release. + +### Aider v0.82.2 + +- Fix editing shell files with diff-fenced, by zjy1412. +- Improve robustness of patch application by allowing multiple update/delete actions for the same file within a single response. +- Update prompts to instruct LLMs to consolidate all edits for a given file into a single block within the patch. + +### Aider v0.82.1 + +- Added support for `o3` and `o4-mini` including provider-specific versions for OpenAI, OpenRouter, and Azure. +- Added support for Azure specific `gpt-4.1` and `gpt-4.1-mini` models. +- Disabled streaming for `o3` models since you need identity verification to stream. +- Fixed handling of file paths in unified diffs, especially those generated by git. + +### Aider v0.82.0 + +- Support for GPT 4.1, mini and nano. +- Added new `patch` edit format for OpenAI's GPT-4.1 model. +- Improved support for using architect mode with Gemini 2.5 Pro. +- Added new `editor-diff`, `editor-whole`, and `editor-diff-fenced` edit formats. +- Bugfix for automatically selecting the best edit format to use in architect mode. +- Added support for `grok-3-fast-beta` and `grok-3-mini-fast-beta` models. +- Aider wrote 92% of the code in this release. + +### Aider v0.81.3 + +- Commit messages generated by aider are no longer forced to be entirely lowercase, by Peter Hadlaw. +- Updated default settings for Grok models. + +### Aider v0.81.2 + +- Add support for `xai/grok-3-beta`, `xai/grok-3-mini-beta`, `openrouter/x-ai/grok-3-beta`, `openrouter/x-ai/grok-3-mini-beta`, and `openrouter/openrouter/optimus-alpha` models. +- Add alias "grok3" for `xai/grok-3-beta`. +- Add alias "optimus" for `openrouter/openrouter/optimus-alpha`. +- Fix URL extraction from error messages. +- Allow adding files by full path even if a file with the same basename is already in the chat. +- Fix quoting of values containing '#' in the sample `aider.conf.yml`. +- Add support for Fireworks AI model 'deepseek-v3-0324', by Felix Lisczyk. +- Commit messages generated by aider are now lowercase, by Anton Ödman. + +### Aider v0.81.1 + +- Added support for the `gemini/gemini-2.5-pro-preview-03-25` model. +- Updated the `gemini` alias to point to `gemini/gemini-2.5-pro-preview-03-25`. +- Added the `gemini-exp` alias for `gemini/gemini-2.5-pro-exp-03-25`. + +### Aider v0.81.0 + +- Added support for the `openrouter/openrouter/quasar-alpha` model. + - Run with `aider --model quasar` +- Offer OpenRouter OAuth authentication if an OpenRouter model is specified but the API key is missing. +- Prevent retrying API calls when the provider reports insufficient credits. +- Improve URL detection to exclude trailing double quotes. +- Aider wrote 86% of the code in this release. + +### Aider v0.80.4 + +- Bumped deps to pickup litellm change to properly display the root cause of OpenRouter "choices" errors. + +### Aider v0.80.3 + +- Improve error message for OpenRouter API connection issues to mention potential rate limiting or upstream provider issues. +- Configure weak models (`gemini/gemini-2.0-flash` and `openrouter/google/gemini-2.0-flash-exp:free`) for Gemini 2.5 Pro models. +- Add model metadata for `openrouter/google/gemini-2.0-flash-exp:free`. + +### Aider v0.80.2 + +- Bumped deps. + +### Aider v0.80.1 + +- Updated deps for yanked fsspec and aiohttp packages #3699 +- Removed redundant dependency check during OpenRouter OAuth flow, by Claudia Pellegrino. + +### Aider v0.80.0 + +- OpenRouter OAuth integration: + - Offer to OAuth against OpenRouter if no model and keys are provided. + - Select OpenRouter default model based on free/paid tier status if `OPENROUTER_API_KEY` is set and no model is specified. +- Prioritize `gemini/gemini-2.5-pro-exp-03-25` if `GEMINI_API_KEY` is set, and `vertex_ai/gemini-2.5-pro-exp-03-25` if `VERTEXAI_PROJECT` is set, when no model is specified. +- Validate user-configured color settings on startup and warn/disable invalid ones. +- Warn at startup if `--stream` and `--cache-prompts` are used together, as cost estimates may be inaccurate. +- Boost repomap ranking for files whose path components match identifiers mentioned in the chat. +- Change web scraping timeout from an error to a warning, allowing scraping to continue with potentially incomplete content. +- Left-align markdown headings in the terminal output, by Peter Schilling. +- Update edit format to the new model's default when switching models with `/model`, if the user was using the old model's default format. +- Add `Ctrl-X Ctrl-E` keybinding to edit the current input buffer in an external editor, by Matteo Landi. +- Fix linting errors for filepaths containing shell metacharacters, by Mir Adnan ALI. +- Add the `openrouter/deepseek-chat-v3-0324:free` model. +- Add repomap support for the Scala language, by Vasil Markoukin. +- Fixed bug in `/run` that was preventing auto-testing. +- Fix bug preventing `UnboundLocalError` during git tree traversal. +- Handle `GitCommandNotFound` error if git is not installed or not in PATH. +- Handle `FileNotFoundError` if the current working directory is deleted while aider is running. +- Fix completion menu current item color styling, by Andrey Ivanov. +- Aider wrote 87% of the code in this release. + +### Aider v0.79.2 + +- Added 'gemini' alias for gemini-2.5-pro model. +- Updated Gemini 2.5 Pro max output tokens to 64k. +- Added support for Lisp-style semicolon comments in file watcher, by Matteo Landi. +- Added OpenRouter API error detection and retries. +- Added openrouter/deepseek-chat-v3-0324 model. +- Aider wrote 93% of the code in this release. + +### Aider v0.79.1 + +- Improved model listing to include all models in fuzzy matching, including those provided by aider (not litellm). + +### Aider v0.79.0 + +- Added support for Gemini 2.5 Pro models. +- Added support for DeepSeek V3 0324 model. +- Added a new `/context` command that automatically identifies which files need to be edited for a given request. +- Added `/edit` as an alias for the `/editor` command. +- Added "overeager" mode for Claude 3.7 Sonnet models to try and keep it working within the requested scope. +- Aider wrote 65% of the code in this release. + +### Aider v0.78.0 + +- Added support for thinking tokens for OpenRouter Sonnet 3.7. +- Added commands to switch between model types: `/editor-model` for Editor Model, and `/weak-model` for Weak Model, by csala. +- Added model setting validation to ignore `--reasoning-effort` and `--thinking-tokens` if the model doesn't support them. +- Added `--check-model-accepts-settings` flag (default: true) to force unsupported model settings. +- Annotated which models support reasoning_effort and thinking_tokens settings in the model settings data. +- Improved code block rendering in markdown output with better padding using NoInsetMarkdown. +- Added `--git-commit-verify` flag (default: False) to control whether git commit hooks are bypassed. +- Fixed autocompletion for `/ask`, `/code`, and `/architect` commands, by shladnik. +- Added vi-like behavior when pressing enter in multiline-mode while in vi normal/navigation-mode, by Marco Mayer. +- Added AWS_PROFILE support for Bedrock models, allowing use of AWS profiles instead of explicit credentials, by lentil32. +- Enhanced `--aiderignore` argument to resolve both absolute and relative paths, by mopemope. +- Improved platform information handling to gracefully handle retrieval errors. +- Aider wrote 92% of the code in this release. + +### Aider v0.77.1 + +- Bumped dependencies to pickup litellm fix for Ollama. +- Added support for `openrouter/google/gemma-3-27b-it` model. +- Updated exclude patterns for help documentation. + +### Aider v0.77.0 + +- Big upgrade in [programming languages supported](https://aider.chat/docs/languages.html) by adopting [tree-sitter-language-pack](https://github.com/Goldziher/tree-sitter-language-pack/). + - 130 new languages with linter support. + - 20 new languages with repo-map support. +- Added `/think-tokens` command to set thinking token budget with support for human-readable formats (8k, 10.5k, 0.5M). +- Added `/reasoning-effort` command to control model reasoning level. +- The `/think-tokens` and `/reasoning-effort` commands display current settings when called without arguments. +- Display of thinking token budget and reasoning effort in model information. +- Changed `--thinking-tokens` argument to accept string values with human-readable formats. +- Added `--auto-accept-architect` flag (default: true) to automatically accept changes from architect coder format without confirmation. +- Added support for `cohere_chat/command-a-03-2025` and `gemini/gemma-3-27b-it` +- The bare `/drop` command now preserves original read-only files provided via args.read. +- Fixed a bug where default model would be set by deprecated `--shortcut` switches even when already specified in the command line. +- Improved AutoCompleter to require 3 characters for autocompletion to reduce noise. +- Aider wrote 72% of the code in this release. + +### Aider v0.76.2 + +- Fixed handling of JSONDecodeError when loading model cache file. +- Fixed handling of GitCommandError when retrieving git user configuration. +- Aider wrote 75% of the code in this release. + +### Aider v0.76.1 + +- Added ignore_permission_denied option to file watcher to prevent errors when accessing restricted files, by Yutaka Matsubara. +- Aider wrote 0% of the code in this release. + +### Aider v0.76.0 + +- Improved support for thinking/reasoningmodels: + - Added `--thinking-tokens` CLI option to control token budget for models that support thinking. + - Display thinking/reasoning content from LLMs which return it. + - Enhanced handling of reasoning tags to better clean up model responses. + - Added deprecation warning for `remove_reasoning` setting, now replaced by `reasoning_tag`. +- Aider will notify you when it's completed the last request and needs your input: + - Added [notifications when LLM responses are ready](https://aider.chat/docs/usage/notifications.html) with `--notifications` flag. + - Specify desktop notification command with `--notifications-command`. +- Added support for QWQ 32B. +- Switch to `tree-sitter-language-pack` for tree sitter support. +- Improved error handling for EOF (Ctrl+D) in user input prompts. +- Added helper function to ensure hex color values have a # prefix. +- Fixed handling of Git errors when reading staged files. +- Improved SSL verification control for model information requests. +- Improved empty LLM response handling with clearer warning messages. +- Fixed Git identity retrieval to respect global configuration, by Akira Komamura. +- Offer to install dependencies for Bedrock and Vertex AI models. +- Deprecated model shortcut args (like --4o, --opus) in favor of the --model flag. +- Aider wrote 85% of the code in this release. + +### Aider v0.75.3 + +- Support for V3 free on OpenRouter: `--model openrouter/deepseek/deepseek-chat:free`. + +### Aider v0.75.2 + - Added support for Claude 3.7 Sonnet models on OpenRouter, Bedrock and Vertex AI. -- Aider wrote 47% of the code in this release. +- Updated default model to Claude 3.7 Sonnet on OpenRouter. +- Added support for GPT-4.5-preview model. +- Added support for Claude 3.7 Sonnet:beta on OpenRouter. +- Fixed weak_model_name patterns to match main model name patterns for some models. ### Aider v0.75.1 diff --git a/README.md b/README.md index b0a09b484..688506ab5 100644 --- a/README.md +++ b/README.md @@ -1,144 +1,177 @@ +

+ Aider Logo +

- +

+AI Pair Programming in Your Terminal +

-# Aider is AI pair programming in your terminal -Aider lets you pair program with LLMs, -to edit code in your local git repository. -Start a new project or work with an existing code base. -Aider works best with Claude 3.5 Sonnet, DeepSeek R1 & Chat V3, OpenAI o1, o3-mini & GPT-4o. Aider can [connect to almost any LLM, including local models](https://aider.chat/docs/llms.html). +

+Aider lets you pair program with LLMs to start a new project or build on your existing codebase. +

-

aider screencast

- - -

- - - - - - -

- -## Getting started + GitHub Stars + PyPI Downloads + Tokens per week + OpenRouter Ranking + Singularity + +

-If you already have python 3.8-3.13 installed, you can get started quickly like this: +## Features + +### [Cloud and local LLMs](https://aider.chat/docs/llms.html) + + +Aider works best with Claude 3.7 Sonnet, DeepSeek R1 & Chat V3, OpenAI o1, o3-mini & GPT-4o, but can connect to almost any LLM, including local models. + +
+ +### [Maps your codebase](https://aider.chat/docs/repomap.html) + + +Aider makes a map of your entire codebase, which helps it work well in larger projects. + +
+ +### [100+ code languages](https://aider.chat/docs/languages.html) + + +Aider works with most popular programming languages: python, javascript, rust, ruby, go, cpp, php, html, css, and dozens more. + +
+ +### [Git integration](https://aider.chat/docs/git.html) + + +Aider automatically commits changes with sensible commit messages. Use familiar git tools to easily diff, manage and undo AI changes. + +
+ +### [Use in your IDE](https://aider.chat/docs/usage/watch.html) + + +Use aider from within your favorite IDE or editor. Ask for changes by adding comments to your code and aider will get to work. + +
+ +### [Images & web pages](https://aider.chat/docs/usage/images-urls.html) + + +Add images and web pages to the chat to provide visual context, screenshots, reference docs, etc. + +
+ +### [Voice-to-code](https://aider.chat/docs/usage/voice.html) + + +Speak with aider about your code! Request new features, test cases or bug fixes using your voice and let aider implement the changes. + +
+ +### [Linting & testing](https://aider.chat/docs/usage/lint-test.html) + + +Automatically lint and test your code every time aider makes changes. Aider can fix problems detected by your linters and test suites. + +
+ +### [Copy/paste to web chat](https://aider.chat/docs/usage/copypaste.html) + + +Work with any LLM via its web chat interface. Aider streamlines copy/pasting code context and edits back and forth with a browser. + +## Getting Started ```bash python -m pip install aider-install aider-install -# Change directory into your code base +# Change directory into your codebase cd /to/your/project -# Work with DeepSeek via DeepSeek's API -aider --model deepseek --api-key deepseek=your-key-goes-here +# DeepSeek +aider --model deepseek --api-key deepseek= -# Work with Claude 3.5 Sonnet via Anthropic's API -aider --model sonnet --api-key anthropic=your-key-goes-here +# Claude 3.7 Sonnet +aider --model sonnet --api-key anthropic= -# Work with GPT-4o via OpenAI's API -aider --model gpt-4o --api-key openai=your-key-goes-here - -# Work with Sonnet via OpenRouter's API -aider --model openrouter/anthropic/claude-3.5-sonnet --api-key openrouter=your-key-goes-here - -# Work with DeepSeek via OpenRouter's API -aider --model openrouter/deepseek/deepseek-chat --api-key openrouter=your-key-goes-here +# o3-mini +aider --model o3-mini --api-key openai= ``` - -See the -[installation instructions](https://aider.chat/docs/install.html) -and -[usage documentation](https://aider.chat/docs/usage.html) -for more details. +See the [installation instructions](https://aider.chat/docs/install.html) and [usage documentation](https://aider.chat/docs/usage.html) for more details. -## Features +## More Information -- Run aider with the files you want to edit: `aider ...` -- Ask for changes: - - Add new features or test cases. - - Describe a bug. - - Paste in an error message or GitHub issue URL. - - Refactor code. - - Update docs. -- Aider will edit your files to complete your request. -- Aider [automatically git commits](https://aider.chat/docs/git.html) changes with a sensible commit message. -- [Use aider inside your favorite editor or IDE](https://aider.chat/docs/usage/watch.html). -- Aider works with [most popular languages](https://aider.chat/docs/languages.html): python, javascript, typescript, php, html, css, and more... -- Aider can edit multiple files at once for complex requests. -- Aider uses a [map of your entire git repo](https://aider.chat/docs/repomap.html), which helps it work well in larger codebases. -- Edit files in your editor or IDE while chatting with aider, -and it will always use the latest version. -Pair program with AI. -- [Add images to the chat](https://aider.chat/docs/usage/images-urls.html) (GPT-4o, Claude 3.5 Sonnet, etc). -- [Add URLs to the chat](https://aider.chat/docs/usage/images-urls.html) and aider will read their content. -- [Code with your voice](https://aider.chat/docs/usage/voice.html). -- Aider works best with Claude 3.5 Sonnet, DeepSeek V3, o1 & GPT-4o and can [connect to almost any LLM](https://aider.chat/docs/llms.html). - - -## Top tier performance - -[Aider has one of the top scores on SWE Bench](https://aider.chat/2024/06/02/main-swe-bench.html). -SWE Bench is a challenging software engineering benchmark where aider -solved *real* GitHub issues from popular open source -projects like django, scikitlearn, matplotlib, etc. - -## More info - -- [Documentation](https://aider.chat/) -- [Installation](https://aider.chat/docs/install.html) -- [Usage](https://aider.chat/docs/usage.html) -- [Tutorial videos](https://aider.chat/docs/usage/tutorials.html) +### Documentation +- [Installation Guide](https://aider.chat/docs/install.html) +- [Usage Guide](https://aider.chat/docs/usage.html) +- [Tutorial Videos](https://aider.chat/docs/usage/tutorials.html) - [Connecting to LLMs](https://aider.chat/docs/llms.html) -- [Configuration](https://aider.chat/docs/config.html) +- [Configuration Options](https://aider.chat/docs/config.html) - [Troubleshooting](https://aider.chat/docs/troubleshooting.html) +- [FAQ](https://aider.chat/docs/faq.html) + +### Community & Resources - [LLM Leaderboards](https://aider.chat/docs/leaderboards/) -- [GitHub](https://github.com/Aider-AI/aider) -- [Discord](https://discord.gg/Tv2uQnR88V) +- [GitHub Repository](https://github.com/Aider-AI/aider) +- [Discord Community](https://discord.gg/Tv2uQnR88V) - [Blog](https://aider.chat/blog/) +## Kind Words From Users -## Kind words from users +- *"My life has changed... There's finally an AI coding tool that's good enough to keep up with me... Aider... It's going to rock your world."* — [Eric S. Raymond](https://x.com/esrtweet/status/1910809356381413593) +- *"The best free open source AI coding assistant."* — [IndyDevDan](https://youtu.be/YALpX8oOn78) +- *"The best AI coding assistant so far."* — [Matthew Berman](https://www.youtube.com/watch?v=df8afeb1FY8) +- *"Aider ... has easily quadrupled my coding productivity."* — [SOLAR_FIELDS](https://news.ycombinator.com/item?id=36212100) +- *"It's a cool workflow... Aider's ergonomics are perfect for me."* — [qup](https://news.ycombinator.com/item?id=38185326) +- *"It's really like having your senior developer live right in your Git repo - truly amazing!"* — [rappster](https://github.com/Aider-AI/aider/issues/124) +- *"What an amazing tool. It's incredible."* — [valyagolev](https://github.com/Aider-AI/aider/issues/6#issue-1722897858) +- *"Aider is such an astounding thing!"* — [cgrothaus](https://github.com/Aider-AI/aider/issues/82#issuecomment-1631876700) +- *"It was WAY faster than I would be getting off the ground and making the first few working versions."* — [Daniel Feldman](https://twitter.com/d_feldman/status/1662295077387923456) +- *"THANK YOU for Aider! It really feels like a glimpse into the future of coding."* — [derwiki](https://news.ycombinator.com/item?id=38205643) +- *"It's just amazing. It is freeing me to do things I felt were out my comfort zone before."* — [Dougie](https://discord.com/channels/1131200896827654144/1174002618058678323/1174084556257775656) +- *"This project is stellar."* — [funkytaco](https://github.com/Aider-AI/aider/issues/112#issuecomment-1637429008) +- *"Amazing project, definitely the best AI coding assistant I've used."* — [joshuavial](https://github.com/Aider-AI/aider/issues/84) +- *"I absolutely love using Aider ... It makes software development feel so much lighter as an experience."* — [principalideal0](https://discord.com/channels/1131200896827654144/1133421607499595858/1229689636012691468) +- *"I have been recovering from multiple shoulder surgeries ... and have used aider extensively. It has allowed me to continue productivity."* — [codeninja](https://www.reddit.com/r/OpenAI/s/nmNwkHy1zG) +- *"I am an aider addict. I'm getting so much more work done, but in less time."* — [dandandan](https://discord.com/channels/1131200896827654144/1131200896827654149/1135913253483069470) +- *"After wasting $100 on tokens trying to find something better, I'm back to Aider. It blows everything else out of the water hands down, there's no competition whatsoever."* — [SystemSculpt](https://discord.com/channels/1131200896827654144/1131200896827654149/1178736602797846548) +- *"Aider is amazing, coupled with Sonnet 3.5 it's quite mind blowing."* — [Josh Dingus](https://discord.com/channels/1131200896827654144/1133060684540813372/1262374225298198548) +- *"Hands down, this is the best AI coding assistant tool so far."* — [IndyDevDan](https://www.youtube.com/watch?v=MPYFPvxfGZs) +- *"[Aider] changed my daily coding workflows. It's mind-blowing how a single Python application can change your life."* — [maledorak](https://discord.com/channels/1131200896827654144/1131200896827654149/1258453375620747264) +- *"Best agent for actual dev work in existing codebases."* — [Nick Dobos](https://twitter.com/NickADobos/status/1690408967963652097?s=20) +- *"One of my favorite pieces of software. Blazing trails on new paradigms!"* — [Chris Wall](https://x.com/chris65536/status/1905053299251798432) +- *"Aider has been revolutionary for me and my work."* — [Starry Hope](https://x.com/starryhopeblog/status/1904985812137132056) +- *"Try aider! One of the best ways to vibe code."* — [Chris Wall](https://x.com/Chris65536/status/1905053418961391929) +- *"Aider is hands down the best. And it's free and opensource."* — [AriyaSavakaLurker](https://www.reddit.com/r/ChatGPTCoding/comments/1ik16y6/whats_your_take_on_aider/mbip39n/) +- *"Aider is also my best friend."* — [jzn21](https://www.reddit.com/r/ChatGPTCoding/comments/1heuvuo/aider_vs_cline_vs_windsurf_vs_cursor/m27dcnb/) +- *"Try Aider, it's worth it."* — [jorgejhms](https://www.reddit.com/r/ChatGPTCoding/comments/1heuvuo/aider_vs_cline_vs_windsurf_vs_cursor/m27cp99/) +- *"I like aider :)"* — [Chenwei Cui](https://x.com/ccui42/status/1904965344999145698) +- *"Aider is the precision tool of LLM code gen... Minimal, thoughtful and capable of surgical changes to your codebase all while keeping the developer in control."* — [Reilly Sweetland](https://x.com/rsweetland/status/1904963807237259586) +- *"Cannot believe aider vibe coded a 650 LOC feature across service and cli today in 1 shot."* - [autopoietist](https://discord.com/channels/1131200896827654144/1131200896827654149/1355675042259796101) +- *"Oh no the secret is out! Yes, Aider is the best coding tool around. I highly, highly recommend it to anyone."* — [Joshua D Vander Hook](https://x.com/jodavaho/status/1911154899057795218) +- *"thanks to aider, i have started and finished three personal projects within the last two days"* — [joseph stalzyn](https://x.com/anitaheeder/status/1908338609645904160) +- *"Been using aider as my daily driver for over a year ... I absolutely love the tool, like beyond words."* — [koleok](https://discord.com/channels/1131200896827654144/1273248471394291754/1356727448372252783) +- *"aider is really cool"* — [kache (@yacineMTB)](https://x.com/yacineMTB/status/1911224442430124387) -- *The best free open source AI coding assistant.* -- [IndyDevDan](https://youtu.be/YALpX8oOn78) -- *The best AI coding assistant so far.* -- [Matthew Berman](https://www.youtube.com/watch?v=df8afeb1FY8) -- *Aider ... has easily quadrupled my coding productivity.* -- [SOLAR_FIELDS](https://news.ycombinator.com/item?id=36212100) -- *It's a cool workflow... Aider's ergonomics are perfect for me.* -- [qup](https://news.ycombinator.com/item?id=38185326) -- *It's really like having your senior developer live right in your Git repo - truly amazing!* -- [rappster](https://github.com/Aider-AI/aider/issues/124) -- *What an amazing tool. It's incredible.* -- [valyagolev](https://github.com/Aider-AI/aider/issues/6#issue-1722897858) -- *Aider is such an astounding thing!* -- [cgrothaus](https://github.com/Aider-AI/aider/issues/82#issuecomment-1631876700) -- *It was WAY faster than I would be getting off the ground and making the first few working versions.* -- [Daniel Feldman](https://twitter.com/d_feldman/status/1662295077387923456) -- *THANK YOU for Aider! It really feels like a glimpse into the future of coding.* -- [derwiki](https://news.ycombinator.com/item?id=38205643) -- *It's just amazing. It is freeing me to do things I felt were out my comfort zone before.* -- [Dougie](https://discord.com/channels/1131200896827654144/1174002618058678323/1174084556257775656) -- *This project is stellar.* -- [funkytaco](https://github.com/Aider-AI/aider/issues/112#issuecomment-1637429008) -- *Amazing project, definitely the best AI coding assistant I've used.* -- [joshuavial](https://github.com/Aider-AI/aider/issues/84) -- *I absolutely love using Aider ... It makes software development feel so much lighter as an experience.* -- [principalideal0](https://discord.com/channels/1131200896827654144/1133421607499595858/1229689636012691468) -- *I have been recovering from multiple shoulder surgeries ... and have used aider extensively. It has allowed me to continue productivity.* -- [codeninja](https://www.reddit.com/r/OpenAI/s/nmNwkHy1zG) -- *I am an aider addict. I'm getting so much more work done, but in less time.* -- [dandandan](https://discord.com/channels/1131200896827654144/1131200896827654149/1135913253483069470) -- *After wasting $100 on tokens trying to find something better, I'm back to Aider. It blows everything else out of the water hands down, there's no competition whatsoever.* -- [SystemSculpt](https://discord.com/channels/1131200896827654144/1131200896827654149/1178736602797846548) -- *Aider is amazing, coupled with Sonnet 3.5 it’s quite mind blowing.* -- [Josh Dingus](https://discord.com/channels/1131200896827654144/1133060684540813372/1262374225298198548) -- *Hands down, this is the best AI coding assistant tool so far.* -- [IndyDevDan](https://www.youtube.com/watch?v=MPYFPvxfGZs) -- *[Aider] changed my daily coding workflows. It's mind-blowing how a single Python application can change your life.* -- [maledorak](https://discord.com/channels/1131200896827654144/1131200896827654149/1258453375620747264) -- *Best agent for actual dev work in existing codebases.* -- [Nick Dobos](https://twitter.com/NickADobos/status/1690408967963652097?s=20) diff --git a/aider/__init__.py b/aider/__init__.py index d569493ec..825bf0439 100644 --- a/aider/__init__.py +++ b/aider/__init__.py @@ -1,6 +1,6 @@ from packaging import version -__version__ = "0.75.2.dev" +__version__ = "0.82.3.dev" safe_version = __version__ try: diff --git a/aider/args.py b/aider/args.py index 2a55b4d28..6df19778b 100644 --- a/aider/args.py +++ b/aider/args.py @@ -3,6 +3,7 @@ import argparse import os import sys +from pathlib import Path import configargparse @@ -12,10 +13,20 @@ from aider.args_formatter import ( MarkdownHelpFormatter, YamlHelpFormatter, ) +from aider.deprecated import add_deprecated_model_args from .dump import dump # noqa: F401 +def resolve_aiderignore_path(path_str, git_root=None): + path = Path(path_str) + if path.is_absolute(): + return str(path) + elif git_root: + return str(Path(git_root) / path) + return str(path) + + def default_env_file(git_root): return os.path.join(git_root, ".env") if git_root else ".env" @@ -38,98 +49,6 @@ def get_parser(default_config_files, git_root): default=None, help="Specify the model to use for the main chat", ) - opus_model = "claude-3-opus-20240229" - group.add_argument( - "--opus", - action="store_const", - dest="model", - const=opus_model, - help=f"Use {opus_model} model for the main chat", - ) - sonnet_model = "anthropic/claude-3-7-sonnet-20250219" - group.add_argument( - "--sonnet", - action="store_const", - dest="model", - const=sonnet_model, - help=f"Use {sonnet_model} model for the main chat", - ) - haiku_model = "claude-3-5-haiku-20241022" - group.add_argument( - "--haiku", - action="store_const", - dest="model", - const=haiku_model, - help=f"Use {haiku_model} model for the main chat", - ) - gpt_4_model = "gpt-4-0613" - group.add_argument( - "--4", - "-4", - action="store_const", - dest="model", - const=gpt_4_model, - help=f"Use {gpt_4_model} model for the main chat", - ) - gpt_4o_model = "gpt-4o" - group.add_argument( - "--4o", - action="store_const", - dest="model", - const=gpt_4o_model, - help=f"Use {gpt_4o_model} model for the main chat", - ) - gpt_4o_mini_model = "gpt-4o-mini" - group.add_argument( - "--mini", - action="store_const", - dest="model", - const=gpt_4o_mini_model, - help=f"Use {gpt_4o_mini_model} model for the main chat", - ) - gpt_4_turbo_model = "gpt-4-1106-preview" - group.add_argument( - "--4-turbo", - action="store_const", - dest="model", - const=gpt_4_turbo_model, - help=f"Use {gpt_4_turbo_model} model for the main chat", - ) - gpt_3_model_name = "gpt-3.5-turbo" - group.add_argument( - "--35turbo", - "--35-turbo", - "--3", - "-3", - action="store_const", - dest="model", - const=gpt_3_model_name, - help=f"Use {gpt_3_model_name} model for the main chat", - ) - deepseek_model = "deepseek/deepseek-chat" - group.add_argument( - "--deepseek", - action="store_const", - dest="model", - const=deepseek_model, - help=f"Use {deepseek_model} model for the main chat", - ) - o1_mini_model = "o1-mini" - group.add_argument( - "--o1-mini", - action="store_const", - dest="model", - const=o1_mini_model, - help=f"Use {o1_mini_model} model for the main chat", - ) - o1_preview_model = "o1-preview" - group.add_argument( - "--o1-preview", - action="store_const", - dest="model", - const=o1_preview_model, - help=f"Use {o1_preview_model} model for the main chat", - ) ########## group = parser.add_argument_group("API Keys and settings") @@ -208,6 +127,11 @@ def get_parser(default_config_files, git_root): type=str, help="Set the reasoning_effort API parameter (default: not set)", ) + group.add_argument( + "--thinking-tokens", + type=str, + help="Set the thinking token budget for models that support it (default: not set)", + ) group.add_argument( "--verify-ssl", action=argparse.BooleanOptionalAction, @@ -234,6 +158,12 @@ def get_parser(default_config_files, git_root): const="architect", help="Use architect edit format for the main chat", ) + group.add_argument( + "--auto-accept-architect", + action=argparse.BooleanOptionalAction, + default=True, + help="Enable/disable automatic acceptance of architect changes (default: True)", + ) group.add_argument( "--weak-model", metavar="WEAK_MODEL", @@ -261,6 +191,14 @@ def get_parser(default_config_files, git_root): default=True, help="Only work with models that have meta-data available (default: True)", ) + group.add_argument( + "--check-model-accepts-settings", + action=argparse.BooleanOptionalAction, + default=True, + help=( + "Check if model accepts settings like reasoning_effort/thinking_tokens (default: True)" + ), + ) group.add_argument( "--max-chat-history-tokens", type=int, @@ -460,9 +398,11 @@ def get_parser(default_config_files, git_root): default_aiderignore_file = ( os.path.join(git_root, ".aiderignore") if git_root else ".aiderignore" ) + group.add_argument( "--aiderignore", metavar="AIDERIGNORE", + type=lambda path_str: resolve_aiderignore_path(path_str, git_root), default=default_aiderignore_file, help="Specify the aider ignore file (default: .aiderignore in git root)", ) @@ -508,6 +448,12 @@ def get_parser(default_config_files, git_root): default=False, help="Prefix all commit messages with 'aider: ' (default: False)", ) + group.add_argument( + "--git-commit-verify", + action=argparse.BooleanOptionalAction, + default=False, + help="Enable/disable git pre-commit hooks with --no-verify (default: False)", + ) group.add_argument( "--commit", action="store_true", @@ -813,6 +759,24 @@ def get_parser(default_config_files, git_root): default=False, help="Enable/disable multi-line input mode with Meta-Enter to submit (default: False)", ) + group.add_argument( + "--notifications", + action=argparse.BooleanOptionalAction, + default=False, + help=( + "Enable/disable terminal bell notifications when LLM responses are ready (default:" + " False)" + ), + ) + group.add_argument( + "--notifications-command", + metavar="COMMAND", + default=None, + help=( + "Specify a command to run for notifications instead of the terminal bell. If not" + " specified, a default command for your OS may be used." + ), + ) group.add_argument( "--detect-urls", action=argparse.BooleanOptionalAction, @@ -823,12 +787,11 @@ def get_parser(default_config_files, git_root): "--editor", help="Specify which editor to use for the /editor command", ) - group.add_argument( - "--install-tree-sitter-language-pack", - action="store_true", - help="Install the tree_sitter_language_pack (experimental)", - default=False, - ) + + ########## + group = parser.add_argument_group("Deprecated model settings") + # Add deprecated model shortcut arguments + add_deprecated_model_args(parser, group) return parser diff --git a/aider/args_formatter.py b/aider/args_formatter.py index 5b458fec2..c7672ccba 100644 --- a/aider/args_formatter.py +++ b/aider/args_formatter.py @@ -143,16 +143,22 @@ class YamlHelpFormatter(argparse.HelpFormatter): default = "true" if default: - parts.append(f"#{switch}: {default}\n") + if "#" in default: + parts.append(f'#{switch}: "{default}"\n') + else: + parts.append(f"#{switch}: {default}\n") elif action.nargs in ("*", "+") or isinstance(action, argparse._AppendAction): parts.append(f"#{switch}: xxx") parts.append("## Specify multiple values like this:") parts.append(f"#{switch}:") - parts.append(f"# - xxx") - parts.append(f"# - yyy") - parts.append(f"# - zzz") + parts.append("# - xxx") + parts.append("# - yyy") + parts.append("# - zzz") else: - parts.append(f"#{switch}: xxx\n") + if switch.endswith("color"): + parts.append(f'#{switch}: "xxx"\n') + else: + parts.append(f"#{switch}: xxx\n") ### # parts.append(str(action)) diff --git a/aider/coders/__init__.py b/aider/coders/__init__.py index f7a9f9ad4..0980dde18 100644 --- a/aider/coders/__init__.py +++ b/aider/coders/__init__.py @@ -1,12 +1,16 @@ from .architect_coder import ArchitectCoder from .ask_coder import AskCoder from .base_coder import Coder +from .context_coder import ContextCoder from .editblock_coder import EditBlockCoder from .editblock_fenced_coder import EditBlockFencedCoder +from .editor_diff_fenced_coder import EditorDiffFencedCoder from .editor_editblock_coder import EditorEditBlockCoder from .editor_whole_coder import EditorWholeFileCoder from .help_coder import HelpCoder +from .patch_coder import PatchCoder from .udiff_coder import UnifiedDiffCoder +from .udiff_simple import UnifiedDiffSimpleCoder from .wholefile_coder import WholeFileCoder from .batch_coder import BatchCoder # from .single_wholefile_func_coder import SingleWholeFileFunctionCoder @@ -19,9 +23,13 @@ __all__ = [ EditBlockFencedCoder, BatchCoder, WholeFileCoder, + PatchCoder, UnifiedDiffCoder, + UnifiedDiffSimpleCoder, # SingleWholeFileFunctionCoder, ArchitectCoder, EditorEditBlockCoder, EditorWholeFileCoder, + EditorDiffFencedCoder, + ContextCoder, ] diff --git a/aider/coders/architect_coder.py b/aider/coders/architect_coder.py index a561e3e0d..f3e2a38b1 100644 --- a/aider/coders/architect_coder.py +++ b/aider/coders/architect_coder.py @@ -6,6 +6,7 @@ from .base_coder import Coder class ArchitectCoder(AskCoder): edit_format = "architect" gpt_prompts = ArchitectPrompts() + auto_accept_architect = False def reply_completed(self): content = self.partial_response_content @@ -13,7 +14,7 @@ class ArchitectCoder(AskCoder): if not content or not content.strip(): return - if not self.io.confirm_ask("Edit the files?"): + if not self.auto_accept_architect and not self.io.confirm_ask("Edit the files?"): return kwargs = dict() diff --git a/aider/coders/base_coder.py b/aider/coders/base_coder.py index d377979be..675570c60 100755 --- a/aider/coders/base_coder.py +++ b/aider/coders/base_coder.py @@ -28,6 +28,12 @@ from aider.io import ConfirmGroup, InputOutput from aider.linter import Linter from aider.llm import litellm from aider.models import RETRY_TIMEOUT +from aider.reasoning_tags import ( + REASONING_TAG, + format_reasoning_content, + remove_reasoning_content, + replace_reasoning_tags, +) from aider.repo import ANY_GIT_ERROR, GitRepo from aider.repomap import RepoMap from aider.run_cmd import run_cmd @@ -201,10 +207,22 @@ class Coder: prefix = "Model" output = f"{prefix}: {main_model.name} with {self.edit_format} edit format" + + # Check for thinking token budget + thinking_tokens = main_model.get_thinking_tokens() + if thinking_tokens: + output += f", {thinking_tokens} think tokens" + + # Check for reasoning effort + reasoning_effort = main_model.get_reasoning_effort() + if reasoning_effort: + output += f", reasoning {reasoning_effort}" + if self.add_cache_headers or main_model.caches_by_default: output += ", prompt cache" if main_model.info.get("supports_assistant_prefill"): output += ", infinite output" + lines.append(output) if self.edit_format == "architect": @@ -304,6 +322,7 @@ class Coder: ignore_mentions=None, file_watcher=None, auto_copy_context=False, + auto_accept_architect=True, ): # Fill in a dummy Analytics if needed, but it is never .enable()'d self.analytics = analytics if analytics is not None else Analytics() @@ -316,6 +335,7 @@ class Coder: self.abs_root_path_cache = {} self.auto_copy_context = auto_copy_context + self.auto_accept_architect = auto_accept_architect self.ignore_mentions = ignore_mentions if not self.ignore_mentions: @@ -375,6 +395,10 @@ class Coder: self.pretty = self.io.pretty self.main_model = main_model + # Set the reasoning tag name based on model settings or default + self.reasoning_tag_name = ( + self.main_model.reasoning_tag if self.main_model.reasoning_tag else REASONING_TAG + ) self.stream = stream and main_model.streaming @@ -898,10 +922,11 @@ class Coder: else: self.io.tool_error(text) - url_pattern = re.compile(r"(https?://[^\s/$.?#].[^\s]*)") + # Exclude double quotes from the matched URL characters + url_pattern = re.compile(r'(https?://[^\s/$.?#].[^\s"]*)') urls = list(set(url_pattern.findall(text))) # Use set to remove duplicates for url in urls: - url = url.rstrip(".',\"") + url = url.rstrip(".',\"}") # Added } to the characters to strip self.io.offer_url(url) return urls @@ -910,7 +935,8 @@ class Coder: if not self.detect_urls: return inp - url_pattern = re.compile(r"(https?://[^\s/$.?#].[^\s]*[^\s,.])") + # Exclude double quotes from the matched URL characters + url_pattern = re.compile(r'(https?://[^\s/$.?#].[^\s"]*[^\s,.])') urls = list(set(url_pattern.findall(inp))) # Use set to remove duplicates group = ConfirmGroup(urls) for url in urls: @@ -1006,7 +1032,13 @@ class Coder: return None def get_platform_info(self): - platform_text = f"- Platform: {platform.platform()}\n" + platform_text = "" + try: + platform_text = f"- Platform: {platform.platform()}\n" + except KeyError: + # Skip platform info if it can't be retrieved + platform_text = "- Platform information unavailable\n" + shell_var = "COMSPEC" if os.name == "nt" else "SHELL" shell_val = os.getenv(shell_var) platform_text += f"- Shell: {shell_var}={shell_val}\n" @@ -1047,17 +1079,25 @@ class Coder: return platform_text def fmt_system_prompt(self, prompt): - lazy_prompt = self.gpt_prompts.lazy_prompt if self.main_model.lazy else "" + if self.main_model.lazy: + lazy_prompt = self.gpt_prompts.lazy_prompt + elif self.main_model.overeager: + lazy_prompt = self.gpt_prompts.overeager_prompt + else: + lazy_prompt = "" + platform_text = self.get_platform_info() if self.suggest_shell_commands: shell_cmd_prompt = self.gpt_prompts.shell_cmd_prompt.format(platform=platform_text) shell_cmd_reminder = self.gpt_prompts.shell_cmd_reminder.format(platform=platform_text) + rename_with_shell = self.gpt_prompts.rename_with_shell else: shell_cmd_prompt = self.gpt_prompts.no_shell_cmd_prompt.format(platform=platform_text) shell_cmd_reminder = self.gpt_prompts.no_shell_cmd_reminder.format( platform=platform_text ) + rename_with_shell = "" if self.chat_language: language = self.chat_language @@ -1077,7 +1117,9 @@ class Coder: lazy_prompt=lazy_prompt, platform=platform_text, shell_cmd_prompt=shell_cmd_prompt, + rename_with_shell=rename_with_shell, shell_cmd_reminder=shell_cmd_reminder, + go_ahead_tip=self.gpt_prompts.go_ahead_tip, language=language, ) @@ -1280,6 +1322,9 @@ class Coder: def send_message(self, inp): self.event("message_send_starting") + # Notify IO that LLM processing is starting + self.io.llm_started() + self.cur_messages += [ dict(role="user", content=inp), ] @@ -1369,11 +1414,14 @@ class Coder: self.mdstream = None self.partial_response_content = self.get_multi_response_content_in_progress(True) - self.partial_response_content = self.main_model.remove_reasoning_content( - self.partial_response_content - ) + self.remove_reasoning_content() self.multi_response_content = "" + ### + # print() + # print("=" * 20) + # dump(self.partial_response_content) + self.io.tool_output() self.show_usage_report() @@ -1414,7 +1462,8 @@ class Coder: return try: - self.reply_completed() + if self.reply_completed(): + return except KeyboardInterrupt: interrupted = True @@ -1557,30 +1606,30 @@ class Coder: ) ] - def get_file_mentions(self, content): + def get_file_mentions(self, content, ignore_current=False): words = set(word for word in content.split()) # drop sentence punctuation from the end words = set(word.rstrip(",.!;:?") for word in words) # strip away all kinds of quotes - quotes = "".join(['"', "'", "`"]) + quotes = "\"'`*_" words = set(word.strip(quotes) for word in words) - addable_rel_fnames = self.get_addable_relative_files() + if ignore_current: + addable_rel_fnames = self.get_all_relative_files() + existing_basenames = {} + else: + addable_rel_fnames = self.get_addable_relative_files() - # Get basenames of files already in chat or read-only - existing_basenames = {os.path.basename(f) for f in self.get_inchat_relative_files()} | { - os.path.basename(self.get_rel_fname(f)) for f in self.abs_read_only_fnames - } + # Get basenames of files already in chat or read-only + existing_basenames = {os.path.basename(f) for f in self.get_inchat_relative_files()} | { + os.path.basename(self.get_rel_fname(f)) for f in self.abs_read_only_fnames + } mentioned_rel_fnames = set() fname_to_rel_fnames = {} for rel_fname in addable_rel_fnames: - # Skip files that share a basename with files already in chat - if os.path.basename(rel_fname) in existing_basenames: - continue - normalized_rel_fname = rel_fname.replace("\\", "/") normalized_words = set(word.replace("\\", "/") for word in words) if normalized_rel_fname in normalized_words: @@ -1595,6 +1644,10 @@ class Coder: fname_to_rel_fnames[fname].append(rel_fname) for fname, rel_fnames in fname_to_rel_fnames.items(): + # If the basename is already in chat, don't add based on a basename mention + if fname in existing_basenames: + continue + # If the basename mention is unique among addable files and present in the text if len(rel_fnames) == 1 and fname in words: mentioned_rel_fnames.add(rel_fnames[0]) @@ -1623,6 +1676,9 @@ class Coder: return prompts.added_files.format(fnames=", ".join(added_fnames)) def send(self, messages, model=None, functions=None): + self.got_reasoning_content = False + self.ended_reasoning_content = False + if not model: model = self.main_model @@ -1690,6 +1746,14 @@ class Coder: except AttributeError as func_err: show_func_err = func_err + try: + reasoning_content = completion.choices[0].message.reasoning_content + except AttributeError: + try: + reasoning_content = completion.choices[0].message.reasoning + except AttributeError: + reasoning_content = None + try: self.partial_response_content = completion.choices[0].message.content or "" except AttributeError as content_err: @@ -1708,6 +1772,15 @@ class Coder: raise Exception("No data found in LLM response!") show_resp = self.render_incremental_response(True) + + if reasoning_content: + formatted_reasoning = format_reasoning_content( + reasoning_content, self.reasoning_tag_name + ) + show_resp = formatted_reasoning + show_resp + + show_resp = replace_reasoning_tags(show_resp, self.reasoning_tag_name) + self.io.assistant_output(show_resp, pretty=self.show_pretty()) if ( @@ -1717,6 +1790,8 @@ class Coder: raise FinishReasonLength() def show_send_output_stream(self, completion): + received_content = False + for chunk in completion: if len(chunk.choices) == 0: continue @@ -1735,19 +1810,46 @@ class Coder: self.partial_response_function_call[k] += v else: self.partial_response_function_call[k] = v + received_content = True except AttributeError: pass + text = "" + try: - text = chunk.choices[0].delta.content - if text: - self.partial_response_content += text + reasoning_content = chunk.choices[0].delta.reasoning_content except AttributeError: - text = None + try: + reasoning_content = chunk.choices[0].delta.reasoning + except AttributeError: + reasoning_content = None + + if reasoning_content: + if not self.got_reasoning_content: + text += f"<{REASONING_TAG}>\n\n" + text += reasoning_content + self.got_reasoning_content = True + received_content = True + + try: + content = chunk.choices[0].delta.content + if content: + if self.got_reasoning_content and not self.ended_reasoning_content: + text += f"\n\n\n\n" + self.ended_reasoning_content = True + + text += content + received_content = True + except AttributeError: + pass + + self.partial_response_content += text if self.show_pretty(): self.live_incremental_response(False) elif text: + # Apply reasoning tag formatting + text = replace_reasoning_tags(text, self.reasoning_tag_name) try: sys.stdout.write(text) except UnicodeEncodeError: @@ -1759,13 +1861,26 @@ class Coder: sys.stdout.flush() yield text + if not received_content: + self.io.tool_warning("Empty response received from LLM. Check your provider account?") + def live_incremental_response(self, final): show_resp = self.render_incremental_response(final) + # Apply any reasoning tag formatting + show_resp = replace_reasoning_tags(show_resp, self.reasoning_tag_name) self.mdstream.update(show_resp, final=final) def render_incremental_response(self, final): return self.get_multi_response_content_in_progress() + def remove_reasoning_content(self): + """Remove reasoning content from the model's response.""" + + self.partial_response_content = remove_reasoning_content( + self.partial_response_content, + self.reasoning_tag_name, + ) + def calculate_and_show_tokens_and_cost(self, messages, completion=None): prompt_tokens = 0 completion_tokens = 0 @@ -1852,11 +1967,6 @@ class Coder: f" ${format_cost(self.total_cost)} session." ) - if self.add_cache_headers and self.stream: - warning = " Use --no-stream for accurate caching costs." - self.usage_report = tokens_report + "\n" + cost_report + warning - return - if cache_hit_tokens and cache_write_tokens: sep = "\n" else: diff --git a/aider/coders/base_prompts.py b/aider/coders/base_prompts.py index c431c7520..464212031 100644 --- a/aider/coders/base_prompts.py +++ b/aider/coders/base_prompts.py @@ -14,6 +14,9 @@ You NEVER leave comments describing code without implementing it! You always COMPLETELY IMPLEMENT the needed code! """ + overeager_prompt = """Pay careful attention to the scope of the user's request. +Do what they ask, but no more.""" + example_messages = [] files_content_prefix = """I have *added these files to the chat* so you can go ahead and edit them. @@ -50,3 +53,6 @@ Do not edit these files! shell_cmd_reminder = "" no_shell_cmd_prompt = "" no_shell_cmd_reminder = "" + + rename_with_shell = "" + go_ahead_tip = "" diff --git a/aider/coders/context_coder.py b/aider/coders/context_coder.py new file mode 100644 index 000000000..73fe64af0 --- /dev/null +++ b/aider/coders/context_coder.py @@ -0,0 +1,53 @@ +from .base_coder import Coder +from .context_prompts import ContextPrompts + + +class ContextCoder(Coder): + """Identify which files need to be edited for a given request.""" + + edit_format = "context" + gpt_prompts = ContextPrompts() + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + if not self.repo_map: + return + + self.repo_map.refresh = "always" + self.repo_map.max_map_tokens *= self.repo_map.map_mul_no_files + self.repo_map.map_mul_no_files = 1.0 + + def reply_completed(self): + content = self.partial_response_content + if not content or not content.strip(): + return True + + # dump(repr(content)) + current_rel_fnames = set(self.get_inchat_relative_files()) + mentioned_rel_fnames = set(self.get_file_mentions(content, ignore_current=True)) + + # dump(current_rel_fnames) + # dump(mentioned_rel_fnames) + # dump(current_rel_fnames == mentioned_rel_fnames) + + if mentioned_rel_fnames == current_rel_fnames: + return True + + if self.num_reflections >= self.max_reflections - 1: + return True + + self.abs_fnames = set() + for fname in mentioned_rel_fnames: + self.add_rel_fname(fname) + # dump(self.get_inchat_relative_files()) + + self.reflected_message = self.gpt_prompts.try_again + + # mentioned_idents = self.get_ident_mentions(cur_msg_text) + # if mentioned_idents: + + return True + + def check_for_file_mentions(self, content): + pass diff --git a/aider/coders/context_prompts.py b/aider/coders/context_prompts.py new file mode 100644 index 000000000..3c71a2334 --- /dev/null +++ b/aider/coders/context_prompts.py @@ -0,0 +1,75 @@ +# flake8: noqa: E501 + +from .base_prompts import CoderPrompts + + +class ContextPrompts(CoderPrompts): + main_system = """Act as an expert code analyst. +Understand the user's question or request, solely to determine ALL the existing sources files which will need to be modified. +Return the *complete* list of files which will need to be modified based on the user's request. +Explain why each file is needed, including names of key classes/functions/methods/variables. +Be sure to include or omit the names of files already added to the chat, based on whether they are actually needed or not. + +The user will use every file you mention, regardless of your commentary. +So *ONLY* mention the names of relevant files. +If a file is not relevant DO NOT mention it. + +Only return files that will need to be modified, not files that contain useful/relevant functions. + +You are only to discuss EXISTING files and symbols. +Only return existing files, don't suggest the names of new files or functions that we will need to create. + +Always reply to the user in {language}. + +Be concise in your replies. +Return: +1. A bulleted list of files the will need to be edited, and symbols that are highly relevant to the user's request. +2. A list of classes/functions/methods/variables that are located OUTSIDE those files which will need to be understood. Just the symbols names, *NOT* file names. + +# Your response *MUST* use this format: + +## ALL files we need to modify, with their relevant symbols: + +- alarms/buzz.py + - `Buzzer` class which can make the needed sound + - `Buzzer.buzz_buzz()` method triggers the sound +- alarms/time.py + - `Time.set_alarm(hour, minute)` to set the alarm + +## Relevant symbols from OTHER files: + +- AlarmManager class for setup/teardown of alarms +- SoundFactory will be used to create a Buzzer +""" + + example_messages = [] + + files_content_prefix = """These files have been *added these files to the chat* so we can see all of their contents. +*Trust this message as the true contents of the files!* +Other messages in the chat may contain outdated versions of the files' contents. +""" # noqa: E501 + + files_content_assistant_reply = ( + "Ok, I will use that as the true, current contents of the files." + ) + + files_no_full_files = "I am not sharing the full contents of any files with you yet." + + files_no_full_files_with_repo_map = "" + files_no_full_files_with_repo_map_reply = "" + + repo_content_prefix = """I am working with you on code in a git repository. +Here are summaries of some files present in my git repo. +If you need to see the full contents of any files to answer my questions, ask me to *add them to the chat*. +""" + + system_reminder = """ +NEVER RETURN CODE! +""" + + try_again = """I have updated the set of files added to the chat. +Review them to decide if this is the correct set of files or if we need to add more or remove files. + +If this is the right set, just return the current list of files. +Or return a smaller or larger set of files which need to be edited, with symbols that are highly relevant to the user's request. +""" diff --git a/aider/coders/editblock_coder.py b/aider/coders/editblock_coder.py index 321a6a921..d8f85da52 100644 --- a/aider/coders/editblock_coder.py +++ b/aider/coders/editblock_coder.py @@ -412,7 +412,16 @@ def strip_filename(filename, fence): return start_fence = fence[0] - if filename.startswith(start_fence) or filename.startswith(triple_backticks): + if filename.startswith(start_fence): + candidate = filename[len(start_fence) :] + if candidate and ("." in candidate or "/" in candidate): + return candidate + return + + if filename.startswith(triple_backticks): + candidate = filename[len(triple_backticks) :] + if candidate and ("." in candidate or "/" in candidate): + return candidate return filename = filename.rstrip(":") @@ -454,7 +463,14 @@ def find_original_update_blocks(content, fence=DEFAULT_FENCE, valid_fnames=None) "```csh", "```tcsh", ] - next_is_editblock = i + 1 < len(lines) and head_pattern.match(lines[i + 1].strip()) + + # Check if the next line or the one after that is an editblock + next_is_editblock = ( + i + 1 < len(lines) + and head_pattern.match(lines[i + 1].strip()) + or i + 2 < len(lines) + and head_pattern.match(lines[i + 2].strip()) + ) if any(line.strip().startswith(start) for start in shell_starts) and not next_is_editblock: shell_content = [] diff --git a/aider/coders/editblock_fenced_prompts.py b/aider/coders/editblock_fenced_prompts.py index 07a8fbbab..74f647f93 100644 --- a/aider/coders/editblock_fenced_prompts.py +++ b/aider/coders/editblock_fenced_prompts.py @@ -19,7 +19,7 @@ class EditBlockFencedPrompts(EditBlockPrompts): Here are the *SEARCH/REPLACE* blocks: -{fence[0]} +{fence[0]}python mathweb/flask/app.py <<<<<<< SEARCH from flask import Flask @@ -29,7 +29,7 @@ from flask import Flask >>>>>>> REPLACE {fence[1]} -{fence[0]} +{fence[0]}python mathweb/flask/app.py <<<<<<< SEARCH def factorial(n): @@ -44,7 +44,7 @@ def factorial(n): >>>>>>> REPLACE {fence[1]} -{fence[0]} +{fence[0]}python mathweb/flask/app.py <<<<<<< SEARCH return str(factorial(n)) @@ -68,7 +68,7 @@ mathweb/flask/app.py Here are the *SEARCH/REPLACE* blocks: -{fence[0]} +{fence[0]}python hello.py <<<<<<< SEARCH ======= @@ -79,7 +79,7 @@ def hello(): >>>>>>> REPLACE {fence[1]} -{fence[0]} +{fence[0]}python main.py <<<<<<< SEARCH def hello(): @@ -93,3 +93,51 @@ from hello import hello """, ), ] + + system_reminder = """ +# *SEARCH/REPLACE block* Rules: + +Every *SEARCH/REPLACE block* must use this format: +1. The opening fence and code language, eg: {fence[0]}python +2. The *FULL* file path alone on a line, verbatim. No bold asterisks, no quotes around it, no escaping of characters, etc. +3. The start of search block: <<<<<<< SEARCH +4. A contiguous chunk of lines to search for in the existing source code +5. The dividing line: ======= +6. The lines to replace into the source code +7. The end of the replace block: >>>>>>> REPLACE +8. The closing fence: {fence[1]} + +Use the *FULL* file path, as shown to you by the user. +{quad_backtick_reminder} +Every *SEARCH* section must *EXACTLY MATCH* the existing file content, character for character, including all comments, docstrings, etc. +If the file contains code or other data wrapped/escaped in json/xml/quotes or other containers, you need to propose edits to the literal contents of the file, including the container markup. + +*SEARCH/REPLACE* blocks will *only* replace the first match occurrence. +Including multiple unique *SEARCH/REPLACE* blocks if needed. +Include enough lines in each SEARCH section to uniquely match each set of lines that need to change. + +Keep *SEARCH/REPLACE* blocks concise. +Break large *SEARCH/REPLACE* blocks into a series of smaller blocks that each change a small portion of the file. +Include just the changing lines, and a few surrounding lines if needed for uniqueness. +Do not include long runs of unchanging lines in *SEARCH/REPLACE* blocks. + +Only create *SEARCH/REPLACE* blocks for files that the user has added to the chat! + +To move code within a file, use 2 *SEARCH/REPLACE* blocks: 1 to delete it from its current location, 1 to insert it in the new location. + +Pay attention to which filenames the user wants you to edit, especially if they are asking you to create a new file. + +If you want to put code in a new file, use a *SEARCH/REPLACE block* with: +- A new file path, including dir name if needed +- An empty `SEARCH` section +- The new file's contents in the `REPLACE` section + +To rename files which have been added to the chat, use shell commands at the end of your response. + +If the user just says something like "ok" or "go ahead" or "do that" they probably want you to make SEARCH/REPLACE blocks for the code changes you just proposed. +The user will say when they've applied your edits. If they haven't explicitly confirmed the edits have been applied, they probably want proper SEARCH/REPLACE blocks. + +{lazy_prompt} +ONLY EVER RETURN CODE IN A *SEARCH/REPLACE BLOCK*! +{shell_cmd_reminder} +""" diff --git a/aider/coders/editblock_prompts.py b/aider/coders/editblock_prompts.py index d183b0ab5..1457e021e 100644 --- a/aider/coders/editblock_prompts.py +++ b/aider/coders/editblock_prompts.py @@ -181,14 +181,17 @@ If you want to put code in a new file, use a *SEARCH/REPLACE block* with: - An empty `SEARCH` section - The new file's contents in the `REPLACE` section -To rename files which have been added to the chat, use shell commands at the end of your response. +{rename_with_shell}{go_ahead_tip}{lazy_prompt}ONLY EVER RETURN CODE IN A *SEARCH/REPLACE BLOCK*! +{shell_cmd_reminder} +""" -If the user just says something like "ok" or "go ahead" or "do that" they probably want you to make SEARCH/REPLACE blocks for the code changes you just proposed. + rename_with_shell = """To rename files which have been added to the chat, use shell commands at the end of your response. + +""" + + go_ahead_tip = """If the user just says something like "ok" or "go ahead" or "do that" they probably want you to make SEARCH/REPLACE blocks for the code changes you just proposed. The user will say when they've applied your edits. If they haven't explicitly confirmed the edits have been applied, they probably want proper SEARCH/REPLACE blocks. -{lazy_prompt} -ONLY EVER RETURN CODE IN A *SEARCH/REPLACE BLOCK*! -{shell_cmd_reminder} """ shell_cmd_reminder = """ @@ -200,4 +203,5 @@ Examples of when to suggest shell commands: - Suggest OS-appropriate commands to delete or rename files/directories, or other file system operations. - If your code changes add new dependencies, suggest the command to install them. - Etc. + """ diff --git a/aider/coders/editor_diff_fenced_coder.py b/aider/coders/editor_diff_fenced_coder.py new file mode 100644 index 000000000..4edc010d0 --- /dev/null +++ b/aider/coders/editor_diff_fenced_coder.py @@ -0,0 +1,9 @@ +from .editblock_fenced_coder import EditBlockFencedCoder +from .editor_diff_fenced_prompts import EditorDiffFencedPrompts + + +class EditorDiffFencedCoder(EditBlockFencedCoder): + "A coder that uses search/replace blocks, focused purely on editing files." + + edit_format = "editor-diff-fenced" + gpt_prompts = EditorDiffFencedPrompts() diff --git a/aider/coders/editor_diff_fenced_prompts.py b/aider/coders/editor_diff_fenced_prompts.py new file mode 100644 index 000000000..15b906f79 --- /dev/null +++ b/aider/coders/editor_diff_fenced_prompts.py @@ -0,0 +1,11 @@ +# flake8: noqa: E501 + +from .editblock_fenced_prompts import EditBlockFencedPrompts + + +class EditorDiffFencedPrompts(EditBlockFencedPrompts): + shell_cmd_prompt = "" + no_shell_cmd_prompt = "" + shell_cmd_reminder = "" + go_ahead_tip = "" + rename_with_shell = "" diff --git a/aider/coders/editor_editblock_prompts.py b/aider/coders/editor_editblock_prompts.py index 595e28bee..463075e2c 100644 --- a/aider/coders/editor_editblock_prompts.py +++ b/aider/coders/editor_editblock_prompts.py @@ -14,3 +14,5 @@ ONLY EVER RETURN CODE IN A *SEARCH/REPLACE BLOCK*! shell_cmd_prompt = "" no_shell_cmd_prompt = "" shell_cmd_reminder = "" + go_ahead_tip = "" + rename_with_shell = "" diff --git a/aider/coders/patch_coder.py b/aider/coders/patch_coder.py new file mode 100644 index 000000000..802e6b9c3 --- /dev/null +++ b/aider/coders/patch_coder.py @@ -0,0 +1,706 @@ +import pathlib +from dataclasses import dataclass, field +from enum import Enum +from typing import Dict, List, Optional, Tuple + +from .base_coder import Coder +from .patch_prompts import PatchPrompts + + +# --------------------------------------------------------------------------- # +# Domain objects & Exceptions (Adapted from apply_patch.py) +# --------------------------------------------------------------------------- # +class DiffError(ValueError): + """Any problem detected while parsing or applying a patch.""" + + +class ActionType(str, Enum): + ADD = "Add" + DELETE = "Delete" + UPDATE = "Update" + + +@dataclass +class Chunk: + orig_index: int = -1 # Line number in the *original* file block where the change starts + del_lines: List[str] = field(default_factory=list) + ins_lines: List[str] = field(default_factory=list) + + +@dataclass +class PatchAction: + type: ActionType + path: str + # For ADD: + new_content: Optional[str] = None + # For UPDATE: + chunks: List[Chunk] = field(default_factory=list) + move_path: Optional[str] = None + + +# Type alias for the return type of get_edits +EditResult = Tuple[str, PatchAction] + + +@dataclass +class Patch: + actions: Dict[str, PatchAction] = field(default_factory=dict) + fuzz: int = 0 # Track fuzziness used during parsing + + +# --------------------------------------------------------------------------- # +# Helper functions (Adapted from apply_patch.py) +# --------------------------------------------------------------------------- # +def _norm(line: str) -> str: + """Strip CR so comparisons work for both LF and CRLF input.""" + return line.rstrip("\r") + + +def find_context_core(lines: List[str], context: List[str], start: int) -> Tuple[int, int]: + """Finds context block, returns start index and fuzz level.""" + if not context: + return start, 0 + + # Exact match + for i in range(start, len(lines) - len(context) + 1): + if lines[i : i + len(context)] == context: + return i, 0 + # Rstrip match + norm_context = [s.rstrip() for s in context] + for i in range(start, len(lines) - len(context) + 1): + if [s.rstrip() for s in lines[i : i + len(context)]] == norm_context: + return i, 1 # Fuzz level 1 + # Strip match + norm_context_strip = [s.strip() for s in context] + for i in range(start, len(lines) - len(context) + 1): + if [s.strip() for s in lines[i : i + len(context)]] == norm_context_strip: + return i, 100 # Fuzz level 100 + return -1, 0 + + +def find_context(lines: List[str], context: List[str], start: int, eof: bool) -> Tuple[int, int]: + """Finds context, handling EOF marker.""" + if eof: + # If EOF marker, first try matching at the very end + if len(lines) >= len(context): + new_index, fuzz = find_context_core(lines, context, len(lines) - len(context)) + if new_index != -1: + return new_index, fuzz + # If not found at end, search from `start` as fallback + new_index, fuzz = find_context_core(lines, context, start) + return new_index, fuzz + 10_000 # Add large fuzz penalty if EOF wasn't at end + # Normal case: search from `start` + return find_context_core(lines, context, start) + + +def peek_next_section(lines: List[str], index: int) -> Tuple[List[str], List[Chunk], int, bool]: + """ + Parses one section (context, -, + lines) of an Update block. + Returns: (context_lines, chunks_in_section, next_index, is_eof) + """ + context_lines: List[str] = [] + del_lines: List[str] = [] + ins_lines: List[str] = [] + chunks: List[Chunk] = [] + mode = "keep" # Start by expecting context lines + start_index = index + + while index < len(lines): + line = lines[index] + norm_line = _norm(line) + + # Check for section terminators + if norm_line.startswith( + ( + "@@", + "*** End Patch", + "*** Update File:", + "*** Delete File:", + "*** Add File:", + "*** End of File", # Special terminator + ) + ): + break + if norm_line == "***": # Legacy/alternative terminator? Handle just in case. + break + if norm_line.startswith("***"): # Invalid line + raise DiffError(f"Invalid patch line found in update section: {line}") + + index += 1 + last_mode = mode + + # Determine line type and strip prefix + if line.startswith("+"): + mode = "add" + line_content = line[1:] + elif line.startswith("-"): + mode = "delete" + line_content = line[1:] + elif line.startswith(" "): + mode = "keep" + line_content = line[1:] + elif line.strip() == "": # Treat blank lines in patch as context ' ' + mode = "keep" + line_content = "" # Keep it as a blank line + else: + # Assume lines without prefix are context if format is loose, + # but strict format requires ' '. Raise error for strictness. + raise DiffError(f"Invalid line prefix in update section: {line}") + + # If mode changes from add/delete back to keep, finalize the previous chunk + if mode == "keep" and last_mode != "keep": + if del_lines or ins_lines: + chunks.append( + Chunk( + # orig_index is relative to the start of the *context* block found + orig_index=len(context_lines) - len(del_lines), + del_lines=del_lines, + ins_lines=ins_lines, + ) + ) + del_lines, ins_lines = [], [] + + # Collect lines based on mode + if mode == "delete": + del_lines.append(line_content) + context_lines.append(line_content) # Deleted lines are part of the original context + elif mode == "add": + ins_lines.append(line_content) + elif mode == "keep": + context_lines.append(line_content) + + # Finalize any pending chunk at the end of the section + if del_lines or ins_lines: + chunks.append( + Chunk( + orig_index=len(context_lines) - len(del_lines), + del_lines=del_lines, + ins_lines=ins_lines, + ) + ) + + # Check for EOF marker + is_eof = False + if index < len(lines) and _norm(lines[index]) == "*** End of File": + index += 1 + is_eof = True + + if index == start_index and not is_eof: # Should not happen if patch is well-formed + raise DiffError("Empty patch section found.") + + return context_lines, chunks, index, is_eof + + +def identify_files_needed(text: str) -> List[str]: + """Extracts file paths from Update and Delete actions.""" + lines = text.splitlines() + paths = set() + for line in lines: + norm_line = _norm(line) + if norm_line.startswith("*** Update File: "): + paths.add(norm_line[len("*** Update File: ") :].strip()) + elif norm_line.startswith("*** Delete File: "): + paths.add(norm_line[len("*** Delete File: ") :].strip()) + return list(paths) + + +# --------------------------------------------------------------------------- # +# PatchCoder Class Implementation +# --------------------------------------------------------------------------- # +class PatchCoder(Coder): + """ + A coder that uses a custom patch format for code modifications, + inspired by the format described in tmp.gpt41edits.txt. + Applies patches using logic adapted from the reference apply_patch.py script. + """ + + edit_format = "patch" + gpt_prompts = PatchPrompts() + + def get_edits(self) -> List[EditResult]: + """ + Parses the LLM response content (containing the patch) into a list of + tuples, where each tuple contains the file path and the PatchAction object. + """ + content = self.partial_response_content + if not content or not content.strip(): + return [] + + # Check for patch sentinels + lines = content.splitlines() + if ( + len(lines) < 2 + or not _norm(lines[0]).startswith("*** Begin Patch") + # Allow flexible end, might be EOF or just end of stream + # or _norm(lines[-1]) != "*** End Patch" + ): + # Tolerate missing sentinels if content looks like a patch action + is_patch_like = any( + _norm(line).startswith( + ("@@", "*** Update File:", "*** Add File:", "*** Delete File:") + ) + for line in lines + ) + if not is_patch_like: + # If it doesn't even look like a patch, return empty + self.io.tool_warning("Response does not appear to be in patch format.") + return [] + # If it looks like a patch but lacks sentinels, try parsing anyway but warn. + self.io.tool_warning( + "Patch format warning: Missing '*** Begin Patch'/'*** End Patch' sentinels." + ) + start_index = 0 + else: + start_index = 1 # Skip "*** Begin Patch" + + # Identify files needed for context lookups during parsing + needed_paths = identify_files_needed(content) + current_files: Dict[str, str] = {} + for rel_path in needed_paths: + abs_path = self.abs_root_path(rel_path) + try: + # Use io.read_text to handle potential errors/encodings + file_content = self.io.read_text(abs_path) + if file_content is None: + raise DiffError( + f"File referenced in patch not found or could not be read: {rel_path}" + ) + current_files[rel_path] = file_content + except FileNotFoundError: + raise DiffError(f"File referenced in patch not found: {rel_path}") + except IOError as e: + raise DiffError(f"Error reading file {rel_path}: {e}") + + try: + # Parse the patch text using adapted logic + patch_obj = self._parse_patch_text(lines, start_index, current_files) + # Convert Patch object actions dict to a list of tuples (path, action) + # for compatibility with the base Coder's prepare_to_edit method. + results = [] + for path, action in patch_obj.actions.items(): + results.append((path, action)) + return results + except DiffError as e: + # Raise as ValueError for consistency with other coders' error handling + raise ValueError(f"Error parsing patch content: {e}") + except Exception as e: + # Catch unexpected errors during parsing + raise ValueError(f"Unexpected error parsing patch: {e}") + + def _parse_patch_text( + self, lines: List[str], start_index: int, current_files: Dict[str, str] + ) -> Patch: + """ + Parses patch content lines into a Patch object. + Adapted from the Parser class in apply_patch.py. + """ + patch = Patch() + index = start_index + fuzz_accumulator = 0 + + while index < len(lines): + line = lines[index] + norm_line = _norm(line) + + if norm_line == "*** End Patch": + index += 1 + break # Successfully reached end + + # ---------- UPDATE ---------- # + if norm_line.startswith("*** Update File: "): + path = norm_line[len("*** Update File: ") :].strip() + index += 1 + if not path: + raise DiffError("Update File action missing path.") + + # Optional move target + move_to = None + if index < len(lines) and _norm(lines[index]).startswith("*** Move to: "): + move_to = _norm(lines[index])[len("*** Move to: ") :].strip() + index += 1 + if not move_to: + raise DiffError("Move to action missing path.") + + if path not in current_files: + raise DiffError(f"Update File Error - missing file content for: {path}") + + file_content = current_files[path] + + existing_action = patch.actions.get(path) + if existing_action is not None: + # Merge additional UPDATE block into the existing one + if existing_action.type != ActionType.UPDATE: + raise DiffError(f"Conflicting actions for file: {path}") + + new_action, index, fuzz = self._parse_update_file_sections( + lines, index, file_content + ) + existing_action.chunks.extend(new_action.chunks) + + if move_to: + if existing_action.move_path and existing_action.move_path != move_to: + raise DiffError(f"Conflicting move targets for file: {path}") + existing_action.move_path = move_to + fuzz_accumulator += fuzz + else: + # First UPDATE block for this file + action, index, fuzz = self._parse_update_file_sections( + lines, index, file_content + ) + action.path = path + action.move_path = move_to + patch.actions[path] = action + fuzz_accumulator += fuzz + continue + + # ---------- DELETE ---------- # + elif norm_line.startswith("*** Delete File: "): + path = norm_line[len("*** Delete File: ") :].strip() + index += 1 + if not path: + raise DiffError("Delete File action missing path.") + existing_action = patch.actions.get(path) + if existing_action: + if existing_action.type == ActionType.DELETE: + # Duplicate delete – ignore the extra block + self.io.tool_warning(f"Duplicate delete action for file: {path} ignored.") + continue + else: + raise DiffError(f"Conflicting actions for file: {path}") + if path not in current_files: + raise DiffError( + f"Delete File Error - file not found: {path}" + ) # Check against known files + + patch.actions[path] = PatchAction(type=ActionType.DELETE, path=path) + continue + + # ---------- ADD ---------- # + elif norm_line.startswith("*** Add File: "): + path = norm_line[len("*** Add File: ") :].strip() + index += 1 + if not path: + raise DiffError("Add File action missing path.") + if path in patch.actions: + raise DiffError(f"Duplicate action for file: {path}") + # Check if file exists in the context provided (should not for Add). + # Note: We only have needed files, a full check requires FS access. + # if path in current_files: + # raise DiffError(f"Add File Error - file already exists: {path}") + + action, index = self._parse_add_file_content(lines, index) + action.path = path # Ensure path is set + patch.actions[path] = action + continue + + # If we are here, the line is unexpected + # Allow blank lines between actions + if not norm_line.strip(): + index += 1 + continue + + raise DiffError(f"Unknown or misplaced line while parsing patch: {line}") + + # Check if we consumed the whole input or stopped early + # Tolerate missing "*** End Patch" if we processed actions + # if index < len(lines) and _norm(lines[index-1]) != "*** End Patch": + # raise DiffError("Patch parsing finished unexpectedly before end of input.") + + patch.fuzz = fuzz_accumulator + return patch + + def _parse_update_file_sections( + self, lines: List[str], index: int, file_content: str + ) -> Tuple[PatchAction, int, int]: + """Parses all sections (@@, context, -, +) for a single Update File action.""" + action = PatchAction(type=ActionType.UPDATE, path="") # Path set by caller + orig_lines = file_content.splitlines() # Use splitlines for consistency + current_file_index = 0 # Track position in original file content + total_fuzz = 0 + + while index < len(lines): + norm_line = _norm(lines[index]) + # Check for terminators for *this* file update + if norm_line.startswith( + ( + "*** End Patch", + "*** Update File:", + "*** Delete File:", + "*** Add File:", + ) + ): + break # End of this file's update section + + # Handle @@ scope lines (optional) + scope_lines = [] + while index < len(lines) and _norm(lines[index]).startswith("@@"): + scope_line_content = lines[index][len("@@") :].strip() + if scope_line_content: # Ignore empty @@ lines? + scope_lines.append(scope_line_content) + index += 1 + + # Find the scope in the original file if specified + if scope_lines: + # Simple scope finding: search from current position + # A more robust finder could handle nested scopes like the reference @@ @@ + found_scope = False + temp_index = current_file_index + while temp_index < len(orig_lines): + # Check if all scope lines match sequentially from temp_index + match = True + for i, scope in enumerate(scope_lines): + if ( + temp_index + i >= len(orig_lines) + or _norm(orig_lines[temp_index + i]).strip() != scope + ): + match = False + break + if match: + current_file_index = temp_index + len(scope_lines) + found_scope = True + break + temp_index += 1 + + if not found_scope: + # Try fuzzy scope matching (strip whitespace) + temp_index = current_file_index + while temp_index < len(orig_lines): + match = True + for i, scope in enumerate(scope_lines): + if ( + temp_index + i >= len(orig_lines) + or _norm(orig_lines[temp_index + i]).strip() != scope.strip() + ): + match = False + break + if match: + current_file_index = temp_index + len(scope_lines) + found_scope = True + total_fuzz += 1 # Add fuzz for scope match difference + break + temp_index += 1 + + if not found_scope: + scope_txt = "\n".join(scope_lines) + raise DiffError(f"Could not find scope context:\n{scope_txt}") + + # Peek and parse the next context/change section + context_block, chunks_in_section, next_index, is_eof = peek_next_section(lines, index) + + # Find where this context block appears in the original file + found_index, fuzz = find_context(orig_lines, context_block, current_file_index, is_eof) + total_fuzz += fuzz + + if found_index == -1: + ctx_txt = "\n".join(context_block) + marker = "*** End of File" if is_eof else "" + raise DiffError( + f"Could not find patch context {marker} starting near line" + f" {current_file_index}:\n{ctx_txt}" + ) + + # Adjust chunk original indices to be absolute within the file + for chunk in chunks_in_section: + # chunk.orig_index from peek is relative to context_block start + # We need it relative to the file start + chunk.orig_index += found_index + action.chunks.append(chunk) + + # Advance file index past the matched context block + current_file_index = found_index + len(context_block) + # Advance line index past the processed section in the patch + index = next_index + + return action, index, total_fuzz + + def _parse_add_file_content(self, lines: List[str], index: int) -> Tuple[PatchAction, int]: + """Parses the content (+) lines for an Add File action.""" + added_lines: List[str] = [] + while index < len(lines): + line = lines[index] + norm_line = _norm(line) + # Stop if we hit another action or end marker + if norm_line.startswith( + ( + "*** End Patch", + "*** Update File:", + "*** Delete File:", + "*** Add File:", + ) + ): + break + + # Expect lines to start with '+' + if not line.startswith("+"): + # Tolerate blank lines? Or require '+'? Reference implies '+' required. + if norm_line.strip() == "": + # Treat blank line as adding a blank line + added_lines.append("") + else: + raise DiffError(f"Invalid Add File line (missing '+'): {line}") + else: + added_lines.append(line[1:]) # Strip leading '+' + + index += 1 + + action = PatchAction(type=ActionType.ADD, path="", new_content="\n".join(added_lines)) + return action, index + + def apply_edits(self, edits: List[PatchAction]): + """ + Applies the parsed PatchActions to the corresponding files. + """ + if not edits: + return + + # Group edits by original path? Not strictly needed if processed sequentially. + + # Edits are now List[Tuple[str, PatchAction]] + for _path_tuple_element, action in edits: + # action is the PatchAction object + # action.path is the canonical path within the action logic + full_path = self.abs_root_path(action.path) + path_obj = pathlib.Path(full_path) + + try: + if action.type == ActionType.ADD: + # Check existence *before* writing + if path_obj.exists(): + raise DiffError(f"ADD Error: File already exists: {action.path}") + if action.new_content is None: + # Parser should ensure this doesn't happen + raise DiffError(f"ADD change for {action.path} has no content") + + self.io.tool_output(f"Adding {action.path}") + path_obj.parent.mkdir(parents=True, exist_ok=True) + # Ensure single trailing newline, matching reference behavior + content_to_write = action.new_content + if not content_to_write.endswith("\n"): + content_to_write += "\n" + self.io.write_text(full_path, content_to_write) + + elif action.type == ActionType.DELETE: + self.io.tool_output(f"Deleting {action.path}") + if not path_obj.exists(): + self.io.tool_warning( + f"DELETE Warning: File not found, skipping: {action.path}" + ) + else: + path_obj.unlink() + + elif action.type == ActionType.UPDATE: + if not path_obj.exists(): + raise DiffError(f"UPDATE Error: File does not exist: {action.path}") + + current_content = self.io.read_text(full_path) + if current_content is None: + # Should have been caught during parsing if file was needed + raise DiffError(f"Could not read file for UPDATE: {action.path}") + + # Apply the update logic using the parsed chunks + new_content = self._apply_update(current_content, action, action.path) + + target_full_path = ( + self.abs_root_path(action.move_path) if action.move_path else full_path + ) + target_path_obj = pathlib.Path(target_full_path) + + if action.move_path: + self.io.tool_output( + f"Updating and moving {action.path} to {action.move_path}" + ) + # Check if target exists before overwriting/moving + if target_path_obj.exists() and full_path != target_full_path: + self.io.tool_warning( + "UPDATE Warning: Target file for move already exists, overwriting:" + f" {action.move_path}" + ) + else: + self.io.tool_output(f"Updating {action.path}") + + # Ensure parent directory exists for target + target_path_obj.parent.mkdir(parents=True, exist_ok=True) + self.io.write_text(target_full_path, new_content) + + # Remove original file *after* successful write to new location if moved + if action.move_path and full_path != target_full_path: + path_obj.unlink() + + else: + # Should not happen + raise DiffError(f"Unknown action type encountered: {action.type}") + + except (DiffError, FileNotFoundError, IOError, OSError) as e: + # Raise a ValueError to signal failure, consistent with other coders. + raise ValueError(f"Error applying action '{action.type}' to {action.path}: {e}") + except Exception as e: + # Catch unexpected errors during application + raise ValueError( + f"Unexpected error applying action '{action.type}' to {action.path}: {e}" + ) + + def _apply_update(self, text: str, action: PatchAction, path: str) -> str: + """ + Applies UPDATE chunks to the given text content. + Adapted from _get_updated_file in apply_patch.py. + """ + if action.type is not ActionType.UPDATE: + # Should not be called otherwise, but check for safety + raise DiffError("_apply_update called with non-update action") + + orig_lines = text.splitlines() # Use splitlines to handle endings consistently + dest_lines: List[str] = [] + current_orig_line_idx = 0 # Tracks index in orig_lines processed so far + + # Sort chunks by their original index to apply them sequentially + sorted_chunks = sorted(action.chunks, key=lambda c: c.orig_index) + + for chunk in sorted_chunks: + # chunk.orig_index is the absolute line number where the change starts + # (where the first deleted line was, or where inserted lines go if no deletes) + chunk_start_index = chunk.orig_index + + if chunk_start_index < current_orig_line_idx: + # This indicates overlapping chunks or incorrect indices from parsing + raise DiffError( + f"{path}: Overlapping or out-of-order chunk detected." + f" Current index {current_orig_line_idx}, chunk starts at {chunk_start_index}." + ) + + # Add lines from original file between the last chunk and this one + dest_lines.extend(orig_lines[current_orig_line_idx:chunk_start_index]) + + # Verify that the lines to be deleted actually match the original file content + # (The parser should have used find_context, but double-check here) + num_del = len(chunk.del_lines) + actual_deleted_lines = orig_lines[chunk_start_index : chunk_start_index + num_del] + + # Use the same normalization as find_context_core for comparison robustness + norm_chunk_del = [_norm(s).strip() for s in chunk.del_lines] + norm_actual_del = [_norm(s).strip() for s in actual_deleted_lines] + + if norm_chunk_del != norm_actual_del: + # This indicates the context matching failed or the file changed since parsing + # Provide detailed error message + expected_str = "\n".join(f"- {s}" for s in chunk.del_lines) + actual_str = "\n".join(f" {s}" for s in actual_deleted_lines) + raise DiffError( + f"{path}: Mismatch applying patch near line {chunk_start_index + 1}.\n" + f"Expected lines to remove:\n{expected_str}\n" + f"Found lines in file:\n{actual_str}" + ) + + # Add the inserted lines from the chunk + dest_lines.extend(chunk.ins_lines) + + # Advance the original line index past the lines processed (deleted lines) + current_orig_line_idx = chunk_start_index + num_del + + # Add any remaining lines from the original file after the last chunk + dest_lines.extend(orig_lines[current_orig_line_idx:]) + + # Join lines and ensure a single trailing newline + result = "\n".join(dest_lines) + if result or orig_lines: # Add newline unless result is empty and original was empty + result += "\n" + return result diff --git a/aider/coders/patch_prompts.py b/aider/coders/patch_prompts.py new file mode 100644 index 000000000..830a04f2b --- /dev/null +++ b/aider/coders/patch_prompts.py @@ -0,0 +1,161 @@ +# flake8: noqa: E501 + +from .base_prompts import CoderPrompts +from .editblock_prompts import EditBlockPrompts + + +class PatchPrompts(EditBlockPrompts): + # --------------------------------------------------------------------- # + # SYSTEM PROMPT + # --------------------------------------------------------------------- # + main_system = """Act as an expert software developer. +Always use best practices when coding. +Respect and use existing conventions, libraries, etc that are already present in the code base. +{lazy_prompt} +Take requests for changes to the supplied code. +If the request is ambiguous, ask questions. + +Always reply to the user in {language}. + +Once you understand the request you MUST: + +1. Decide if you need to propose edits to any files that haven't been added to the chat. You can create new files without asking! + + • If you need to propose edits to existing files not already added to the chat, you *MUST* tell the user their full path names and ask them to *add the files to the chat*. + • End your reply and wait for their approval. + • You can keep asking if you then decide you need to edit more files. + +2. Think step‑by‑step and explain the needed changes in a few short sentences. + +3. Describe the changes using the V4A diff format, enclosed within `*** Begin Patch` and `*** End Patch` markers. + +IMPORTANT: Each file MUST appear only once in the patch. +Consolidate **all** edits for a given file into a single `*** [ACTION] File:` block. +{shell_cmd_prompt} +""" + + # --------------------------------------------------------------------- # + # EXAMPLE MESSAGES + # --------------------------------------------------------------------- # + example_messages = [ + # --- Example 1 ---------------------------------------------------- # + dict( + role="user", + content="Change get_factorial() to use math.factorial", + ), + dict( + role="assistant", + content="""To make this change we need to modify `mathweb/flask/app.py`: + +1. Import the `math` module. +2. Replace the custom recursive `factorial()` helper with `math.factorial`. +3. Update `get_factorial()` to call `math.factorial`. + +Here is the patch: + +*** Begin Patch +*** Update File: mathweb/flask/app.py +@@ +-from flask import Flask +- +-from flask import Flask ++from flask import Flask ++import math +@@ +-def factorial(n): +- "compute factorial" +- +- if n == 0: +- return 1 +- else: +- return n * factorial(n-1) ++def factorial(n): ++ "compute factorial" ++ ++ return math.factorial(n) +@@ def get_factorial(self, n): +- return str(factorial(n)) ++ return str(math.factorial(n)) +*** End Patch +""", + ), + # --- Example 2 ---------------------------------------------------- # + dict( + role="user", + content="Refactor hello() into its own file.", + ), + dict( + role="assistant", + content="""To make this change we need to modify `main.py` and create a new file `hello.py`: + +1. Create `hello.py` with the `hello()` function. +2. Remove `hello()` from `main.py` and import it instead. + +Here is the patch: + +*** Begin Patch +*** Add File: hello.py ++# A simple function ++def hello(): ++ "print a greeting" ++ ++ print("hello") +*** Update File: main.py +@@ +-def hello(): +- "print a greeting" +- +- print("hello") ++from hello import hello +*** End Patch +""", + ), + ] + + # --------------------------------------------------------------------- # + # SYSTEM REMINDER + # --------------------------------------------------------------------- # + system_reminder = """# V4A Diff Format Rules: + +Your entire response containing the patch MUST start with `*** Begin Patch` on a line by itself. +Your entire response containing the patch MUST end with `*** End Patch` on a line by itself. + +Use the *FULL* file path, as shown to you by the user. +{quad_backtick_reminder} + +For each file you need to modify, start with a marker line: + + *** [ACTION] File: [path/to/file] + +Where `[ACTION]` is one of `Add`, `Update`, or `Delete`. + +⇨ **Each file MUST appear only once in the patch.** + Consolidate all changes for that file into the same block. + If you are moving code within a file, include both the deletions and the + insertions as separate hunks inside this single `*** Update File:` block + (do *not* open a second block for the same file). + +For `Update` actions, describe each snippet of code that needs to be changed using the following format: +1. Context lines: Include 3 lines of context *before* the change. These lines MUST start with a single space ` `. +2. Lines to remove: Precede each line to be removed with a minus sign `-`. +3. Lines to add: Precede each line to be added with a plus sign `+`. +4. Context lines: Include 3 lines of context *after* the change. These lines MUST start with a single space ` `. + +Context lines MUST exactly match the existing file content, character for character, including indentation. +If a change is near the beginning or end of the file, include fewer than 3 context lines as appropriate. +If 3 lines of context is insufficient to uniquely identify the snippet, use `@@ [CLASS_OR_FUNCTION_NAME]` markers on their own lines *before* the context lines to specify the scope. You can use multiple `@@` markers if needed. +Do not include line numbers. + +Only create patches for files that the user has added to the chat! + +When moving code *within* a single file, keep everything inside one +`*** Update File:` block. Provide one hunk that deletes the code from its +original location and another hunk that inserts it at the new location. + +For `Add` actions, use the `*** Add File: [path/to/new/file]` marker, followed by the lines of the new file, each preceded by a plus sign `+`. + +For `Delete` actions, use the `*** Delete File: [path/to/file]` marker. No other lines are needed for the deletion. + +{rename_with_shell}{go_ahead_tip}{lazy_prompt}ONLY EVER RETURN CODE IN THE SPECIFIED V4A DIFF FORMAT! +{shell_cmd_reminder} +""" diff --git a/aider/coders/search_replace.py b/aider/coders/search_replace.py index a72a7845b..f31e97799 100755 --- a/aider/coders/search_replace.py +++ b/aider/coders/search_replace.py @@ -235,20 +235,6 @@ Left Left """ -""" -ri = RelativeIndenter([example]) -dump(example) - -rel_example = ri.make_relative(example) -dump(repr(rel_example)) - -abs_example = ri.make_absolute(rel_example) -dump(abs_example) - - -sys.exit() -""" - def relative_indent(texts): ri = RelativeIndenter(texts) @@ -349,7 +335,7 @@ def lines_to_chars(lines, mapping): return new_text -def dmp_lines_apply(texts, remap=True): +def dmp_lines_apply(texts): debug = False # debug = True @@ -655,8 +641,6 @@ def proc(dname): (dmp_lines_apply, all_preprocs), ] - _strategies = editblock_strategies # noqa: F841 - short_names = dict( search_and_replace="sr", git_cherry_pick_osr_onto_o="cp_o", diff --git a/aider/coders/udiff_coder.py b/aider/coders/udiff_coder.py index b0955872b..fff6971b7 100644 --- a/aider/coders/udiff_coder.py +++ b/aider/coders/udiff_coder.py @@ -45,6 +45,7 @@ other_hunks_applied = ( class UnifiedDiffCoder(Coder): """A coder that uses unified diff format for code modifications.""" + edit_format = "udiff" gpt_prompts = UnifiedDiffPrompts() @@ -344,7 +345,16 @@ def process_fenced_block(lines, start_line_num): if block[0].startswith("--- ") and block[1].startswith("+++ "): # Extract the file path, considering that it might contain spaces - fname = block[1][4:].strip() + a_fname = block[0][4:].strip() + b_fname = block[1][4:].strip() + + # Check if standard git diff prefixes are present (or /dev/null) and strip them + if (a_fname.startswith("a/") or a_fname == "/dev/null") and b_fname.startswith("b/"): + fname = b_fname[2:] + else: + # Otherwise, assume the path is as intended + fname = b_fname + block = block[2:] else: fname = None diff --git a/aider/coders/udiff_simple.py b/aider/coders/udiff_simple.py new file mode 100644 index 000000000..9cc51991d --- /dev/null +++ b/aider/coders/udiff_simple.py @@ -0,0 +1,14 @@ +from .udiff_coder import UnifiedDiffCoder +from .udiff_simple_prompts import UnifiedDiffSimplePrompts + + +class UnifiedDiffSimpleCoder(UnifiedDiffCoder): + """ + A coder that uses unified diff format for code modifications. + This variant uses a simpler prompt that doesn't mention specific + diff rules like using `@@ ... @@` lines or avoiding line numbers. + """ + + edit_format = "udiff-simple" + + gpt_prompts = UnifiedDiffSimplePrompts() diff --git a/aider/coders/udiff_simple_prompts.py b/aider/coders/udiff_simple_prompts.py new file mode 100644 index 000000000..706bb1026 --- /dev/null +++ b/aider/coders/udiff_simple_prompts.py @@ -0,0 +1,25 @@ +from .udiff_prompts import UnifiedDiffPrompts + + +class UnifiedDiffSimplePrompts(UnifiedDiffPrompts): + """ + Prompts for the UnifiedDiffSimpleCoder. + Inherits from UnifiedDiffPrompts and can override specific prompts + if a simpler wording is desired for this edit format. + """ + + example_messages = [] + + system_reminder = """# File editing rules: + +Return edits similar to unified diffs that `diff -U0` would produce. + +The user's patch tool needs CORRECT patches that apply cleanly against the current contents of the file! +Think carefully and make sure you include and mark all lines that need to be removed or changed as `-` lines. +Make sure you mark all new or modified lines with `+`. +Don't leave out any lines or the diff patch won't apply correctly. + +To make a new file, show a diff from `--- /dev/null` to `+++ path/to/new/file.ext`. + +{lazy_prompt} +""" diff --git a/aider/commands.py b/aider/commands.py index 546541f81..52731f927 100644 --- a/aider/commands.py +++ b/aider/commands.py @@ -17,6 +17,7 @@ from aider import models, prompts, voice from aider.editor import pipe_editor from aider.format_settings import format_settings from aider.help import Help, install_help_extra +from aider.io import CommandCompletionException from aider.llm import litellm from aider.repo import ANY_GIT_ERROR from aider.run_cmd import run_cmd @@ -27,8 +28,9 @@ from .dump import dump # noqa: F401 class SwitchCoder(Exception): - def __init__(self, **kwargs): + def __init__(self, placeholder=None, **kwargs): self.kwargs = kwargs + self.placeholder = placeholder class Commands: @@ -59,6 +61,7 @@ class Commands: parser=None, verbose=False, editor=None, + original_read_only_fnames=None, ): self.io = io self.coder = coder @@ -77,11 +80,52 @@ class Commands: self.help = None self.editor = editor + # Store the original read-only filenames provided via args.read + self.original_read_only_fnames = set(original_read_only_fnames or []) + def cmd_model(self, args): - "Switch to a new LLM" + "Switch the Main Model to a new LLM" model_name = args.strip() - model = models.Model(model_name, weak_model=self.coder.main_model.weak_model.name) + model = models.Model( + model_name, + editor_model=self.coder.main_model.editor_model.name, + weak_model=self.coder.main_model.weak_model.name, + ) + models.sanity_check_models(self.io, model) + + # Check if the current edit format is the default for the old model + old_model_edit_format = self.coder.main_model.edit_format + current_edit_format = self.coder.edit_format + + new_edit_format = current_edit_format + if current_edit_format == old_model_edit_format: + # If the user was using the old model's default, switch to the new model's default + new_edit_format = model.edit_format + + raise SwitchCoder(main_model=model, edit_format=new_edit_format) + + def cmd_editor_model(self, args): + "Switch the Editor Model to a new LLM" + + model_name = args.strip() + model = models.Model( + self.coder.main_model.name, + editor_model=model_name, + weak_model=self.coder.main_model.weak_model.name, + ) + models.sanity_check_models(self.io, model) + raise SwitchCoder(main_model=model) + + def cmd_weak_model(self, args): + "Switch the Weak Model to a new LLM" + + model_name = args.strip() + model = models.Model( + self.coder.main_model.name, + editor_model=self.coder.main_model.editor_model.name, + weak_model=model_name, + ) models.sanity_check_models(self.io, model) raise SwitchCoder(main_model=model) @@ -114,6 +158,10 @@ class Commands: " them." ), ), + ( + "context", + "Automatically identify which files will need to be edited.", + ), ] ) @@ -355,7 +403,21 @@ class Commands: def _drop_all_files(self): self.coder.abs_fnames = set() - self.coder.abs_read_only_fnames = set() + + # When dropping all files, keep those that were originally provided via args.read + if self.original_read_only_fnames: + # Keep only the original read-only files + to_keep = set() + for abs_fname in self.coder.abs_read_only_fnames: + rel_fname = self.coder.get_rel_fname(abs_fname) + if ( + abs_fname in self.original_read_only_fnames + or rel_fname in self.original_read_only_fnames + ): + to_keep.add(abs_fname) + self.coder.abs_read_only_fnames = to_keep + else: + self.coder.abs_read_only_fnames = set() def _clear_chat_history(self): self.coder.done_messages = [] @@ -822,7 +884,12 @@ class Commands: "Remove files from the chat session to free up context space" if not args.strip(): - self.io.tool_output("Dropping all files from the chat session.") + if self.original_read_only_fnames: + self.io.tool_output( + "Dropping all files from the chat session except originally read-only files." + ) + else: + self.io.tool_output("Dropping all files from the chat session.") self._drop_all_files() return @@ -947,9 +1014,15 @@ class Commands: dict(role="assistant", content="Ok."), ] - if add and exit_status != 0: + if add_on_nonzero_exit and exit_status != 0: + # Return the formatted output message for test failures + return msg + elif add and exit_status != 0: self.io.placeholder = "What's wrong? Fix" + # Return None if output wasn't added or command succeeded + return None + def cmd_exit(self, args): "Exit the application" self.coder.event("exit", reason="/exit") @@ -1067,6 +1140,18 @@ class Commands: def cmd_batch(self, args): """Iteratively perform the change on files in batches that fit to context and output limits""" return self._generic_chat_command(args, "batch") + def completions_ask(self): + raise CommandCompletionException() + + def completions_code(self): + raise CommandCompletionException() + + def completions_architect(self): + raise CommandCompletionException() + + def completions_context(self): + raise CommandCompletionException() + def cmd_ask(self, args): """Ask questions about the code base without editing any files. If no prompt provided, switches to ask mode.""" # noqa return self._generic_chat_command(args, "ask") @@ -1079,7 +1164,11 @@ class Commands: """Enter architect/editor mode using 2 different models. If no prompt provided, switches to architect/editor mode.""" # noqa return self._generic_chat_command(args, "architect") - def _generic_chat_command(self, args, edit_format): + def cmd_context(self, args): + """Enter context mode to see surrounding code context. If no prompt provided, switches to context mode.""" # noqa + return self._generic_chat_command(args, "context", placeholder=args.strip() or None) + + def _generic_chat_command(self, args, edit_format, placeholder=None): if not args.strip(): # Switch to the corresponding chat mode if no args provided return self.cmd_chat_mode(edit_format) @@ -1096,11 +1185,13 @@ class Commands: user_msg = args coder.run(user_msg) + # Use the provided placeholder if any raise SwitchCoder( edit_format=self.coder.edit_format, summarize_from_coder=False, from_coder=coder, show_announcements=False, + placeholder=placeholder, ) def get_help_md(self): @@ -1413,6 +1504,62 @@ class Commands: if user_input.strip(): self.io.set_placeholder(user_input.rstrip()) + def cmd_edit(self, args=""): + "Alias for /editor: Open an editor to write a prompt" + return self.cmd_editor(args) + + def cmd_think_tokens(self, args): + "Set the thinking token budget (supports formats like 8096, 8k, 10.5k, 0.5M)" + model = self.coder.main_model + + if not args.strip(): + # Display current value if no args are provided + formatted_budget = model.get_thinking_tokens() + if formatted_budget is None: + self.io.tool_output("Thinking tokens are not currently set.") + else: + budget = model.get_raw_thinking_tokens() + self.io.tool_output( + f"Current thinking token budget: {budget:,} tokens ({formatted_budget})." + ) + return + + value = args.strip() + model.set_thinking_tokens(value) + + formatted_budget = model.get_thinking_tokens() + budget = model.get_raw_thinking_tokens() + + self.io.tool_output(f"Set thinking token budget to {budget:,} tokens ({formatted_budget}).") + self.io.tool_output() + + # Output announcements + announcements = "\n".join(self.coder.get_announcements()) + self.io.tool_output(announcements) + + def cmd_reasoning_effort(self, args): + "Set the reasoning effort level (values: number or low/medium/high depending on model)" + model = self.coder.main_model + + if not args.strip(): + # Display current value if no args are provided + reasoning_value = model.get_reasoning_effort() + if reasoning_value is None: + self.io.tool_output("Reasoning effort is not currently set.") + else: + self.io.tool_output(f"Current reasoning effort: {reasoning_value}") + return + + value = args.strip() + model.set_reasoning_effort(value) + reasoning_value = model.get_reasoning_effort() + self.io.tool_output(f"Set reasoning effort to {reasoning_value}") + self.io.tool_output() + + # Output announcements + announcements = "\n".join(self.coder.get_announcements()) + self.io.tool_output(announcements) + def cmd_copy_context(self, args=None): """Copy the current chat context as markdown, suitable to paste into a web UI""" diff --git a/aider/deprecated.py b/aider/deprecated.py new file mode 100644 index 000000000..e40924f5c --- /dev/null +++ b/aider/deprecated.py @@ -0,0 +1,126 @@ +def add_deprecated_model_args(parser, group): + """Add deprecated model shortcut arguments to the argparse parser.""" + opus_model = "claude-3-opus-20240229" + group.add_argument( + "--opus", + action="store_true", + help=f"Use {opus_model} model for the main chat (deprecated, use --model)", + default=False, + ) + sonnet_model = "anthropic/claude-3-7-sonnet-20250219" + group.add_argument( + "--sonnet", + action="store_true", + help=f"Use {sonnet_model} model for the main chat (deprecated, use --model)", + default=False, + ) + haiku_model = "claude-3-5-haiku-20241022" + group.add_argument( + "--haiku", + action="store_true", + help=f"Use {haiku_model} model for the main chat (deprecated, use --model)", + default=False, + ) + gpt_4_model = "gpt-4-0613" + group.add_argument( + "--4", + "-4", + action="store_true", + help=f"Use {gpt_4_model} model for the main chat (deprecated, use --model)", + default=False, + ) + gpt_4o_model = "gpt-4o" + group.add_argument( + "--4o", + action="store_true", + help=f"Use {gpt_4o_model} model for the main chat (deprecated, use --model)", + default=False, + ) + gpt_4o_mini_model = "gpt-4o-mini" + group.add_argument( + "--mini", + action="store_true", + help=f"Use {gpt_4o_mini_model} model for the main chat (deprecated, use --model)", + default=False, + ) + gpt_4_turbo_model = "gpt-4-1106-preview" + group.add_argument( + "--4-turbo", + action="store_true", + help=f"Use {gpt_4_turbo_model} model for the main chat (deprecated, use --model)", + default=False, + ) + gpt_3_model_name = "gpt-3.5-turbo" + group.add_argument( + "--35turbo", + "--35-turbo", + "--3", + "-3", + action="store_true", + help=f"Use {gpt_3_model_name} model for the main chat (deprecated, use --model)", + default=False, + ) + deepseek_model = "deepseek/deepseek-chat" + group.add_argument( + "--deepseek", + action="store_true", + help=f"Use {deepseek_model} model for the main chat (deprecated, use --model)", + default=False, + ) + o1_mini_model = "o1-mini" + group.add_argument( + "--o1-mini", + action="store_true", + help=f"Use {o1_mini_model} model for the main chat (deprecated, use --model)", + default=False, + ) + o1_preview_model = "o1-preview" + group.add_argument( + "--o1-preview", + action="store_true", + help=f"Use {o1_preview_model} model for the main chat (deprecated, use --model)", + default=False, + ) + + +def handle_deprecated_model_args(args, io): + """Handle deprecated model shortcut arguments and provide appropriate warnings.""" + # Define model mapping + model_map = { + "opus": "claude-3-opus-20240229", + "sonnet": "anthropic/claude-3-7-sonnet-20250219", + "haiku": "claude-3-5-haiku-20241022", + "4": "gpt-4-0613", + "4o": "gpt-4o", + "mini": "gpt-4o-mini", + "4_turbo": "gpt-4-1106-preview", + "35turbo": "gpt-3.5-turbo", + "deepseek": "deepseek/deepseek-chat", + "o1_mini": "o1-mini", + "o1_preview": "o1-preview", + } + + # Check if any deprecated args are used + for arg_name, model_name in model_map.items(): + arg_name_clean = arg_name.replace("-", "_") + if hasattr(args, arg_name_clean) and getattr(args, arg_name_clean): + # Find preferred name to display in warning + from aider.models import MODEL_ALIASES + + display_name = model_name + # Check if there's a shorter alias for this model + for alias, full_name in MODEL_ALIASES.items(): + if full_name == model_name: + display_name = alias + break + + # Show the warning + io.tool_warning( + f"The --{arg_name.replace('_', '-')} flag is deprecated and will be removed in a" + f" future version. Please use --model {display_name} instead." + ) + + # Set the model + if not args.model: + args.model = model_name + break diff --git a/aider/exceptions.py b/aider/exceptions.py index 2fc810430..a81a058e0 100644 --- a/aider/exceptions.py +++ b/aider/exceptions.py @@ -83,4 +83,25 @@ class LiteLLMExceptions: ) if "boto3" in str(ex): return ExInfo("APIConnectionError", False, "You need to: pip install boto3") + if "OpenrouterException" in str(ex) and "'choices'" in str(ex): + return ExInfo( + "APIConnectionError", + True, + ( + "OpenRouter or the upstream API provider is down, overloaded or rate" + " limiting your requests." + ), + ) + + # Check for specific non-retryable APIError cases like insufficient credits + if ex.__class__ is litellm.APIError: + err_str = str(ex).lower() + if "insufficient credits" in err_str and '"code":402' in err_str: + return ExInfo( + "APIError", + False, + "Insufficient credits with the API provider. Please add credits.", + ) + # Fall through to default APIError handling if not the specific credits error + return self.exceptions.get(ex.__class__, ExInfo(None, None, None)) diff --git a/aider/help_pats.py b/aider/help_pats.py index 5d2b14865..546517d66 100644 --- a/aider/help_pats.py +++ b/aider/help_pats.py @@ -10,4 +10,10 @@ exclude_website_pats = [ "docs/unified-diffs.md", "docs/leaderboards/index.md", "assets/**", + ".jekyll-metadata", + "Gemfile.lock", + "Gemfile", + "_config.yml", + "**/OLD/**", + "OLD/**", ] diff --git a/aider/io.py b/aider/io.py index 5ab11f0d6..08c03ef53 100644 --- a/aider/io.py +++ b/aider/io.py @@ -1,7 +1,9 @@ import base64 import functools import os +import shutil import signal +import subprocess import time import webbrowser from collections import defaultdict @@ -16,6 +18,7 @@ from prompt_toolkit.enums import EditingMode from prompt_toolkit.filters import Condition, is_searching from prompt_toolkit.history import FileHistory from prompt_toolkit.key_binding import KeyBindings +from prompt_toolkit.key_binding.vi_state import InputMode from prompt_toolkit.keys import Keys from prompt_toolkit.lexers import PygmentsLexer from prompt_toolkit.output.vt100 import is_dumb_terminal @@ -23,6 +26,7 @@ from prompt_toolkit.shortcuts import CompleteStyle, PromptSession from prompt_toolkit.styles import Style from pygments.lexers import MarkdownLexer, guess_lexer_for_filename from pygments.token import Token +from rich.color import ColorParseError from rich.columns import Columns from rich.console import Console from rich.markdown import Markdown @@ -32,8 +36,23 @@ from rich.text import Text from aider.mdstream import MarkdownStream from .dump import dump # noqa: F401 +from .editor import pipe_editor from .utils import is_image_file +# Constants +NOTIFICATION_MESSAGE = "Aider is waiting for your input" + + +def ensure_hash_prefix(color): + """Ensure hex color values have a # prefix.""" + if not color: + return color + if isinstance(color, str) and color.strip() and not color.startswith("#"): + # Check if it's a valid hex color (3 or 6 hex digits) + if all(c in "0123456789ABCDEFabcdef" for c in color) and len(color) in (3, 6): + return f"#{color}" + return color + def restore_multiline(func): """Decorator to restore multiline mode after function execution""" @@ -52,6 +71,13 @@ def restore_multiline(func): return wrapper +class CommandCompletionException(Exception): + """Raised when a command should use the normal autocompleter instead of + command-specific completion.""" + + pass + + @dataclass class ConfirmGroup: preference: str = None @@ -170,14 +196,23 @@ class AutoCompleter(Completer): return if text[0] == "/": - yield from self.get_command_completions(document, complete_event, text, words) - return + try: + yield from self.get_command_completions(document, complete_event, text, words) + return + except CommandCompletionException: + # Fall through to normal completion + pass candidates = self.words candidates.update(set(self.fname_to_rel_fnames)) candidates = [word if type(word) is tuple else (word, word) for word in candidates] last_word = words[-1] + + # Only provide completions if the user has typed at least 3 characters + if len(last_word) < 3: + return + completions = [] for word_match, word_insert in candidates: if word_match.lower().startswith(last_word.lower()): @@ -196,6 +231,8 @@ class InputOutput: num_error_outputs = 0 num_user_asks = 0 clipboard_watcher = None + bell_on_next_input = False + notifications_command = None def __init__( self, @@ -224,25 +261,40 @@ class InputOutput: file_watcher=None, multiline_mode=False, root=".", + notifications=False, + notifications_command=None, ): self.placeholder = None self.interrupted = False self.never_prompts = set() self.editingmode = editingmode self.multiline_mode = multiline_mode + self.bell_on_next_input = False + self.notifications = notifications + if notifications and notifications_command is None: + self.notifications_command = self.get_default_notification_command() + else: + self.notifications_command = notifications_command + no_color = os.environ.get("NO_COLOR") if no_color is not None and no_color != "": pretty = False - self.user_input_color = user_input_color if pretty else None - self.tool_output_color = tool_output_color if pretty else None - self.tool_error_color = tool_error_color if pretty else None - self.tool_warning_color = tool_warning_color if pretty else None - self.assistant_output_color = assistant_output_color - self.completion_menu_color = completion_menu_color if pretty else None - self.completion_menu_bg_color = completion_menu_bg_color if pretty else None - self.completion_menu_current_color = completion_menu_current_color if pretty else None - self.completion_menu_current_bg_color = completion_menu_current_bg_color if pretty else None + self.user_input_color = ensure_hash_prefix(user_input_color) if pretty else None + self.tool_output_color = ensure_hash_prefix(tool_output_color) if pretty else None + self.tool_error_color = ensure_hash_prefix(tool_error_color) if pretty else None + self.tool_warning_color = ensure_hash_prefix(tool_warning_color) if pretty else None + self.assistant_output_color = ensure_hash_prefix(assistant_output_color) + self.completion_menu_color = ensure_hash_prefix(completion_menu_color) if pretty else None + self.completion_menu_bg_color = ( + ensure_hash_prefix(completion_menu_bg_color) if pretty else None + ) + self.completion_menu_current_color = ( + ensure_hash_prefix(completion_menu_current_color) if pretty else None + ) + self.completion_menu_current_bg_color = ( + ensure_hash_prefix(completion_menu_current_bg_color) if pretty else None + ) self.code_theme = code_theme @@ -310,6 +362,35 @@ class InputOutput: self.file_watcher = file_watcher self.root = root + # Validate color settings after console is initialized + self._validate_color_settings() + + def _validate_color_settings(self): + """Validate configured color strings and reset invalid ones.""" + color_attributes = [ + "user_input_color", + "tool_output_color", + "tool_error_color", + "tool_warning_color", + "assistant_output_color", + "completion_menu_color", + "completion_menu_bg_color", + "completion_menu_current_color", + "completion_menu_current_bg_color", + ] + for attr_name in color_attributes: + color_value = getattr(self, attr_name, None) + if color_value: + try: + # Try creating a style to validate the color + RichStyle(color=color_value) + except ColorParseError as e: + self.console.print( + "[bold red]Warning:[/bold red] Invalid configuration for" + f" {attr_name}: '{color_value}'. {e}. Disabling this color." + ) + setattr(self, attr_name, None) # Reset invalid color to None + def _get_style(self): style_dict = {} if not self.pretty: @@ -335,9 +416,9 @@ class InputOutput: # Conditionally add 'completion-menu.completion.current' style completion_menu_current_style = [] if self.completion_menu_current_bg_color: - completion_menu_current_style.append(f"bg:{self.completion_menu_current_bg_color}") + completion_menu_current_style.append(self.completion_menu_current_bg_color) if self.completion_menu_current_color: - completion_menu_current_style.append(self.completion_menu_current_color) + completion_menu_current_style.append(f"bg:{self.completion_menu_current_color}") if completion_menu_current_style: style_dict["completion-menu.completion.current"] = " ".join( completion_menu_current_style @@ -444,6 +525,9 @@ class InputOutput: ): self.rule() + # Ring the bell if needed + self.ring_bell() + rel_fnames = list(rel_fnames) show = "" if rel_fnames: @@ -451,11 +535,16 @@ class InputOutput: get_rel_fname(fname, root) for fname in (abs_read_only_fnames or []) ] show = self.format_files_for_input(rel_fnames, rel_read_only_fnames) + + prompt_prefix = "" if edit_format: - show += edit_format + prompt_prefix += edit_format if self.multiline_mode: - show += (" " if edit_format else "") + "multi" - show += "> " + prompt_prefix += (" " if edit_format else "") + "multi" + prompt_prefix += "> " + + show += prompt_prefix + self.prompt_prefix = prompt_prefix inp = "" multiline_input = False @@ -499,11 +588,30 @@ class InputOutput: "Navigate forward through history" event.current_buffer.history_forward() + @kb.add("c-x", "c-e") + def _(event): + "Edit current input in external editor (like Bash)" + buffer = event.current_buffer + current_text = buffer.text + + # Open the editor with the current text + edited_text = pipe_editor(input_data=current_text) + + # Replace the buffer with the edited text, strip any trailing newlines + buffer.text = edited_text.rstrip("\n") + + # Move cursor to the end of the text + buffer.cursor_position = len(buffer.text) + @kb.add("enter", eager=True, filter=~is_searching) def _(event): "Handle Enter key press" - if self.multiline_mode: - # In multiline mode, Enter adds a newline + if self.multiline_mode and not ( + self.editingmode == EditingMode.VI + and event.app.vi_state.input_mode == InputMode.NAVIGATION + ): + # In multiline mode and if not in vi-mode or vi navigation/normal mode, + # Enter adds a newline event.current_buffer.insert_text("\n") else: # In normal mode, Enter submits @@ -521,7 +629,7 @@ class InputOutput: while True: if multiline_input: - show = ". " + show = self.prompt_prefix try: if self.prompt_session: @@ -537,7 +645,7 @@ class InputOutput: self.clipboard_watcher.start() def get_continuation(width, line_number, is_soft_wrap): - return ". " + return self.prompt_prefix line = self.prompt_session.prompt( show, @@ -696,6 +804,9 @@ class InputOutput: ): self.num_user_asks += 1 + # Ring the bell if needed + self.ring_bell() + question_id = (question, subject) if question_id in self.never_prompts: @@ -750,14 +861,19 @@ class InputOutput: self.user_input(f"{question}{res}", log_only=False) else: while True: - if self.prompt_session: - res = self.prompt_session.prompt( - question, - style=style, - complete_while_typing=False, - ) - else: - res = input(question) + try: + if self.prompt_session: + res = self.prompt_session.prompt( + question, + style=style, + complete_while_typing=False, + ) + else: + res = input(question) + except EOFError: + # Treat EOF (Ctrl+D) as if the user pressed Enter + res = default + break if not res: res = default @@ -801,6 +917,9 @@ class InputOutput: def prompt_ask(self, question, default="", subject=None): self.num_user_asks += 1 + # Ring the bell if needed + self.ring_bell() + if subject: self.tool_output() self.tool_output(subject, bold=True) @@ -812,15 +931,19 @@ class InputOutput: elif self.yes is False: res = "no" else: - if self.prompt_session: - res = self.prompt_session.prompt( - question + " ", - default=default, - style=style, - complete_while_typing=True, - ) - else: - res = input(question + " ") + try: + if self.prompt_session: + res = self.prompt_session.prompt( + question + " ", + default=default, + style=style, + complete_while_typing=True, + ) + else: + res = input(question + " ") + except EOFError: + # Treat EOF (Ctrl+D) as if the user pressed Enter + res = default hist = f"{question.strip()} {res.strip()}" self.append_chat_history(hist, linebreak=True, blockquote=True) @@ -840,6 +963,7 @@ class InputOutput: if not isinstance(message, Text): message = Text(message) + color = ensure_hash_prefix(color) if color else None style = dict(style=color) if self.pretty and color else dict() try: self.console.print(message, **style) @@ -870,7 +994,7 @@ class InputOutput: style = dict() if self.pretty: if self.tool_output_color: - style["color"] = self.tool_output_color + style["color"] = ensure_hash_prefix(self.tool_output_color) style["reverse"] = bold style = RichStyle(**style) @@ -882,6 +1006,10 @@ class InputOutput: return mdStream def assistant_output(self, message, pretty=None): + if not message: + self.tool_warning("Empty response received from LLM. Check your provider account?") + return + show_resp = message # Coder will force pretty off if fence is not triple-backticks @@ -893,7 +1021,7 @@ class InputOutput: message, style=self.assistant_output_color, code_theme=self.code_theme ) else: - show_resp = Text(message or "") + show_resp = Text(message or "(empty response)") self.console.print(show_resp) @@ -904,6 +1032,61 @@ class InputOutput: def print(self, message=""): print(message) + def llm_started(self): + """Mark that the LLM has started processing, so we should ring the bell on next input""" + self.bell_on_next_input = True + + def get_default_notification_command(self): + """Return a default notification command based on the operating system.""" + import platform + + system = platform.system() + + if system == "Darwin": # macOS + # Check for terminal-notifier first + if shutil.which("terminal-notifier"): + return f"terminal-notifier -title 'Aider' -message '{NOTIFICATION_MESSAGE}'" + # Fall back to osascript + return ( + f'osascript -e \'display notification "{NOTIFICATION_MESSAGE}" with title "Aider"\'' + ) + elif system == "Linux": + # Check for common Linux notification tools + for cmd in ["notify-send", "zenity"]: + if shutil.which(cmd): + if cmd == "notify-send": + return f"notify-send 'Aider' '{NOTIFICATION_MESSAGE}'" + elif cmd == "zenity": + return f"zenity --notification --text='{NOTIFICATION_MESSAGE}'" + return None # No known notification tool found + elif system == "Windows": + # PowerShell notification + return ( + "powershell -command" + " \"[System.Reflection.Assembly]::LoadWithPartialName('System.Windows.Forms');" + f" [System.Windows.Forms.MessageBox]::Show('{NOTIFICATION_MESSAGE}'," + " 'Aider')\"" + ) + + return None # Unknown system + + def ring_bell(self): + """Ring the terminal bell if needed and clear the flag""" + if self.bell_on_next_input and self.notifications: + if self.notifications_command: + try: + result = subprocess.run( + self.notifications_command, shell=True, capture_output=True + ) + if result.returncode != 0 and result.stderr: + error_msg = result.stderr.decode("utf-8", errors="replace") + self.tool_warning(f"Failed to run notifications command: {error_msg}") + except Exception as e: + self.tool_warning(f"Failed to run notifications command: {e}") + else: + print("\a", end="", flush=True) # Ring the bell + self.bell_on_next_input = False # Clear the flag + def toggle_multiline_mode(self): """Toggle between normal and multiline input modes""" self.multiline_mode = not self.multiline_mode diff --git a/aider/linter.py b/aider/linter.py index 9b9ef24f7..920a8b7c6 100644 --- a/aider/linter.py +++ b/aider/linter.py @@ -4,6 +4,7 @@ import subprocess import sys import traceback import warnings +import shlex from dataclasses import dataclass from pathlib import Path @@ -44,7 +45,7 @@ class Linter: return fname def run_cmd(self, cmd, rel_fname, code): - cmd += " " + rel_fname + cmd += " " + shlex.quote(rel_fname) returncode = 0 stdout = "" diff --git a/aider/main.py b/aider/main.py index 76c9da478..89286e1de 100644 --- a/aider/main.py +++ b/aider/main.py @@ -1,4 +1,3 @@ -import configparser import json import os import re @@ -25,11 +24,13 @@ from aider.coders import Coder from aider.coders.base_coder import UnknownEditFormat from aider.commands import Commands, SwitchCoder from aider.copypaste import ClipboardWatcher +from aider.deprecated import handle_deprecated_model_args from aider.format_settings import format_settings, scrub_sensitive_info from aider.history import ChatSummary from aider.io import InputOutput from aider.llm import litellm # noqa: F401; properly init litellm on launch from aider.models import ModelSettings +from aider.onboarding import offer_openrouter_oauth, select_default_model from aider.repo import ANY_GIT_ERROR, GitRepo from aider.report import report_uncaught_exceptions from aider.versioncheck import check_version, install_from_main_branch, install_upgrade @@ -126,17 +127,15 @@ def setup_git(git_root, io): if not repo: return - user_name = None - user_email = None - with repo.config_reader() as config: - try: - user_name = config.get_value("user", "name", None) - except (configparser.NoSectionError, configparser.NoOptionError): - pass - try: - user_email = config.get_value("user", "email", None) - except (configparser.NoSectionError, configparser.NoOptionError): - pass + try: + user_name = repo.git.config("--get", "user.name") or None + except git.exc.GitCommandError: + user_name = None + + try: + user_email = repo.git.config("--get", "user.email") or None + except git.exc.GitCommandError: + user_email = None if user_name and user_email: return repo.working_tree_dir @@ -214,18 +213,6 @@ def check_streamlit_install(io): ) -def install_tree_sitter_language_pack(io): - return utils.check_pip_install_extra( - io, - "tree_sitter_language_pack", - "Install tree_sitter_language_pack?", - [ - "tree-sitter-language-pack==0.4.0", - "tree-sitter==0.24.0", - ], - ) - - def write_streamlit_credentials(): from streamlit.file_util import get_streamlit_file_path @@ -371,11 +358,21 @@ def register_models(git_root, model_settings_fname, io, verbose=False): def load_dotenv_files(git_root, dotenv_fname, encoding="utf-8"): + # Standard .env file search path dotenv_files = generate_search_path_list( ".env", git_root, dotenv_fname, ) + + # Explicitly add the OAuth keys file to the beginning of the list + oauth_keys_file = Path.home() / ".aider" / "oauth-keys.env" + if oauth_keys_file.exists(): + # Insert at the beginning so it's loaded first (and potentially overridden) + dotenv_files.insert(0, str(oauth_keys_file.resolve())) + # Remove duplicates if it somehow got included by generate_search_path_list + dotenv_files = list(dict.fromkeys(dotenv_files)) + loaded = [] for fname in dotenv_files: try: @@ -519,6 +516,8 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F litellm._load_litellm() litellm._lazy_module.client_session = httpx.Client(verify=False) litellm._lazy_module.aclient_session = httpx.AsyncClient(verify=False) + # Set verify_ssl on the model_info_manager + models.model_info_manager.set_verify_ssl(False) if args.timeout: models.request_timeout = args.timeout @@ -567,6 +566,8 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F editingmode=editing_mode, fancy_input=args.fancy_input, multiline_mode=args.multiline, + notifications=args.notifications, + notifications_command=args.notifications_command, ) io = get_io(args.pretty) @@ -606,6 +607,9 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F if args.openai_api_key: os.environ["OPENAI_API_KEY"] = args.openai_api_key + + # Handle deprecated model shortcut args + handle_deprecated_model_args(args, io) if args.openai_api_base: os.environ["OPENAI_API_BASE"] = args.openai_api_base if args.openai_api_version: @@ -718,19 +722,9 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F analytics.event("exit", reason="Upgrade completed") return 0 if success else 1 - if args.install_tree_sitter_language_pack: - success = install_tree_sitter_language_pack(io) - analytics.event("exit", reason="Install TSLP completed") - return 0 if success else 1 - if args.check_update: check_version(io, verbose=args.verbose) - if args.list_models: - models.print_matching_models(io, args.list_models) - analytics.event("exit", reason="Listed models") - return 0 - if args.git: git_root = setup_git(git_root, io) if args.gitignore: @@ -750,6 +744,11 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F register_models(git_root, args.model_settings_file, io, verbose=args.verbose) register_litellm_models(git_root, args.model_metadata_file, io, verbose=args.verbose) + if args.list_models: + models.print_matching_models(io, args.list_models) + analytics.event("exit", reason="Listed models") + return 0 + # Process any command line aliases if args.alias: for alias_def in args.alias: @@ -763,26 +762,49 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F alias, model = parts models.MODEL_ALIASES[alias.strip()] = model.strip() - if not args.model: - # Select model based on available API keys - model_key_pairs = [ - ("ANTHROPIC_API_KEY", "sonnet"), - ("DEEPSEEK_API_KEY", "deepseek"), - ("OPENROUTER_API_KEY", "openrouter/anthropic/claude-3.5-sonnet"), - ("OPENAI_API_KEY", "gpt-4o"), - ("GEMINI_API_KEY", "flash"), - ] + selected_model_name = select_default_model(args, io, analytics) + if not selected_model_name: + # Error message and analytics event are handled within select_default_model + # It might have already offered OAuth if no model/keys were found. + # If it failed here, we exit. + return 1 + args.model = selected_model_name # Update args with the selected model - for env_key, model_name in model_key_pairs: - if os.environ.get(env_key): - args.model = model_name - io.tool_warning( - f"Found {env_key} so using {model_name} since no --model was specified." + # Check if an OpenRouter model was selected/specified but the key is missing + if args.model.startswith("openrouter/") and not os.environ.get("OPENROUTER_API_KEY"): + io.tool_warning( + f"The specified model '{args.model}' requires an OpenRouter API key, which was not" + " found." + ) + # Attempt OAuth flow because the specific model needs it + if offer_openrouter_oauth(io, analytics): + # OAuth succeeded, the key should now be in os.environ. + # Check if the key is now present after the flow. + if os.environ.get("OPENROUTER_API_KEY"): + io.tool_output( + "OpenRouter successfully connected." + ) # Inform user connection worked + else: + # This case should ideally not happen if offer_openrouter_oauth succeeded + # but check defensively. + io.tool_error( + "OpenRouter authentication seemed successful, but the key is still missing." ) - break - if not args.model: - io.tool_error("You need to specify a --model and an --api-key to use.") - io.offer_url(urls.models_and_keys, "Open documentation url for more info?") + analytics.event( + "exit", + reason="OpenRouter key missing after successful OAuth for specified model", + ) + return 1 + else: + # OAuth failed or was declined by the user + io.tool_error( + f"Unable to proceed without an OpenRouter API key for model '{args.model}'." + ) + io.offer_url(urls.models_and_keys, "Open documentation URL for more info?") + analytics.event( + "exit", + reason="OpenRouter key missing for specified model and OAuth failed/declined", + ) return 1 main_model = models.Model( @@ -790,18 +812,52 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F weak_model=args.weak_model, editor_model=args.editor_model, editor_edit_format=args.editor_edit_format, + verbose=args.verbose, ) - # add --reasoning-effort cli param + # Check if deprecated remove_reasoning is set + if main_model.remove_reasoning is not None: + io.tool_warning( + "Model setting 'remove_reasoning' is deprecated, please use 'reasoning_tag' instead." + ) + + # Set reasoning effort and thinking tokens if specified if args.reasoning_effort is not None: - if not getattr(main_model, "extra_params", None): - main_model.extra_params = {} - if "extra_body" not in main_model.extra_params: - main_model.extra_params["extra_body"] = {} - main_model.extra_params["extra_body"]["reasoning_effort"] = args.reasoning_effort + # Apply if check is disabled or model explicitly supports it + if not args.check_model_accepts_settings or ( + main_model.accepts_settings and "reasoning_effort" in main_model.accepts_settings + ): + main_model.set_reasoning_effort(args.reasoning_effort) + + if args.thinking_tokens is not None: + # Apply if check is disabled or model explicitly supports it + if not args.check_model_accepts_settings or ( + main_model.accepts_settings and "thinking_tokens" in main_model.accepts_settings + ): + main_model.set_thinking_tokens(args.thinking_tokens) + + # Show warnings about unsupported settings that are being ignored + if args.check_model_accepts_settings: + settings_to_check = [ + {"arg": args.reasoning_effort, "name": "reasoning_effort"}, + {"arg": args.thinking_tokens, "name": "thinking_tokens"}, + ] + + for setting in settings_to_check: + if setting["arg"] is not None and ( + not main_model.accepts_settings + or setting["name"] not in main_model.accepts_settings + ): + io.tool_warning( + f"Warning: {main_model.name} does not support '{setting['name']}', ignoring." + ) + io.tool_output( + f"Use --no-check-model-accepts-settings to force the '{setting['name']}'" + " setting." + ) if args.copy_paste and args.edit_format is None: - if main_model.edit_format in ("diff", "whole"): + if main_model.edit_format in ("diff", "whole", "diff-fenced"): main_model.edit_format = "editor-" + main_model.edit_format if args.verbose: @@ -847,6 +903,7 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F attribute_commit_message_committer=args.attribute_commit_message_committer, commit_prompt=args.commit_prompt, subtree_only=args.subtree_only, + git_commit_verify=args.git_commit_verify, ) except FileNotFoundError: pass @@ -872,6 +929,7 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F parser=parser, verbose=args.verbose, editor=args.editor, + original_read_only_fnames=read_only_fnames, ) summarizer = ChatSummary( @@ -894,6 +952,9 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F else: map_tokens = args.map_tokens + # Track auto-commits configuration + analytics.event("auto_commits", enabled=bool(args.auto_commits)) + try: coder = Coder.create( main_model=main_model, @@ -926,6 +987,7 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F chat_language=args.chat_language, detect_urls=args.detect_urls, auto_copy_context=args.copy_paste, + auto_accept_architect=args.auto_accept_architect, ) except UnknownEditFormat as err: io.tool_error(str(err)) @@ -1039,6 +1101,9 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F io.tool_output(f"Cur working dir: {Path.cwd()}") io.tool_output(f"Git working dir: {git_root}") + if args.stream and args.cache_prompts: + io.tool_warning("Cost estimates may be inaccurate when using streaming and caching.") + if args.load: commands.cmd_load(args.load) @@ -1084,6 +1149,10 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F except SwitchCoder as switch: coder.ok_to_warm_cache = False + # Set the placeholder if provided + if hasattr(switch, "placeholder") and switch.placeholder is not None: + io.placeholder = switch.placeholder + kwargs = dict(io=io, from_coder=coder) kwargs.update(switch.kwargs) if "show_announcements" in kwargs: diff --git a/aider/mdstream.py b/aider/mdstream.py index e7e19df87..24c14f0d4 100755 --- a/aider/mdstream.py +++ b/aider/mdstream.py @@ -3,9 +3,12 @@ import io import time +from rich import box from rich.console import Console from rich.live import Live -from rich.markdown import Markdown +from rich.markdown import CodeBlock, Heading, Markdown +from rich.panel import Panel +from rich.syntax import Syntax from rich.text import Text from aider.dump import dump # noqa: F401 @@ -46,6 +49,46 @@ The end. """ # noqa: E501 +class NoInsetCodeBlock(CodeBlock): + """A code block with syntax highlighting and no padding.""" + + def __rich_console__(self, console, options): + code = str(self.text).rstrip() + syntax = Syntax(code, self.lexer_name, theme=self.theme, word_wrap=True, padding=(1, 0)) + yield syntax + + +class LeftHeading(Heading): + """A heading class that renders left-justified.""" + + def __rich_console__(self, console, options): + text = self.text + text.justify = "left" # Override justification + if self.tag == "h1": + # Draw a border around h1s, but keep text left-aligned + yield Panel( + text, + box=box.HEAVY, + style="markdown.h1.border", + ) + else: + # Styled text for h2 and beyond + if self.tag == "h2": + yield Text("") # Keep the blank line before h2 + yield text + + +class NoInsetMarkdown(Markdown): + """Markdown with code blocks that have no padding and left-justified headings.""" + + elements = { + **Markdown.elements, + "fence": NoInsetCodeBlock, + "code_block": NoInsetCodeBlock, + "heading_open": LeftHeading, + } + + class MarkdownStream: """Streaming markdown renderer that progressively displays content with a live updating window. @@ -88,7 +131,7 @@ class MarkdownStream: # Render the markdown to a string buffer string_io = io.StringIO() console = Console(file=string_io, force_terminal=True) - markdown = Markdown(text, **self.mdargs) + markdown = NoInsetMarkdown(text, **self.mdargs) console.print(markdown) output = string_io.getvalue() @@ -186,6 +229,7 @@ if __name__ == "__main__": _text = _text * 10 pm = MarkdownStream() + print("Using NoInsetMarkdown for code blocks with padding=0") for i in range(6, len(_text), 5): pm.update(_text[:i]) time.sleep(0.01) diff --git a/aider/models.py b/aider/models.py index 870b6777b..dd0abd452 100644 --- a/aider/models.py +++ b/aider/models.py @@ -5,7 +5,6 @@ import json import math import os import platform -import re import sys import time from dataclasses import dataclass, fields @@ -19,6 +18,7 @@ from PIL import Image from aider.dump import dump # noqa: F401 from aider.llm import litellm from aider.sendchat import ensure_alternating_roles, sanity_check_messages +from aider.utils import check_pip_install_extra RETRY_TIMEOUT = 60 @@ -88,8 +88,14 @@ MODEL_ALIASES = { "3": "gpt-3.5-turbo", # Other models "deepseek": "deepseek/deepseek-chat", + "flash": "gemini/gemini-2.5-flash-preview-04-17", + "quasar": "openrouter/openrouter/quasar-alpha", "r1": "deepseek/deepseek-reasoner", - "flash": "gemini/gemini-2.0-flash-exp", + "gemini-2.5-pro": "gemini/gemini-2.5-pro-exp-03-25", + "gemini": "gemini/gemini-2.5-pro-preview-03-25", + "gemini-exp": "gemini/gemini-2.5-pro-exp-03-25", + "grok3": "xai/grok-3-beta", + "optimus": "openrouter/openrouter/optimus-alpha", } # Model metadata loaded from resources and user's files. @@ -103,6 +109,7 @@ class ModelSettings: use_repo_map: bool = False send_undo_reply: bool = False lazy: bool = False + overeager: bool = False reminder: str = "user" examples_as_sys_msg: bool = False extra_params: Optional[dict] = None @@ -113,8 +120,10 @@ class ModelSettings: streaming: bool = True editor_model_name: Optional[str] = None editor_edit_format: Optional[str] = None - remove_reasoning: Optional[str] = None + reasoning_tag: Optional[str] = None + remove_reasoning: Optional[str] = None # Deprecated alias for reasoning_tag system_prompt_prefix: Optional[str] = None + accepts_settings: Optional[list] = None # Load model settings from package resource @@ -137,23 +146,37 @@ class ModelInfoManager: self.cache_file = self.cache_dir / "model_prices_and_context_window.json" self.content = None self.local_model_metadata = {} - self._load_cache() + self.verify_ssl = True + self._cache_loaded = False + + def set_verify_ssl(self, verify_ssl): + self.verify_ssl = verify_ssl def _load_cache(self): + if self._cache_loaded: + return + try: self.cache_dir.mkdir(parents=True, exist_ok=True) if self.cache_file.exists(): cache_age = time.time() - self.cache_file.stat().st_mtime if cache_age < self.CACHE_TTL: - self.content = json.loads(self.cache_file.read_text()) + try: + self.content = json.loads(self.cache_file.read_text()) + except json.JSONDecodeError: + # If the cache file is corrupted, treat it as missing + self.content = None except OSError: pass + self._cache_loaded = True + def _update_cache(self): try: import requests - response = requests.get(self.MODEL_INFO_URL, timeout=5) + # Respect the --no-verify-ssl switch + response = requests.get(self.MODEL_INFO_URL, timeout=5, verify=self.verify_ssl) if response.status_code == 200: self.content = response.json() try: @@ -173,6 +196,9 @@ class ModelInfoManager: if data: return data + # Ensure cache is loaded before checking content + self._load_cache() + if not self.content: self._update_cache() @@ -212,11 +238,14 @@ model_info_manager = ModelInfoManager() class Model(ModelSettings): - def __init__(self, model, weak_model=None, editor_model=None, editor_edit_format=None): + def __init__( + self, model, weak_model=None, editor_model=None, editor_edit_format=None, verbose=False + ): # Map any alias to its canonical name model = MODEL_ALIASES.get(model, model) self.name = model + self.verbose = verbose self.max_chat_history_tokens = 1024 self.weak_model = None @@ -259,6 +288,11 @@ class Model(ModelSettings): val = getattr(source, field.name) setattr(self, field.name, val) + # Handle backward compatibility: if remove_reasoning is set but reasoning_tag isn't, + # use remove_reasoning's value for reasoning_tag + if self.reasoning_tag is None and self.remove_reasoning is not None: + self.reasoning_tag = self.remove_reasoning + def configure_model_settings(self, model): # Look for exact model match exact_match = False @@ -269,6 +303,10 @@ class Model(ModelSettings): exact_match = True break # Continue to apply overrides + # Initialize accepts_settings if it's None + if self.accepts_settings is None: + self.accepts_settings = [] + model = model.lower() # If no exact match, try generic settings @@ -276,7 +314,11 @@ class Model(ModelSettings): self.apply_generic_model_settings(model) # Apply override settings last if they exist - if self.extra_model_settings and self.extra_model_settings.extra_params: + if ( + self.extra_model_settings + and self.extra_model_settings.extra_params + and self.extra_model_settings.name == "aider/extra_params" + ): # Initialize extra_params if it doesn't exist if not self.extra_params: self.extra_params = {} @@ -296,6 +338,23 @@ class Model(ModelSettings): self.use_repo_map = True self.use_temperature = False self.system_prompt_prefix = "Formatting re-enabled. " + self.system_prompt_prefix = "Formatting re-enabled. " + if "reasoning_effort" not in self.accepts_settings: + self.accepts_settings.append("reasoning_effort") + return # <-- + + if "gpt-4.1-mini" in model: + self.edit_format = "diff" + self.use_repo_map = True + self.reminder = "sys" + self.examples_as_sys_msg = False + return # <-- + + if "gpt-4.1" in model: + self.edit_format = "diff" + self.use_repo_map = True + self.reminder = "sys" + self.examples_as_sys_msg = False return # <-- if "/o1-mini" in model: @@ -317,6 +376,8 @@ class Model(ModelSettings): self.use_temperature = False self.streaming = False self.system_prompt_prefix = "Formatting re-enabled. " + if "reasoning_effort" not in self.accepts_settings: + self.accepts_settings.append("reasoning_effort") return # <-- if "deepseek" in model and "v3" in model: @@ -331,7 +392,7 @@ class Model(ModelSettings): self.use_repo_map = True self.examples_as_sys_msg = True self.use_temperature = False - self.remove_reasoning = "think" + self.reasoning_tag = "think" return # <-- if ("llama3" in model or "llama-3" in model) and "70b" in model: @@ -357,6 +418,15 @@ class Model(ModelSettings): self.reminder = "sys" return # <-- + if "3-7-sonnet" in model: + self.edit_format = "diff" + self.use_repo_map = True + self.examples_as_sys_msg = True + self.reminder = "user" + if "thinking_tokens" not in self.accepts_settings: + self.accepts_settings.append("thinking_tokens") + return # <-- + if "3.5-sonnet" in model or "3-5-sonnet" in model: self.edit_format = "diff" self.use_repo_map = True @@ -380,6 +450,16 @@ class Model(ModelSettings): self.use_repo_map = True return # <-- + if "qwq" in model and "32b" in model and "preview" not in model: + self.edit_format = "diff" + self.editor_edit_format = "editor-diff" + self.use_repo_map = True + self.reasoning_tag = "think" + self.examples_as_sys_msg = True + self.use_temperature = 0.6 + self.extra_params = dict(top_p=0.95) + return # <-- + # use the defaults if self.edit_format == "diff": self.use_repo_map = True @@ -427,6 +507,8 @@ class Model(ModelSettings): if not self.editor_edit_format: self.editor_edit_format = self.editor_model.edit_format + if self.editor_edit_format in ("diff", "whole", "diff-fenced"): + self.editor_edit_format = "editor-" + self.editor_edit_format return self.editor_model @@ -535,6 +617,21 @@ class Model(ModelSettings): model = self.name res = litellm.validate_environment(model) + + # If missing AWS credential keys but AWS_PROFILE is set, consider AWS credentials valid + if res["missing_keys"] and any( + key in ["AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY"] for key in res["missing_keys"] + ): + if model.startswith("bedrock/") or model.startswith("us.anthropic."): + if os.environ.get("AWS_PROFILE"): + res["missing_keys"] = [ + k + for k in res["missing_keys"] + if k not in ["AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY"] + ] + if not res["missing_keys"]: + res["keys_in_environment"] = True + if res["keys_in_environment"]: return res if res["missing_keys"]: @@ -559,6 +656,108 @@ class Model(ModelSettings): map_tokens = max(map_tokens, 1024) return map_tokens + def set_reasoning_effort(self, effort): + """Set the reasoning effort parameter for models that support it""" + if effort is not None: + if not self.extra_params: + self.extra_params = {} + if "extra_body" not in self.extra_params: + self.extra_params["extra_body"] = {} + self.extra_params["extra_body"]["reasoning_effort"] = effort + + def parse_token_value(self, value): + """ + Parse a token value string into an integer. + Accepts formats: 8096, "8k", "10.5k", "0.5M", "10K", etc. + + Args: + value: String or int token value + + Returns: + Integer token value + """ + if isinstance(value, int): + return value + + if not isinstance(value, str): + return int(value) # Try to convert to int + + value = value.strip().upper() + + if value.endswith("K"): + multiplier = 1024 + value = value[:-1] + elif value.endswith("M"): + multiplier = 1024 * 1024 + value = value[:-1] + else: + multiplier = 1 + + # Convert to float first to handle decimal values like "10.5k" + return int(float(value) * multiplier) + + def set_thinking_tokens(self, value): + """ + Set the thinking token budget for models that support it. + Accepts formats: 8096, "8k", "10.5k", "0.5M", "10K", etc. + """ + if value is not None: + num_tokens = self.parse_token_value(value) + self.use_temperature = False + if not self.extra_params: + self.extra_params = {} + + # OpenRouter models use 'reasoning' instead of 'thinking' + if self.name.startswith("openrouter/"): + self.extra_params["reasoning"] = {"max_tokens": num_tokens} + else: + self.extra_params["thinking"] = {"type": "enabled", "budget_tokens": num_tokens} + + def get_raw_thinking_tokens(self): + """Get formatted thinking token budget if available""" + budget = None + + if self.extra_params: + # Check for OpenRouter reasoning format + if "reasoning" in self.extra_params and "max_tokens" in self.extra_params["reasoning"]: + budget = self.extra_params["reasoning"]["max_tokens"] + # Check for standard thinking format + elif ( + "thinking" in self.extra_params and "budget_tokens" in self.extra_params["thinking"] + ): + budget = self.extra_params["thinking"]["budget_tokens"] + + return budget + + def get_thinking_tokens(self): + budget = self.get_raw_thinking_tokens() + + if budget is not None: + # Format as xx.yK for thousands, xx.yM for millions + if budget >= 1024 * 1024: + value = budget / (1024 * 1024) + if value == int(value): + return f"{int(value)}M" + else: + return f"{value:.1f}M" + else: + value = budget / 1024 + if value == int(value): + return f"{int(value)}k" + else: + return f"{value:.1f}k" + return None + + def get_reasoning_effort(self): + """Get reasoning effort value if available""" + if ( + self.extra_params + and "extra_body" in self.extra_params + and "reasoning_effort" in self.extra_params["extra_body"] + ): + return self.extra_params["extra_body"]["reasoning_effort"] + return None + def is_deepseek_r1(self): name = self.name.lower() if "deepseek" not in name: @@ -577,7 +776,6 @@ class Model(ModelSettings): kwargs = dict( model=self.name, - messages=messages, stream=stream, ) @@ -606,17 +804,13 @@ class Model(ModelSettings): hash_object = hashlib.sha1(key) if "timeout" not in kwargs: kwargs["timeout"] = request_timeout + if self.verbose: + dump(kwargs) + kwargs["messages"] = messages + res = litellm.completion(**kwargs) return hash_object, res - def remove_reasoning_content(self, res): - if not self.remove_reasoning: - return res - - pattern = f"<{self.remove_reasoning}>.*?" - res = re.sub(pattern, "", res, flags=re.DOTALL).strip() - return res - def simple_send_with_retries(self, messages): from aider.exceptions import LiteLLMExceptions @@ -637,7 +831,9 @@ class Model(ModelSettings): if not response or not hasattr(response, "choices") or not response.choices: return None res = response.choices[0].message.content - return self.remove_reasoning_content(res) + from aider.reasoning_tags import remove_reasoning_content + + return remove_reasoning_content(res, self.reasoning_tag) except litellm_ex.exceptions_tuple() as err: ex_info = litellm_ex.get_ex_info(err) @@ -760,6 +956,9 @@ def sanity_check_model(io, model): show = True io.tool_warning(f"Warning for {model}: Unknown which environment variables are required.") + # Check for model-specific dependencies + check_for_dependencies(io, model.name) + if not model.info: show = True io.tool_warning( @@ -775,11 +974,38 @@ def sanity_check_model(io, model): return show +def check_for_dependencies(io, model_name): + """ + Check for model-specific dependencies and install them if needed. + + Args: + io: The IO object for user interaction + model_name: The name of the model to check dependencies for + """ + # Check if this is a Bedrock model and ensure boto3 is installed + if model_name.startswith("bedrock/"): + check_pip_install_extra( + io, "boto3", "AWS Bedrock models require the boto3 package.", ["boto3"] + ) + + # Check if this is a Vertex AI model and ensure google-cloud-aiplatform is installed + elif model_name.startswith("vertex_ai/"): + check_pip_install_extra( + io, + "google.cloud.aiplatform", + "Google Vertex AI models require the google-cloud-aiplatform package.", + ["google-cloud-aiplatform"], + ) + + def fuzzy_match_models(name): name = name.lower() chat_models = set() - for orig_model, attrs in litellm.model_cost.items(): + model_metadata = list(litellm.model_cost.items()) + model_metadata += list(model_info_manager.local_model_metadata.items()) + + for orig_model, attrs in model_metadata: model = orig_model.lower() if attrs.get("mode") != "chat": continue diff --git a/aider/onboarding.py b/aider/onboarding.py new file mode 100644 index 000000000..0321c0d63 --- /dev/null +++ b/aider/onboarding.py @@ -0,0 +1,428 @@ +import base64 +import hashlib +import http.server +import os +import secrets +import socketserver +import threading +import time +import webbrowser +from urllib.parse import parse_qs, urlparse + +import requests + +from aider import urls +from aider.io import InputOutput + + +def check_openrouter_tier(api_key): + """ + Checks if the user is on a free tier for OpenRouter. + + Args: + api_key: The OpenRouter API key to check. + + Returns: + A boolean indicating if the user is on a free tier (True) or paid tier (False). + Returns True if the check fails. + """ + try: + response = requests.get( + "https://openrouter.ai/api/v1/auth/key", + headers={"Authorization": f"Bearer {api_key}"}, + timeout=5, # Add a reasonable timeout + ) + response.raise_for_status() + data = response.json() + # According to the documentation, 'is_free_tier' will be true if the user has never paid + return data.get("data", {}).get("is_free_tier", True) # Default to True if not found + except Exception: + # If there's any error, we'll default to assuming free tier + return True + + +def try_to_select_default_model(): + """ + Attempts to select a default model based on available API keys. + Checks OpenRouter tier status to select appropriate model. + + Returns: + The name of the selected model, or None if no suitable default is found. + """ + # Special handling for OpenRouter + openrouter_key = os.environ.get("OPENROUTER_API_KEY") + if openrouter_key: + # Check if the user is on a free tier + is_free_tier = check_openrouter_tier(openrouter_key) + if is_free_tier: + return "openrouter/google/gemini-2.5-pro-exp-03-25:free" + else: + return "openrouter/anthropic/claude-3.7-sonnet" + + # Select model based on other available API keys + model_key_pairs = [ + ("ANTHROPIC_API_KEY", "sonnet"), + ("DEEPSEEK_API_KEY", "deepseek"), + ("OPENAI_API_KEY", "gpt-4o"), + ("GEMINI_API_KEY", "gemini/gemini-2.5-pro-exp-03-25"), + ("VERTEXAI_PROJECT", "vertex_ai/gemini-2.5-pro-exp-03-25"), + ] + + for env_key, model_name in model_key_pairs: + api_key_value = os.environ.get(env_key) + if api_key_value: + return model_name + + return None + + +def offer_openrouter_oauth(io, analytics): + """ + Offers OpenRouter OAuth flow to the user if no API keys are found. + + Args: + io: The InputOutput object for user interaction. + analytics: The Analytics object for tracking events. + + Returns: + True if authentication was successful, False otherwise. + """ + # No API keys found - Offer OpenRouter OAuth + io.tool_output("OpenRouter provides free and paid access to many LLMs.") + # Use confirm_ask which handles non-interactive cases + if io.confirm_ask( + "Login to OpenRouter or create a free account?", + default="y", + ): + analytics.event("oauth_flow_initiated", provider="openrouter") + openrouter_key = start_openrouter_oauth_flow(io, analytics) + if openrouter_key: + # Successfully got key via OAuth, use the default OpenRouter model + # Ensure OPENROUTER_API_KEY is now set in the environment for later use + os.environ["OPENROUTER_API_KEY"] = openrouter_key + # Track OAuth success leading to model selection + analytics.event("oauth_flow_success") + return True + + # OAuth failed or was cancelled by user implicitly (e.g., closing browser) + # Error messages are handled within start_openrouter_oauth_flow + analytics.event("oauth_flow_failure") + io.tool_error("OpenRouter authentication did not complete successfully.") + # Fall through to the final error message + + return False + + +def select_default_model(args, io, analytics): + """ + Selects a default model based on available API keys if no model is specified. + Offers OAuth flow for OpenRouter if no keys are found. + + Args: + args: The command line arguments object. + io: The InputOutput object for user interaction. + analytics: The Analytics object for tracking events. + + Returns: + The name of the selected model, or None if no suitable default is found. + """ + if args.model: + return args.model # Model already specified + + model = try_to_select_default_model() + if model: + io.tool_warning(f"Using {model} model with API key from environment.") + analytics.event("auto_model_selection", model=model) + return model + + no_model_msg = "No LLM model was specified and no API keys were provided." + io.tool_warning(no_model_msg) + + # Try OAuth if no model was detected + offer_openrouter_oauth(io, analytics) + + # Check again after potential OAuth success + model = try_to_select_default_model() + if model: + return model + + io.offer_url(urls.models_and_keys, "Open documentation URL for more info?") + + +# Helper function to find an available port +def find_available_port(start_port=8484, end_port=8584): + for port in range(start_port, end_port + 1): + try: + # Check if the port is available by trying to bind to it + with socketserver.TCPServer(("localhost", port), None): + return port + except OSError: + # Port is likely already in use + continue + return None + + +# PKCE code generation +def generate_pkce_codes(): + code_verifier = secrets.token_urlsafe(64) + hasher = hashlib.sha256() + hasher.update(code_verifier.encode("utf-8")) + code_challenge = base64.urlsafe_b64encode(hasher.digest()).rstrip(b"=").decode("utf-8") + return code_verifier, code_challenge + + +# Function to exchange the authorization code for an API key +def exchange_code_for_key(code, code_verifier, io): + try: + response = requests.post( + "https://openrouter.ai/api/v1/auth/keys", + headers={"Content-Type": "application/json"}, + json={ + "code": code, + "code_verifier": code_verifier, + "code_challenge_method": "S256", + }, + timeout=30, # Add a timeout + ) + response.raise_for_status() # Raise exception for bad status codes (4xx or 5xx) + data = response.json() + api_key = data.get("key") + if not api_key: + io.tool_error("Error: 'key' not found in OpenRouter response.") + io.tool_error(f"Response: {response.text}") + return None + return api_key + except requests.exceptions.Timeout: + io.tool_error("Error: Request to OpenRouter timed out during code exchange.") + return None + except requests.exceptions.HTTPError as e: + io.tool_error( + "Error exchanging code for OpenRouter key:" + f" {e.response.status_code} {e.response.reason}" + ) + io.tool_error(f"Response: {e.response.text}") + return None + except requests.exceptions.RequestException as e: + io.tool_error(f"Error exchanging code for OpenRouter key: {e}") + return None + except Exception as e: + io.tool_error(f"Unexpected error during code exchange: {e}") + return None + + +# Function to start the OAuth flow +def start_openrouter_oauth_flow(io, analytics): + """Initiates the OpenRouter OAuth PKCE flow using a local server.""" + + port = find_available_port() + if not port: + io.tool_error("Could not find an available port between 8484 and 8584.") + io.tool_error("Please ensure a port in this range is free, or configure manually.") + return None + + callback_url = f"http://localhost:{port}/callback/aider" + auth_code = None + server_error = None + server_started = threading.Event() + shutdown_server = threading.Event() + + class OAuthCallbackHandler(http.server.SimpleHTTPRequestHandler): + def do_GET(self): + nonlocal auth_code, server_error + parsed_path = urlparse(self.path) + if parsed_path.path == "/callback/aider": + query_params = parse_qs(parsed_path.query) + if "code" in query_params: + auth_code = query_params["code"][0] + self.send_response(200) + self.send_header("Content-type", "text/html") + self.end_headers() + self.wfile.write( + b"

Success!

" + b"

Aider has received the authentication code. " + b"You can close this browser tab.

" + ) + # Signal the main thread to shut down the server + # Signal the main thread to shut down the server + shutdown_server.set() + else: + # Redirect to aider website if 'code' is missing (e.g., user visited manually) + self.send_response(302) # Found (temporary redirect) + self.send_header("Location", urls.website) + self.end_headers() + # No need to set server_error, just redirect. + # Do NOT shut down the server here; wait for timeout or success. + else: + # Redirect anything else (e.g., favicon.ico) to the main website as well + self.send_response(302) + self.send_header("Location", urls.website) + self.end_headers() + self.wfile.write(b"Not Found") + + def log_message(self, format, *args): + # Suppress server logging to keep terminal clean + pass + + def run_server(): + nonlocal server_error + try: + with socketserver.TCPServer(("localhost", port), OAuthCallbackHandler) as httpd: + io.tool_output(f"Temporary server listening on {callback_url}", log_only=True) + server_started.set() # Signal that the server is ready + # Wait until shutdown is requested or timeout occurs (handled by main thread) + while not shutdown_server.is_set(): + httpd.handle_request() # Handle one request at a time + # Add a small sleep to prevent busy-waiting if needed, + # though handle_request should block appropriately. + time.sleep(0.1) + io.tool_output("Shutting down temporary server.", log_only=True) + except Exception as e: + server_error = f"Failed to start or run temporary server: {e}" + server_started.set() # Signal even if failed, error will be checked + shutdown_server.set() # Ensure shutdown logic proceeds + + server_thread = threading.Thread(target=run_server, daemon=True) + server_thread.start() + + # Wait briefly for the server to start, or for an error + if not server_started.wait(timeout=5): + io.tool_error("Temporary authentication server failed to start in time.") + shutdown_server.set() # Ensure thread exits if it eventually starts + server_thread.join(timeout=1) + return None + + # Check if server failed during startup + if server_error: + io.tool_error(server_error) + shutdown_server.set() # Ensure thread exits + server_thread.join(timeout=1) + return None + + # Generate codes and URL + code_verifier, code_challenge = generate_pkce_codes() + auth_url_base = "https://openrouter.ai/auth" + auth_params = { + "callback_url": callback_url, + "code_challenge": code_challenge, + "code_challenge_method": "S256", + } + auth_url = f"{auth_url_base}?{'&'.join(f'{k}={v}' for k, v in auth_params.items())}" + + io.tool_output("\nPlease open this URL in your browser to connect Aider with OpenRouter:") + io.tool_output() + print(auth_url) + + MINUTES = 5 + io.tool_output(f"\nWaiting up to {MINUTES} minutes for you to finish in the browser...") + io.tool_output("Use Control-C to interrupt.") + + try: + webbrowser.open(auth_url) + except Exception: + pass + + # Wait for the callback to set the auth_code or for timeout/error + interrupted = False + try: + shutdown_server.wait(timeout=MINUTES * 60) # Convert minutes to seconds + except KeyboardInterrupt: + io.tool_warning("\nOAuth flow interrupted.") + analytics.event("oauth_flow_failed", provider="openrouter", reason="user_interrupt") + interrupted = True + # Ensure the server thread is signaled to shut down + shutdown_server.set() + + # Join the server thread to ensure it's cleaned up + server_thread.join(timeout=1) + + if interrupted: + return None # Return None if interrupted by user + + if server_error: + io.tool_error(f"Authentication failed: {server_error}") + analytics.event("oauth_flow_failed", provider="openrouter", reason=server_error) + return None + + if not auth_code: + io.tool_error("Authentication with OpenRouter failed.") + analytics.event("oauth_flow_failed", provider="openrouter") + return None + + io.tool_output("Completing authentication...") + analytics.event("oauth_flow_code_received", provider="openrouter") + + # Exchange code for key + api_key = exchange_code_for_key(auth_code, code_verifier, io) + + if api_key: + # Set env var for the current session immediately + os.environ["OPENROUTER_API_KEY"] = api_key + + # Save the key to the oauth-keys.env file + try: + config_dir = os.path.expanduser("~/.aider") + os.makedirs(config_dir, exist_ok=True) + key_file = os.path.join(config_dir, "oauth-keys.env") + with open(key_file, "a", encoding="utf-8") as f: + f.write(f'OPENROUTER_API_KEY="{api_key}"\n') + + io.tool_warning("Aider will load the OpenRouter key automatically in future sessions.") + io.tool_output() + + analytics.event("oauth_flow_success", provider="openrouter") + return api_key + except Exception as e: + io.tool_error(f"Successfully obtained key, but failed to save it to file: {e}") + io.tool_warning("Set OPENROUTER_API_KEY environment variable for this session only.") + # Still return the key for the current session even if saving failed + analytics.event("oauth_flow_save_failed", provider="openrouter", reason=str(e)) + return api_key + else: + io.tool_error("Authentication with OpenRouter failed.") + analytics.event("oauth_flow_failed", provider="openrouter", reason="code_exchange_failed") + return None + + +# Dummy Analytics class for testing +class DummyAnalytics: + def event(self, *args, **kwargs): + # print(f"Analytics Event: {args} {kwargs}") # Optional: print events + pass + + +def main(): + """Main function to test the OpenRouter OAuth flow.""" + print("Starting OpenRouter OAuth flow test...") + + # Use a real IO object for interaction + io = InputOutput( + pretty=True, + yes=False, + input_history_file=None, + chat_history_file=None, + tool_output_color="BLUE", + tool_error_color="RED", + ) + # Use a dummy analytics object + analytics = DummyAnalytics() + + # Ensure OPENROUTER_API_KEY is not set, to trigger the flow naturally + # (though start_openrouter_oauth_flow doesn't check this itself) + if "OPENROUTER_API_KEY" in os.environ: + print("Warning: OPENROUTER_API_KEY is already set in environment.") + # del os.environ["OPENROUTER_API_KEY"] # Optionally unset it for testing + + api_key = start_openrouter_oauth_flow(io, analytics) + + if api_key: + print("\nOAuth flow completed successfully!") + print(f"Obtained API Key (first 5 chars): {api_key[:5]}...") + # Be careful printing the key, even partially + else: + print("\nOAuth flow failed or was cancelled.") + + print("\nOpenRouter OAuth flow test finished.") + + +if __name__ == "__main__": + main() diff --git a/aider/prompts.py b/aider/prompts.py index 27d833fdd..84ed75e9b 100644 --- a/aider/prompts.py +++ b/aider/prompts.py @@ -15,7 +15,7 @@ Use these for : fix, feat, build, chore, ci, docs, style, refactor, perf, Ensure the commit message: - Starts with the appropriate prefix. -- Is in the imperative mood (e.g., \"Add feature\" not \"Added feature\" or \"Adding feature\"). +- Is in the imperative mood (e.g., \"add feature\" not \"added feature\" or \"adding feature\"). - Does not exceed 72 characters. Reply only with the one-line commit message, without any additional text, explanations, \ diff --git a/aider/queries/tree-sitter-language-pack/README.md b/aider/queries/tree-sitter-language-pack/README.md new file mode 100644 index 000000000..4654865ef --- /dev/null +++ b/aider/queries/tree-sitter-language-pack/README.md @@ -0,0 +1,7 @@ +These scm files are all adapted from the github repositories listed here: + +https://github.com/Goldziher/tree-sitter-language-pack/blob/main/sources/language_definitions.json + +See this URL for information on the licenses of each repo: + +https://github.com/Goldziher/tree-sitter-language-pack/ diff --git a/aider/queries/tree-sitter-language-pack/arduino-tags.scm b/aider/queries/tree-sitter-language-pack/arduino-tags.scm new file mode 100644 index 000000000..71cc3849f --- /dev/null +++ b/aider/queries/tree-sitter-language-pack/arduino-tags.scm @@ -0,0 +1,5 @@ +(function_declarator + declarator: (identifier) @name.definition.function) @definition.function + +(call_expression + function: (identifier) @name.reference.call) @reference.call diff --git a/aider/queries/tree-sitter-language-pack/c-tags.scm b/aider/queries/tree-sitter-language-pack/c-tags.scm new file mode 100644 index 000000000..1035aa224 --- /dev/null +++ b/aider/queries/tree-sitter-language-pack/c-tags.scm @@ -0,0 +1,9 @@ +(struct_specifier name: (type_identifier) @name.definition.class body:(_)) @definition.class + +(declaration type: (union_specifier name: (type_identifier) @name.definition.class)) @definition.class + +(function_declarator declarator: (identifier) @name.definition.function) @definition.function + +(type_definition declarator: (type_identifier) @name.definition.type) @definition.type + +(enum_specifier name: (type_identifier) @name.definition.type) @definition.type diff --git a/aider/queries/tree-sitter-language-pack/chatito-tags.scm b/aider/queries/tree-sitter-language-pack/chatito-tags.scm new file mode 100644 index 000000000..6fbac9420 --- /dev/null +++ b/aider/queries/tree-sitter-language-pack/chatito-tags.scm @@ -0,0 +1,16 @@ +; Definitions +(intent_def + (intent) @name.definition.intent) @definition.intent + +(slot_def + (slot) @name.definition.slot) @definition.slot + +(alias_def + (alias) @name.definition.alias) @definition.alias + +; References +(slot_ref + (slot) @name.reference.slot) @reference.slot + +(alias_ref + (alias) @name.reference.alias) @reference.alias diff --git a/aider/queries/tree-sitter-language-pack/commonlisp-tags.scm b/aider/queries/tree-sitter-language-pack/commonlisp-tags.scm new file mode 100644 index 000000000..a47dfeeda --- /dev/null +++ b/aider/queries/tree-sitter-language-pack/commonlisp-tags.scm @@ -0,0 +1,122 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; Function Definitions ;;;;;;;;;;;;;;;;;;;;;;; + +(defun_header + function_name: (sym_lit) @name.definition.function) @definition.function + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; Function Calls ;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; Basically, we consider every list literal with symbol as the +;;; first element to be a call to a function named by that element. +;;; But we must exclude some cases. Note, tree-sitter @ignore +;;; cases only work if they are declared before the cases +;;; we want to include. + +;; Exclude lambda lists for function definitions +;; For example: +;; +;; (defun my-func (arg1 arg2) ...) +;; +;; do not treat (arg1 arg2) as a call of function arg1 +;; +(defun_header + lambda_list: (list_lit . [(sym_lit) (package_lit)] @ignore)) + +;; Similar to the above, but for +;; +;; (defmethod m ((type1 param1) (type2 param2)) ...) +;; +;; where list literals having symbol as their first element +;; are nested inside the lambda list. +(defun_header + lambda_list: (list_lit (list_lit . [(sym_lit) (package_lit)] @ignore))) + +;; +;; (let ((var ...) (var2 ...)) ...) +;; +;; - exclude var, var2 +;; - the same for let*, flet, labels, macrolet, symbol-macrolet +(list_lit . [(sym_lit) (package_lit)] @name.reference.call + . (list_lit (list_lit . [(sym_lit) (package_lit)] @ignore)) + (#match? @name.reference.call + "(?i)^(cl:)?(let|let\\*|flet|labels|macrolet|symbol-macrolet)$") + ) + +;; TODO: +;; - exclude also: +;; - (defclass name (parent parent2) +;; ((slot1 ...) +;; (slot2 ...)) +;; exclude the parent, slot1, slot2 +;; - (flet ((func-1 (param1 param2))) ...) +;; - we already exclude func-1, but param1 is still recognized +;; as a function call - exclude it too +;; - the same for labels +;; - the same macrolet +;; - what else? +;; (that's a non-goal to completely support all macros +;; and special operators, but every one we support +;; makes the solution a little bit better) +;; - (flet ((func-1 (param1 param2))) ...) +;; - instead of simply excluding it, as we do today, +;; tag func-1 as @local.definition.function (I suppose) +;; - the same for labels, macrolet +;; - @local.scope for let, let*, flet, labels, macrolet +;; - I guess the whole span of the scope text, +;; till the closing paren, should be tagged as @local.scope; +;; Hopefully, combined with @local.definition.function +;; within the scope, the usual @reference.call within +;; that scope will refer to the local definition, +;; and there will be no need to use @local.reference.call +;; (which is more difficult to implement). +;; - When implementing, remember the scope rules differences +;; of let vs let*, flet vs labels. + + +;; Include all other cases - list literal with symbol as the +;; first element +(list_lit . [(sym_lit) (package_lit)] @name.reference.call) @reference.call + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; classes + +(list_lit . [(sym_lit) (package_lit)] @ignore + . [(sym_lit) (package_lit)] @name.definition.class + (#match? @ignore "(?i)^(cl:)?defclass$") + ) @definition.class + +(list_lit . [(sym_lit) (package_lit)] @ignore + . (quoting_lit [(sym_lit) (package_lit)] @name.reference.class) + (#match? @ignore "(?i)^(cl:)?make-instance$") + ) @reference.class + +;;; TODO: +;; - @reference.class for base classes + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; TODO: +;; - Symbols referenced in defpackage +;; +;; (defpackage ... +;; (:export (symbol-a :symbol-b #:symbol-c "SYMBOL-D"))) +;; +;; The goal is to allow quick navigation from the API +;; overview in the form of defpackage, to the definition +;; where user can read parameters, docstring, etc. +;; - The @name must not include the colon, or sharpsign colon, quotes, +;; just symbol-a, symbol-b, symbol-c, sybmol-d +;; - Downcase the names specified as string literals? +;; ("SYMBOL-D" -> symbol-d) +;; - We don't know if the exported symbol is a function, variable, +;; class or something else. The official doc +;; (https://tree-sitter.github.io/tree-sitter/code-navigation-systems) +;; does not even suggest a tag for variable reference. +;; (Although in practice, the `tree-sitter tags` command +;; allows any @reference.* and @definition.* tags) +;; Probably it's better to just use @reference.call for all +;; the symbols in the :export clause. +;; +;; - The same for the export function call: +;; +;; (export '(symbol-a :symbol-b #:symbol-c "SYMBOL-D")) diff --git a/aider/queries/tree-sitter-language-pack/cpp-tags.scm b/aider/queries/tree-sitter-language-pack/cpp-tags.scm new file mode 100644 index 000000000..00cc96637 --- /dev/null +++ b/aider/queries/tree-sitter-language-pack/cpp-tags.scm @@ -0,0 +1,15 @@ +(struct_specifier name: (type_identifier) @name.definition.class body:(_)) @definition.class + +(declaration type: (union_specifier name: (type_identifier) @name.definition.class)) @definition.class + +(function_declarator declarator: (identifier) @name.definition.function) @definition.function + +(function_declarator declarator: (field_identifier) @name.definition.function) @definition.function + +(function_declarator declarator: (qualified_identifier scope: (namespace_identifier) @local.scope name: (identifier) @name.definition.method)) @definition.method + +(type_definition declarator: (type_identifier) @name.definition.type) @definition.type + +(enum_specifier name: (type_identifier) @name.definition.type) @definition.type + +(class_specifier name: (type_identifier) @name.definition.class) @definition.class diff --git a/aider/queries/tree-sitter-language-pack/csharp-tags.scm b/aider/queries/tree-sitter-language-pack/csharp-tags.scm new file mode 100644 index 000000000..36ef49a29 --- /dev/null +++ b/aider/queries/tree-sitter-language-pack/csharp-tags.scm @@ -0,0 +1,26 @@ +; Based on https://github.com/tree-sitter/tree-sitter-c-sharp/blob/master/queries/tags.scm +; MIT License. + +(class_declaration name: (identifier) @name.definition.class) @definition.class + +(class_declaration (base_list (_) @name.reference.class)) @reference.class + +(interface_declaration name: (identifier) @name.definition.interface) @definition.interface + +(interface_declaration (base_list (_) @name.reference.interface)) @reference.interface + +(method_declaration name: (identifier) @name.definition.method) @definition.method + +(object_creation_expression type: (identifier) @name.reference.class) @reference.class + +(type_parameter_constraints_clause (identifier) @name.reference.class) @reference.class + +(type_parameter_constraint (type type: (identifier) @name.reference.class)) @reference.class + +(variable_declaration type: (identifier) @name.reference.class) @reference.class + +(invocation_expression function: (member_access_expression name: (identifier) @name.reference.send)) @reference.send + +(namespace_declaration name: (identifier) @name.definition.module) @definition.module + +(namespace_declaration name: (identifier) @name.definition.module) @module diff --git a/aider/queries/tree-sitter-language-pack/d-tags.scm b/aider/queries/tree-sitter-language-pack/d-tags.scm new file mode 100644 index 000000000..7572cc4a6 --- /dev/null +++ b/aider/queries/tree-sitter-language-pack/d-tags.scm @@ -0,0 +1,26 @@ +(module_def (module_declaration (module_fqn) @name.definition.module)) @definition.module + +(struct_declaration (struct) . (identifier) @name.definition.class) @definition.class +(interface_declaration (interface) . (identifier) @name.definition.interface) @definition.interface +(enum_declaration (enum) . (identifier) @name.definition.type) @definition.type + +(class_declaration (class) . (identifier) @name.definition.class) @definition.class +(constructor (this) @name.definition.method) @definition.method +(destructor (this) @name.definition.method) @definition.method +(postblit (this) @name.definition.method) @definition.method + +(manifest_declarator . (identifier) @name.definition.type) @definition.type + +(function_declaration (identifier) @name.definition.function) @definition.function + +(union_declaration (union) . (identifier) @name.definition.type) @definition.type + +(anonymous_enum_declaration (enum_member . (identifier) @name.definition.constant)) @definition.constant + +(enum_declaration (enum_member . (identifier) @name.definition.constant)) @definition.constant + +(call_expression (identifier) @name.reference.call) @reference.call +(call_expression (type (template_instance (identifier) @name.reference.call))) @reference.call +(parameter (type (identifier) @name.reference.class) @reference.class (identifier)) + +(variable_declaration (type (identifier) @name.reference.class) @reference.class (declarator)) diff --git a/aider/queries/tree-sitter-language-pack/dart-tags.scm b/aider/queries/tree-sitter-language-pack/dart-tags.scm new file mode 100644 index 000000000..a11fafcb1 --- /dev/null +++ b/aider/queries/tree-sitter-language-pack/dart-tags.scm @@ -0,0 +1,92 @@ + +(class_definition + name: (identifier) @name.definition.class) @definition.class + +(method_signature + (function_signature)) @definition.method + +(type_alias + (type_identifier) @name.definition.type) @definition.type + +(method_signature +(getter_signature + name: (identifier) @name.definition.method)) @definition.method + +(method_signature +(setter_signature + name: (identifier) @name.definition.method)) @definition.method + +(method_signature + (function_signature + name: (identifier) @name.definition.method)) @definition.method + +(method_signature + (factory_constructor_signature + (identifier) @name.definition.method)) @definition.method + +(method_signature + (constructor_signature + name: (identifier) @name.definition.method)) @definition.method + +(method_signature + (operator_signature)) @definition.method + +(method_signature) @definition.method + +(mixin_declaration + (mixin) + (identifier) @name.definition.mixin) @definition.mixin + +(extension_declaration + name: (identifier) @name.definition.extension) @definition.extension + + +(new_expression + (type_identifier) @name.reference.class) @reference.class + +(enum_declaration + name: (identifier) @name.definition.enum) @definition.enum + +(function_signature + name: (identifier) @name.definition.function) @definition.function + +(initialized_variable_definition + name: (identifier) + value: (identifier) @name.reference.class + value: (selector + "!"? + (argument_part + (arguments + (argument)*))?)?) @reference.class + +(assignment_expression + left: (assignable_expression + (identifier) + (unconditional_assignable_selector + "." + (identifier) @name.reference.send))) @reference.call + +(assignment_expression + left: (assignable_expression + (identifier) + (conditional_assignable_selector + "?." + (identifier) @name.reference.send))) @reference.call + +((identifier) @name.reference.send + (selector + "!"? + (conditional_assignable_selector + "?." (identifier) @name.reference.send)? + (unconditional_assignable_selector + "."? (identifier) @name.reference.send)? + (argument_part + (arguments + (argument)*))?)* + (cascade_section + (cascade_selector + (identifier)) @name.reference.send + (argument_part + (arguments + (argument)*))?)?) @reference.call + diff --git a/aider/queries/tree-sitter-language-pack/elisp-tags.scm b/aider/queries/tree-sitter-language-pack/elisp-tags.scm new file mode 100644 index 000000000..81e50d8e0 --- /dev/null +++ b/aider/queries/tree-sitter-language-pack/elisp-tags.scm @@ -0,0 +1,5 @@ +;; defun/defsubst +(function_definition name: (symbol) @name.definition.function) @definition.function + +;; Treat macros as function definitions for the sake of TAGS. +(macro_definition name: (symbol) @name.definition.function) @definition.function diff --git a/aider/queries/tree-sitter-language-pack/elixir-tags.scm b/aider/queries/tree-sitter-language-pack/elixir-tags.scm new file mode 100644 index 000000000..e0a351e32 --- /dev/null +++ b/aider/queries/tree-sitter-language-pack/elixir-tags.scm @@ -0,0 +1,54 @@ +; Definitions + +; * modules and protocols +(call + target: (identifier) @ignore + (arguments (alias) @name.definition.module) + (#any-of? @ignore "defmodule" "defprotocol")) @definition.module + +; * functions/macros +(call + target: (identifier) @ignore + (arguments + [ + ; zero-arity functions with no parentheses + (identifier) @name.definition.function + ; regular function clause + (call target: (identifier) @name.definition.function) + ; function clause with a guard clause + (binary_operator + left: (call target: (identifier) @name.definition.function) + operator: "when") + ]) + (#any-of? @ignore "def" "defp" "defdelegate" "defguard" "defguardp" "defmacro" "defmacrop" "defn" "defnp")) @definition.function + +; References + +; ignore calls to kernel/special-forms keywords +(call + target: (identifier) @ignore + (#any-of? @ignore "def" "defp" "defdelegate" "defguard" "defguardp" "defmacro" "defmacrop" "defn" "defnp" "defmodule" "defprotocol" "defimpl" "defstruct" "defexception" "defoverridable" "alias" "case" "cond" "else" "for" "if" "import" "quote" "raise" "receive" "require" "reraise" "super" "throw" "try" "unless" "unquote" "unquote_splicing" "use" "with")) + +; ignore module attributes +(unary_operator + operator: "@" + operand: (call + target: (identifier) @ignore)) + +; * function call +(call + target: [ + ; local + (identifier) @name.reference.call + ; remote + (dot + right: (identifier) @name.reference.call) + ]) @reference.call + +; * pipe into function call +(binary_operator + operator: "|>" + right: (identifier) @name.reference.call) @reference.call + +; * modules +(alias) @name.reference.module @reference.module diff --git a/aider/queries/tree-sitter-language-pack/elm-tags.scm b/aider/queries/tree-sitter-language-pack/elm-tags.scm new file mode 100644 index 000000000..c2e042763 --- /dev/null +++ b/aider/queries/tree-sitter-language-pack/elm-tags.scm @@ -0,0 +1,19 @@ +(value_declaration (function_declaration_left (lower_case_identifier) @name.definition.function)) @definition.function + +(function_call_expr (value_expr (value_qid) @name.reference.function)) @reference.function +(exposed_value (lower_case_identifier) @name.reference.function) @reference.function +(type_annotation ((lower_case_identifier) @name.reference.function) (colon)) @reference.function + +(type_declaration ((upper_case_identifier) @name.definition.type) ) @definition.type + +(type_ref (upper_case_qid (upper_case_identifier) @name.reference.type)) @reference.type +(exposed_type (upper_case_identifier) @name.reference.type) @reference.type + +(type_declaration (union_variant (upper_case_identifier) @name.definition.union)) @definition.union + +(value_expr (upper_case_qid (upper_case_identifier) @name.reference.union)) @reference.union + + +(module_declaration + (upper_case_qid (upper_case_identifier)) @name.definition.module +) @definition.module diff --git a/aider/queries/tree-sitter-language-pack/gleam-tags.scm b/aider/queries/tree-sitter-language-pack/gleam-tags.scm new file mode 100644 index 000000000..b1b934c20 --- /dev/null +++ b/aider/queries/tree-sitter-language-pack/gleam-tags.scm @@ -0,0 +1,41 @@ +; Modules +(module) @name.reference.module @reference.module +(import alias: (identifier) @name.reference.module) @reference.module +(remote_type_identifier + module: (identifier) @name.reference.module) @reference.module +((field_access + record: (identifier) @name.reference.module) + (#is-not? local)) @reference.module + +; Functions +(function + name: (identifier) @name.definition.function) @definition.function +(external_function + name: (identifier) @name.definition.function) @definition.function +(unqualified_import (identifier) @name.reference.function) @reference.function +((function_call + function: (identifier) @name.reference.function) @reference.function + (#is-not? local)) +((field_access + record: (identifier) @ignore + field: (label) @name.reference.function) + (#is-not? local)) @reference.function +((binary_expression + operator: "|>" + right: (identifier) @name.reference.function) + (#is-not? local)) @reference.function + +; Types +(type_definition + (type_name + name: (type_identifier) @name.definition.type)) @definition.type +(type_definition + (data_constructors + (data_constructor + name: (constructor_name) @name.definition.constructor))) @definition.constructor +(external_type + (type_name + name: (type_identifier) @name.definition.type)) @definition.type + +(type_identifier) @name.reference.type @reference.type +(constructor_name) @name.reference.constructor @reference.constructor diff --git a/aider/queries/tree-sitter-language-pack/go-tags.scm b/aider/queries/tree-sitter-language-pack/go-tags.scm new file mode 100644 index 000000000..16ecc4de8 --- /dev/null +++ b/aider/queries/tree-sitter-language-pack/go-tags.scm @@ -0,0 +1,42 @@ +( + (comment)* @doc + . + (function_declaration + name: (identifier) @name.definition.function) @definition.function + (#strip! @doc "^//\\s*") + (#set-adjacent! @doc @definition.function) +) + +( + (comment)* @doc + . + (method_declaration + name: (field_identifier) @name.definition.method) @definition.method + (#strip! @doc "^//\\s*") + (#set-adjacent! @doc @definition.method) +) + +(call_expression + function: [ + (identifier) @name.reference.call + (parenthesized_expression (identifier) @name.reference.call) + (selector_expression field: (field_identifier) @name.reference.call) + (parenthesized_expression (selector_expression field: (field_identifier) @name.reference.call)) + ]) @reference.call + +(type_spec + name: (type_identifier) @name.definition.type) @definition.type + +(type_identifier) @name.reference.type @reference.type + +(package_clause "package" (package_identifier) @name.definition.module) + +(type_declaration (type_spec name: (type_identifier) @name.definition.interface type: (interface_type))) + +(type_declaration (type_spec name: (type_identifier) @name.definition.class type: (struct_type))) + +(import_declaration (import_spec) @name.reference.module) + +(var_declaration (var_spec name: (identifier) @name.definition.variable)) + +(const_declaration (const_spec name: (identifier) @name.definition.constant)) diff --git a/aider/queries/tree-sitter-language-pack/java-tags.scm b/aider/queries/tree-sitter-language-pack/java-tags.scm new file mode 100644 index 000000000..ae4481e9e --- /dev/null +++ b/aider/queries/tree-sitter-language-pack/java-tags.scm @@ -0,0 +1,20 @@ +(class_declaration + name: (identifier) @name.definition.class) @definition.class + +(method_declaration + name: (identifier) @name.definition.method) @definition.method + +(method_invocation + name: (identifier) @name.reference.method + arguments: (argument_list) @reference.call) + +(interface_declaration + name: (identifier) @name.definition.interface) @definition.interface + +(type_list + (type_identifier) @name.reference.interface) @reference.implementation + +(object_creation_expression + type: (type_identifier) @name.reference.class) @reference.class + +(superclass (type_identifier) @name.reference.class) @reference.class diff --git a/aider/queries/tree-sitter-language-pack/lua-tags.scm b/aider/queries/tree-sitter-language-pack/lua-tags.scm new file mode 100644 index 000000000..0910cf153 --- /dev/null +++ b/aider/queries/tree-sitter-language-pack/lua-tags.scm @@ -0,0 +1,34 @@ +(function_declaration + name: [ + (identifier) @name.definition.function + (dot_index_expression + field: (identifier) @name.definition.function) + ]) @definition.function + +(function_declaration + name: (method_index_expression + method: (identifier) @name.definition.method)) @definition.method + +(assignment_statement + (variable_list . + name: [ + (identifier) @name.definition.function + (dot_index_expression + field: (identifier) @name.definition.function) + ]) + (expression_list . + value: (function_definition))) @definition.function + +(table_constructor + (field + name: (identifier) @name.definition.function + value: (function_definition))) @definition.function + +(function_call + name: [ + (identifier) @name.reference.call + (dot_index_expression + field: (identifier) @name.reference.call) + (method_index_expression + method: (identifier) @name.reference.method) + ]) @reference.call diff --git a/aider/queries/tree-sitter-language-pack/pony-tags.scm b/aider/queries/tree-sitter-language-pack/pony-tags.scm new file mode 100644 index 000000000..695f628ea --- /dev/null +++ b/aider/queries/tree-sitter-language-pack/pony-tags.scm @@ -0,0 +1,39 @@ +;Class definitions @definition.class +;Function definitions @definition.function +;Interface definitions @definition.interface +;Method definitions @definition.method +;Module definitions @definition.module +;Function/method calls @reference.call +;Class reference @reference.class +;Interface implementation @reference.implementation +( + (identifier) @reference.class + (#match? @reference.class "^_*[A-Z][a-zA-Z0-9_]*$") +) + +(class_definition (identifier) @name.definition.class) @definition.class +(actor_definition (identifier) @name.definition.class) @definition.class +(primitive_definition (identifier) @name.definition.class) @definition.class +(struct_definition (identifier) @name.definition.class) @definition.class +(type_alias (identifier) @name.definition.class) @definition.class + +(trait_definition (identifier) @name.definition.interface) @definition.interface +(interface_definition (identifier) @name.definition.interface) @definition.interface + +(constructor (identifier) @name.definition.method) @definition.method +(method (identifier) @name.definition.method) @definition.method +(behavior (identifier) @name.definition.method) @definition.method + +(class_definition (type) @name.reference.implementation) @reference.implementation +(actor_definition (type) @name.reference.implementation) @reference.implementation +(primitive_definition (type) @name.reference.implementation) @reference.implementation +(struct_definition (type) @name.reference.implementation) @reference.implementation +(type_alias (type) @name.reference.implementation) @reference.implementation + +; calls - not catching all possible call cases of callees for capturing the method name +(call_expression callee: [(identifier) (ffi_identifier)] @name.reference.call) @reference.call +(call_expression callee: (generic_expression [(identifier) (ffi_identifier)] @name.reference.call)) @reference.call +(call_expression callee: (member_expression (identifier) @name.reference.call .)) @reference.call +(call_expression callee: (member_expression (generic_expression [(identifier) (ffi_identifier)] @name.reference.call) .)) @reference.call +; TODO: add more possible callee expressions +(call_expression) @reference.call diff --git a/aider/queries/tree-sitter-language-pack/properties-tags.scm b/aider/queries/tree-sitter-language-pack/properties-tags.scm new file mode 100644 index 000000000..1d70c6a7f --- /dev/null +++ b/aider/queries/tree-sitter-language-pack/properties-tags.scm @@ -0,0 +1,5 @@ +(property + (key) @name.definition.property) @definition.property + +(substitution + (key) @name.reference.property) @reference.property diff --git a/aider/queries/tree-sitter-language-pack/python-tags.scm b/aider/queries/tree-sitter-language-pack/python-tags.scm new file mode 100644 index 000000000..dab8b941d --- /dev/null +++ b/aider/queries/tree-sitter-language-pack/python-tags.scm @@ -0,0 +1,14 @@ +(module (expression_statement (assignment left: (identifier) @name.definition.constant) @definition.constant)) + +(class_definition + name: (identifier) @name.definition.class) @definition.class + +(function_definition + name: (identifier) @name.definition.function) @definition.function + +(call + function: [ + (identifier) @name.reference.call + (attribute + attribute: (identifier) @name.reference.call) + ]) @reference.call diff --git a/aider/queries/tree-sitter-language-pack/r-tags.scm b/aider/queries/tree-sitter-language-pack/r-tags.scm new file mode 100644 index 000000000..5ffc72332 --- /dev/null +++ b/aider/queries/tree-sitter-language-pack/r-tags.scm @@ -0,0 +1,21 @@ +(binary_operator + lhs: (identifier) @name.definition.function + operator: "<-" + rhs: (function_definition) +) @definition.function + +(binary_operator + lhs: (identifier) @name.definition.function + operator: "=" + rhs: (function_definition) +) @definition.function + +(call + function: (identifier) @name.reference.call +) @reference.call + +(call + function: (namespace_operator + rhs: (identifier) @name.reference.call + ) +) @reference.call diff --git a/aider/queries/tree-sitter-language-pack/racket-tags.scm b/aider/queries/tree-sitter-language-pack/racket-tags.scm new file mode 100644 index 000000000..b3034026c --- /dev/null +++ b/aider/queries/tree-sitter-language-pack/racket-tags.scm @@ -0,0 +1,12 @@ +(list + . + (symbol) @reference._define + (#match? @reference._define "^(define|define/contract)$") + . + (list + . + (symbol) @name.definition.function) @definition.function) + +(list + . + (symbol) @name.reference.call) diff --git a/aider/queries/tree-sitter-language-pack/ruby-tags.scm b/aider/queries/tree-sitter-language-pack/ruby-tags.scm new file mode 100644 index 000000000..79e71d2d6 --- /dev/null +++ b/aider/queries/tree-sitter-language-pack/ruby-tags.scm @@ -0,0 +1,64 @@ +; Method definitions + +( + (comment)* @doc + . + [ + (method + name: (_) @name.definition.method) @definition.method + (singleton_method + name: (_) @name.definition.method) @definition.method + ] + (#strip! @doc "^#\\s*") + (#select-adjacent! @doc @definition.method) +) + +(alias + name: (_) @name.definition.method) @definition.method + +(setter + (identifier) @ignore) + +; Class definitions + +( + (comment)* @doc + . + [ + (class + name: [ + (constant) @name.definition.class + (scope_resolution + name: (_) @name.definition.class) + ]) @definition.class + (singleton_class + value: [ + (constant) @name.definition.class + (scope_resolution + name: (_) @name.definition.class) + ]) @definition.class + ] + (#strip! @doc "^#\\s*") + (#select-adjacent! @doc @definition.class) +) + +; Module definitions + +( + (module + name: [ + (constant) @name.definition.module + (scope_resolution + name: (_) @name.definition.module) + ]) @definition.module +) + +; Calls + +(call method: (identifier) @name.reference.call) @reference.call + +( + [(identifier) (constant)] @name.reference.call @reference.call + (#is-not? local) + (#not-match? @name.reference.call "^(lambda|load|require|require_relative|__FILE__|__LINE__)$") +) diff --git a/aider/queries/tree-sitter-language-pack/rust-tags.scm b/aider/queries/tree-sitter-language-pack/rust-tags.scm new file mode 100644 index 000000000..0888cc0d8 --- /dev/null +++ b/aider/queries/tree-sitter-language-pack/rust-tags.scm @@ -0,0 +1,60 @@ +; ADT definitions + +(struct_item + name: (type_identifier) @name.definition.class) @definition.class + +(enum_item + name: (type_identifier) @name.definition.class) @definition.class + +(union_item + name: (type_identifier) @name.definition.class) @definition.class + +; type aliases + +(type_item + name: (type_identifier) @name.definition.class) @definition.class + +; method definitions + +(declaration_list + (function_item + name: (identifier) @name.definition.method) @definition.method) + +; function definitions + +(function_item + name: (identifier) @name.definition.function) @definition.function + +; trait definitions +(trait_item + name: (type_identifier) @name.definition.interface) @definition.interface + +; module definitions +(mod_item + name: (identifier) @name.definition.module) @definition.module + +; macro definitions + +(macro_definition + name: (identifier) @name.definition.macro) @definition.macro + +; references + +(call_expression + function: (identifier) @name.reference.call) @reference.call + +(call_expression + function: (field_expression + field: (field_identifier) @name.reference.call)) @reference.call + +(macro_invocation + macro: (identifier) @name.reference.call) @reference.call + +; implementations + +(impl_item + trait: (type_identifier) @name.reference.implementation) @reference.implementation + +(impl_item + type: (type_identifier) @name.reference.implementation + !trait) @reference.implementation diff --git a/aider/queries/tree-sitter-language-pack/solidity-tags.scm b/aider/queries/tree-sitter-language-pack/solidity-tags.scm new file mode 100644 index 000000000..d56bc19a0 --- /dev/null +++ b/aider/queries/tree-sitter-language-pack/solidity-tags.scm @@ -0,0 +1,43 @@ +;; Method and Function declarations +(contract_declaration (_ + (function_definition + name: (identifier) @name.definition.function) @definition.method)) + +(source_file + (function_definition + name: (identifier) @name.definition.function) @definition.function) + +;; Contract, struct, enum and interface declarations +(contract_declaration + name: (identifier) @name.definition.class) @definition.class + +(interface_declaration + name: (identifier) @name.definition.interface) @definition.interface + +(library_declaration + name: (identifier) @name.definition.class) @definition.interface + +(struct_declaration name: (identifier) @name.definition.class) @definition.class +(enum_declaration name: (identifier) @name.definition.class) @definition.class +(event_definition name: (identifier) @name.definition.class) @definition.class + +;; Function calls +(call_expression (expression (identifier)) @name.reference.call ) @reference.call + +(call_expression + (expression (member_expression + property: (_) @name.reference.method ))) @reference.call + +;; Log emit +(emit_statement name: (_) @name.reference.class) @reference.class + + +;; Inheritance + +(inheritance_specifier + ancestor: (user_defined_type (_) @name.reference.class . )) @reference.class + + +;; Imports ( note that unknown is not standardised ) +(import_directive + import_name: (_) @name.reference.module ) @reference.unknown diff --git a/aider/queries/tree-sitter-language-pack/swift-tags.scm b/aider/queries/tree-sitter-language-pack/swift-tags.scm new file mode 100644 index 000000000..9b81cf7bd --- /dev/null +++ b/aider/queries/tree-sitter-language-pack/swift-tags.scm @@ -0,0 +1,51 @@ +(class_declaration + name: (type_identifier) @name.definition.class) @definition.class + +(protocol_declaration + name: (type_identifier) @name.definition.interface) @definition.interface + +(class_declaration + (class_body + [ + (function_declaration + name: (simple_identifier) @name.definition.method + ) + (subscript_declaration + (parameter (simple_identifier) @name.definition.method) + ) + (init_declaration "init" @name.definition.method) + (deinit_declaration "deinit" @name.definition.method) + ] + ) +) @definition.method + +(protocol_declaration + (protocol_body + [ + (protocol_function_declaration + name: (simple_identifier) @name.definition.method + ) + (subscript_declaration + (parameter (simple_identifier) @name.definition.method) + ) + (init_declaration "init" @name.definition.method) + ] + ) +) @definition.method + +(class_declaration + (class_body + [ + (property_declaration + (pattern (simple_identifier) @name.definition.property) + ) + ] + ) +) @definition.property + +(property_declaration + (pattern (simple_identifier) @name.definition.property) +) @definition.property + +(function_declaration + name: (simple_identifier) @name.definition.function) @definition.function diff --git a/aider/queries/tree-sitter-language-pack/udev-tags.scm b/aider/queries/tree-sitter-language-pack/udev-tags.scm new file mode 100644 index 000000000..a3a60b569 --- /dev/null +++ b/aider/queries/tree-sitter-language-pack/udev-tags.scm @@ -0,0 +1,20 @@ +(assignment + key: "LABEL" + (value + (content) @name.definition.label)) @definition.label + +(assignment + key: "GOTO" + (value + (content) @name.reference.label)) @reference.label + +(assignment + key: "ENV" + (env_var) @name.definition.variable) @definition.variable + +(match + key: "ENV" + (env_var) @name.reference.variable) @reference.variable + +(var_sub + (env_var) @name.reference.variable) @reference.variable diff --git a/aider/queries/tree-sitter-languages/scala-tags.scm b/aider/queries/tree-sitter-languages/scala-tags.scm new file mode 100644 index 000000000..4bf3953ff --- /dev/null +++ b/aider/queries/tree-sitter-languages/scala-tags.scm @@ -0,0 +1,65 @@ +; Definitions + +(package_clause + name: (package_identifier) @name.definition.module) @definition.module + +(trait_definition + name: (identifier) @name.definition.interface) @definition.interface + +(enum_definition + name: (identifier) @name.definition.enum) @definition.enum + +(simple_enum_case + name: (identifier) @name.definition.class) @definition.class + +(full_enum_case + name: (identifier) @name.definition.class) @definition.class + +(class_definition + name: (identifier) @name.definition.class) @definition.class + +(object_definition + name: (identifier) @name.definition.object) @definition.object + +(function_definition + name: (identifier) @name.definition.function) @definition.function + +(val_definition + pattern: (identifier) @name.definition.variable) @definition.variable + +(given_definition + name: (identifier) @name.definition.variable) @definition.variable + +(var_definition + pattern: (identifier) @name.definition.variable) @definition.variable + +(val_declaration + name: (identifier) @name.definition.variable) @definition.variable + +(var_declaration + name: (identifier) @name.definition.variable) @definition.variable + +(type_definition + name: (type_identifier) @name.definition.type) @definition.type + +(class_parameter + name: (identifier) @name.definition.property) @definition.property + +; References + +(call_expression + (identifier) @name.reference.call) @reference.call + +(instance_expression + (type_identifier) @name.reference.interface) @reference.interface + +(instance_expression + (generic_type + (type_identifier) @name.reference.interface)) @reference.interface + +(extends_clause + (type_identifier) @name.reference.class) @reference.class + +(extends_clause + (generic_type + (type_identifier) @name.reference.class)) @reference.class diff --git a/aider/reasoning_tags.py b/aider/reasoning_tags.py new file mode 100644 index 000000000..e87922383 --- /dev/null +++ b/aider/reasoning_tags.py @@ -0,0 +1,82 @@ +#!/usr/bin/env python + +import re + +from aider.dump import dump # noqa + +# Standard tag identifier +REASONING_TAG = "thinking-content-" + "7bbeb8e1441453ad999a0bbba8a46d4b" +# Output formatting +REASONING_START = "--------------\n► **THINKING**" +REASONING_END = "------------\n► **ANSWER**" + + +def remove_reasoning_content(res, reasoning_tag): + """ + Remove reasoning content from text based on tags. + + Args: + res (str): The text to process + reasoning_tag (str): The tag name to remove + + Returns: + str: Text with reasoning content removed + """ + if not reasoning_tag: + return res + + # Try to match the complete tag pattern first + pattern = f"<{reasoning_tag}>.*?" + res = re.sub(pattern, "", res, flags=re.DOTALL).strip() + + # If closing tag exists but opening tag might be missing, remove everything before closing + # tag + closing_tag = f"" + if closing_tag in res: + # Split on the closing tag and keep everything after it + parts = res.split(closing_tag, 1) + res = parts[1].strip() if len(parts) > 1 else res + + return res + + +def replace_reasoning_tags(text, tag_name): + """ + Replace opening and closing reasoning tags with standard formatting. + Ensures exactly one blank line before START and END markers. + + Args: + text (str): The text containing the tags + tag_name (str): The name of the tag to replace + + Returns: + str: Text with reasoning tags replaced with standard format + """ + if not text: + return text + + # Replace opening tag with proper spacing + text = re.sub(f"\\s*<{tag_name}>\\s*", f"\n{REASONING_START}\n\n", text) + + # Replace closing tag with proper spacing + text = re.sub(f"\\s*\\s*", f"\n\n{REASONING_END}\n\n", text) + + return text + + +def format_reasoning_content(reasoning_content, tag_name): + """ + Format reasoning content with appropriate tags. + + Args: + reasoning_content (str): The content to format + tag_name (str): The tag name to use + + Returns: + str: Formatted reasoning content with tags + """ + if not reasoning_content: + return "" + + formatted = f"<{tag_name}>\n\n{reasoning_content}\n\n" + return formatted diff --git a/aider/repo.py b/aider/repo.py index a46a9f412..5ece5147c 100644 --- a/aider/repo.py +++ b/aider/repo.py @@ -9,6 +9,7 @@ try: git.exc.ODBError, git.exc.GitError, git.exc.InvalidGitRepositoryError, + git.exc.GitCommandNotFound, ] except ImportError: git = None @@ -56,6 +57,7 @@ class GitRepo: attribute_commit_message_committer=False, commit_prompt=None, subtree_only=False, + git_commit_verify=True, ): self.io = io self.models = models @@ -69,6 +71,7 @@ class GitRepo: self.attribute_commit_message_committer = attribute_commit_message_committer self.commit_prompt = commit_prompt self.subtree_only = subtree_only + self.git_commit_verify = git_commit_verify self.ignore_file_cache = {} if git_dname: @@ -133,7 +136,9 @@ class GitRepo: # if context: # full_commit_message += "\n\n# Aider chat conversation:\n\n" + context - cmd = ["-m", full_commit_message, "--no-verify"] + cmd = ["-m", full_commit_message] + if not self.git_commit_verify: + cmd.append("--no-verify") if fnames: fnames = [str(self.abs_root_path(fn)) for fn in fnames] for fname in fnames: @@ -145,7 +150,7 @@ class GitRepo: else: cmd += ["-a"] - original_user_name = self.repo.config_reader().get_value("user", "name") + original_user_name = self.repo.git.config("--get", "user.name") original_committer_name_env = os.environ.get("GIT_COMMITTER_NAME") committer_name = f"{original_user_name} (aider)" @@ -289,13 +294,19 @@ class GitRepo: else: try: iterator = commit.tree.traverse() + blob = None # Initialize blob while True: try: blob = next(iterator) if blob.type == "blob": # blob is a file files.add(blob.path) except IndexError: - self.io.tool_warning(f"GitRepo: read error skipping {blob.path}") + # Handle potential index error during tree traversal + # without relying on potentially unassigned 'blob' + self.io.tool_warning( + "GitRepo: Index error encountered while reading git tree object." + " Skipping." + ) continue except StopIteration: break @@ -309,8 +320,11 @@ class GitRepo: # Add staged files index = self.repo.index - staged_files = [path for path, _ in index.entries.keys()] - files.update(self.normalize_path(path) for path in staged_files) + try: + staged_files = [path for path, _ in index.entries.keys()] + files.update(self.normalize_path(path) for path in staged_files) + except ANY_GIT_ERROR as err: + self.io.tool_error(f"Unable to read staged files: {err}") res = [fname for fname in files if not self.ignored_file(fname)] diff --git a/aider/repomap.py b/aider/repomap.py index 750bd6529..598770d18 100644 --- a/aider/repomap.py +++ b/aider/repomap.py @@ -398,13 +398,30 @@ class RepoMap: # dump(fname) rel_fname = self.get_rel_fname(fname) + current_pers = 0.0 # Start with 0 personalization score if fname in chat_fnames: - personalization[rel_fname] = personalize + current_pers += personalize chat_rel_fnames.add(rel_fname) if rel_fname in mentioned_fnames: - personalization[rel_fname] = personalize + # Use max to avoid double counting if in chat_fnames and mentioned_fnames + current_pers = max(current_pers, personalize) + + # Check path components against mentioned_idents + path_obj = Path(rel_fname) + path_components = set(path_obj.parts) + basename_with_ext = path_obj.name + basename_without_ext, _ = os.path.splitext(basename_with_ext) + components_to_check = path_components.union({basename_with_ext, basename_without_ext}) + + matched_idents = components_to_check.intersection(mentioned_idents) + if matched_idents: + # Add personalization *once* if any path component matches a mentioned ident + current_pers += personalize + + if current_pers > 0: + personalization[rel_fname] = current_pers # Assign the final calculated value tags = list(self.get_tags(fname, rel_fname)) if tags is None: @@ -431,17 +448,33 @@ class RepoMap: G = nx.MultiDiGraph() + # Add a small self-edge for every definition that has no references + # Helps with tree-sitter 0.23.2 with ruby, where "def greet(name)" + # isn't counted as a def AND a ref. tree-sitter 0.24.0 does. + for ident in defines.keys(): + if ident in references: + continue + for definer in defines[ident]: + G.add_edge(definer, definer, weight=0.1, ident=ident) + for ident in idents: if progress: progress() definers = defines[ident] + + mul = 1.0 + + is_snake = ("_" in ident) and any(c.isalpha() for c in ident) + is_camel = any(c.isupper() for c in ident) and any(c.islower() for c in ident) if ident in mentioned_idents: - mul = 10 - elif ident.startswith("_"): - mul = 0.1 - else: - mul = 1 + mul *= 10 + if (is_snake or is_camel) and len(ident) >= 8: + mul *= 10 + if ident.startswith("_"): + mul *= 0.1 + if len(defines[ident]) > 5: + mul *= 0.1 for referencer, num_refs in Counter(references[ident]).items(): for definer in definers: @@ -449,10 +482,14 @@ class RepoMap: # if referencer == definer: # continue + use_mul = mul + if referencer in chat_rel_fnames: + use_mul *= 50 + # scale down so high freq (low value) mentions don't dominate num_refs = math.sqrt(num_refs) - G.add_edge(referencer, definer, weight=mul * num_refs, ident=ident) + G.add_edge(referencer, definer, weight=use_mul * num_refs, ident=ident) if not references: pass diff --git a/aider/resources/model-metadata.json b/aider/resources/model-metadata.json index 13585282f..336c6bee8 100644 --- a/aider/resources/model-metadata.json +++ b/aider/resources/model-metadata.json @@ -15,22 +15,6 @@ //"supports_tool_choice": true, "supports_prompt_caching": true }, - "openrouter/deepseek/deepseek-r1": { - "max_tokens": 8192, - "max_input_tokens": 64000, - "max_output_tokens": 8192, - "input_cost_per_token": 0.00000055, - "input_cost_per_token_cache_hit": 0.00000014, - "cache_read_input_token_cost": 0.00000014, - "cache_creation_input_token_cost": 0.0, - "output_cost_per_token": 0.00000219, - "litellm_provider": "openrouter", - "mode": "chat", - //"supports_function_calling": true, - "supports_assistant_prefill": true, - //"supports_tool_choice": true, - "supports_prompt_caching": true - }, "openrouter/deepseek/deepseek-r1:free": { "max_tokens": 8192, "max_input_tokens": 64000, @@ -47,6 +31,49 @@ //"supports_tool_choice": true, "supports_prompt_caching": true }, + "openrouter/deepseek/deepseek-chat:free": { + "max_tokens": 8192, + "max_input_tokens": 64000, + "max_output_tokens": 8192, + "input_cost_per_token": 0.0, + "input_cost_per_token_cache_hit": 0.0, + "cache_read_input_token_cost": 0.00, + "cache_creation_input_token_cost": 0.0, + "output_cost_per_token": 0.0, + "litellm_provider": "openrouter", + "mode": "chat", + //"supports_function_calling": true, + "supports_assistant_prefill": true, + //"supports_tool_choice": true, + "supports_prompt_caching": true + }, + "openrouter/deepseek/deepseek-chat-v3-0324": { + "max_tokens": 8192, + "max_input_tokens": 64000, + "max_output_tokens": 8192, + "input_cost_per_token": 0.00000055, + "input_cost_per_token_cache_hit": 0.00000014, + "cache_read_input_token_cost": 0.00000014, + "cache_creation_input_token_cost": 0.0, + "output_cost_per_token": 0.00000219, + "litellm_provider": "openrouter", + "mode": "chat", + //"supports_function_calling": true, + "supports_assistant_prefill": true, + //"supports_tool_choice": true, + "supports_prompt_caching": true + }, + "openrouter/deepseek/deepseek-chat-v3-0324:free": { + "max_tokens": 131072, + "max_input_tokens": 131072, + "max_output_tokens": 131072, + "input_cost_per_token": 0, + "output_cost_per_token": 0, + "litellm_provider": "openrouter", + "supports_prompt_caching": true, + "mode": "chat", + "supports_tool_choice": true + }, "fireworks_ai/accounts/fireworks/models/deepseek-r1": { "max_tokens": 160000, "max_input_tokens": 128000, @@ -56,8 +83,8 @@ "output_cost_per_token": 0.000008, "mode": "chat", }, - "fireworks_ai/accounts/fireworks/models/deepseek-v3": { - "max_tokens": 128000, + "fireworks_ai/accounts/fireworks/models/deepseek-v3-0324": { + "max_tokens": 160000, "max_input_tokens": 100000, "max_output_tokens": 8192, "litellm_provider": "fireworks_ai", @@ -65,53 +92,25 @@ "output_cost_per_token": 0.0000009, "mode": "chat", }, - "o3-mini": { - "max_tokens": 100000, - "max_input_tokens": 200000, - "max_output_tokens": 100000, - "input_cost_per_token": 0.0000011, - "output_cost_per_token": 0.0000044, - "cache_read_input_token_cost": 0.00000055, - "litellm_provider": "openai", - "mode": "chat", - "supports_function_calling": true, - "supports_parallel_function_calling": true, - "supports_vision": true, - "supports_prompt_caching": true, - "supports_system_messages": true, - "supports_response_schema": true - }, - "openrouter/openai/o3-mini": { - "max_tokens": 100000, - "max_input_tokens": 200000, - "max_output_tokens": 100000, - "input_cost_per_token": 0.0000011, - "output_cost_per_token": 0.0000044, - "cache_read_input_token_cost": 0.00000055, + "openrouter/openrouter/quasar-alpha": { + "max_input_tokens": 1000000, + "max_output_tokens": 32000, + "input_cost_per_token": 0.0, + "output_cost_per_token": 0.0, "litellm_provider": "openrouter", "mode": "chat", - "supports_function_calling": true, - "supports_parallel_function_calling": true, "supports_vision": true, - "supports_prompt_caching": true, + "supports_function_calling": true, "supports_system_messages": true, - "supports_response_schema": true + "supports_prompt_caching": true }, - "openrouter/openai/o3-mini-high": { - "max_tokens": 100000, - "max_input_tokens": 200000, - "max_output_tokens": 100000, - "input_cost_per_token": 0.0000011, - "output_cost_per_token": 0.0000044, - "cache_read_input_token_cost": 0.00000055, + "openrouter/openrouter/optimus-alpha": { + "max_input_tokens": 1000000, + "max_output_tokens": 32000, + "input_cost_per_token": 0.0, + "output_cost_per_token": 0.0, "litellm_provider": "openrouter", - "mode": "chat", - "supports_function_calling": true, - "supports_parallel_function_calling": true, - "supports_vision": true, - "supports_prompt_caching": true, - "supports_system_messages": true, - "supports_response_schema": true + "mode": "chat" }, "openrouter/openai/gpt-4o-mini": { "max_tokens": 16384, @@ -131,26 +130,6 @@ "supports_prompt_caching": true, "supports_system_messages": true }, - "claude-3-7-sonnet-20250219": { - "max_tokens": 8192, - "max_input_tokens": 200000, - "max_output_tokens": 8192, - "input_cost_per_token": 0.000003, - "output_cost_per_token": 0.000015, - "cache_creation_input_token_cost": 0.00000375, - "cache_read_input_token_cost": 0.0000003, - "litellm_provider": "anthropic", - "mode": "chat", - "supports_function_calling": true, - "supports_vision": true, - "tool_use_system_prompt_tokens": 159, - "supports_assistant_prefill": true, - "supports_pdf_input": true, - "supports_prompt_caching": true, - "supports_response_schema": true, - "deprecation_date": "2025-10-01", - "supports_tool_choice": true - }, "anthropic/claude-3-7-sonnet-20250219": { "max_tokens": 8192, "max_input_tokens": 200000, @@ -171,43 +150,6 @@ "deprecation_date": "2025-10-01", "supports_tool_choice": true }, - "openrouter/anthropic/claude-3.7-sonnet": { - "max_tokens": 8192, - "max_input_tokens": 200000, - "max_output_tokens": 8192, - "input_cost_per_token": 0.000003, - "output_cost_per_token": 0.000015, - "cache_creation_input_token_cost": 0.00000375, - "cache_read_input_token_cost": 0.0000003, - "litellm_provider": "openrouter", - "mode": "chat", - "supports_function_calling": true, - "supports_vision": true, - "tool_use_system_prompt_tokens": 159, - "supports_assistant_prefill": true, - "supports_pdf_input": true, - "supports_prompt_caching": true, - "supports_response_schema": true, - "deprecation_date": "2025-10-01", - "supports_tool_choice": true - }, - "gpt-4.5-preview": { - "max_tokens": 16384, - "max_input_tokens": 128000, - "max_output_tokens": 16384, - "input_cost_per_token": 0.000075, - "output_cost_per_token": 0.00015, - "cache_read_input_token_cost": 0.0000375, - "litellm_provider": "openai", - "mode": "chat", - "supports_function_calling": true, - "supports_parallel_function_calling": true, - "supports_response_schema": true, - "supports_vision": true, - "supports_prompt_caching": true, - "supports_system_messages": true, - "supports_tool_choice": true - }, "openai/gpt-4.5-preview": { "max_tokens": 16384, "max_input_tokens": 128000, @@ -225,4 +167,240 @@ "supports_system_messages": true, "supports_tool_choice": true }, + "gemini/gemini-2.5-pro-exp-03-25": { + "max_tokens": 8192, + "max_input_tokens": 1048576, + "max_output_tokens": 64000, + "max_images_per_prompt": 3000, + "max_videos_per_prompt": 10, + "max_video_length": 1, + "max_audio_length_hours": 8.4, + "max_audio_per_prompt": 1, + "max_pdf_size_mb": 30, + "input_cost_per_image": 0, + "input_cost_per_video_per_second": 0, + "input_cost_per_audio_per_second": 0, + "input_cost_per_token": 0, + "input_cost_per_character": 0, + "input_cost_per_token_above_128k_tokens": 0, + "input_cost_per_character_above_128k_tokens": 0, + "input_cost_per_image_above_128k_tokens": 0, + "input_cost_per_video_per_second_above_128k_tokens": 0, + "input_cost_per_audio_per_second_above_128k_tokens": 0, + "output_cost_per_token": 0, + "output_cost_per_character": 0, + "output_cost_per_token_above_128k_tokens": 0, + "output_cost_per_character_above_128k_tokens": 0, + //"litellm_provider": "vertex_ai-language-models", + "litellm_provider": "gemini", + "mode": "chat", + "supports_system_messages": true, + "supports_function_calling": true, + "supports_vision": true, + "supports_audio_input": true, + "supports_video_input": true, + "supports_pdf_input": true, + "supports_response_schema": true, + "supports_tool_choice": true, + "source": "https://cloud.google.com/vertex-ai/generative-ai/pricing" + }, + "vertex_ai/gemini-2.5-pro-exp-03-25": { + "max_tokens": 8192, + "max_input_tokens": 1048576, + "max_output_tokens": 64000, + "max_images_per_prompt": 3000, + "max_videos_per_prompt": 10, + "max_video_length": 1, + "max_audio_length_hours": 8.4, + "max_audio_per_prompt": 1, + "max_pdf_size_mb": 30, + "input_cost_per_image": 0, + "input_cost_per_video_per_second": 0, + "input_cost_per_audio_per_second": 0, + "input_cost_per_token": 0, + "input_cost_per_character": 0, + "input_cost_per_token_above_128k_tokens": 0, + "input_cost_per_character_above_128k_tokens": 0, + "input_cost_per_image_above_128k_tokens": 0, + "input_cost_per_video_per_second_above_128k_tokens": 0, + "input_cost_per_audio_per_second_above_128k_tokens": 0, + "output_cost_per_token": 0, + "output_cost_per_character": 0, + "output_cost_per_token_above_128k_tokens": 0, + "output_cost_per_character_above_128k_tokens": 0, + "litellm_provider": "vertex_ai-language-models", + "mode": "chat", + "supports_system_messages": true, + "supports_function_calling": true, + "supports_vision": true, + "supports_audio_input": true, + "supports_video_input": true, + "supports_pdf_input": true, + "supports_response_schema": true, + "supports_tool_choice": true, + "source": "https://cloud.google.com/vertex-ai/generative-ai/pricing" + }, + "vertex_ai/gemini-2.5-pro-preview-03-25": { + "max_tokens": 8192, + "max_input_tokens": 1048576, + "max_output_tokens": 64000, + "max_images_per_prompt": 3000, + "max_videos_per_prompt": 10, + "max_video_length": 1, + "max_audio_length_hours": 8.4, + "max_audio_per_prompt": 1, + "max_pdf_size_mb": 30, + "input_cost_per_image": 0, + "input_cost_per_video_per_second": 0, + "input_cost_per_audio_per_second": 0, + "input_cost_per_token": 0.00000125, + "input_cost_per_character": 0, + "input_cost_per_token_above_128k_tokens": 0, + "input_cost_per_character_above_128k_tokens": 0, + "input_cost_per_image_above_128k_tokens": 0, + "input_cost_per_video_per_second_above_128k_tokens": 0, + "input_cost_per_audio_per_second_above_128k_tokens": 0, + "output_cost_per_token": 0.000010, + "output_cost_per_character": 0, + "output_cost_per_token_above_128k_tokens": 0, + "output_cost_per_character_above_128k_tokens": 0, + "litellm_provider": "vertex_ai-language-models", + "mode": "chat", + "supports_system_messages": true, + "supports_function_calling": true, + "supports_vision": true, + "supports_audio_input": true, + "supports_video_input": true, + "supports_pdf_input": true, + "supports_response_schema": true, + "supports_tool_choice": true, + "source": "https://cloud.google.com/vertex-ai/generative-ai/pricing" + }, + "openrouter/google/gemini-2.5-pro-preview-03-25": { + "max_tokens": 8192, + "max_input_tokens": 1048576, + "max_output_tokens": 64000, + "max_images_per_prompt": 3000, + "max_videos_per_prompt": 10, + "max_video_length": 1, + "max_audio_length_hours": 8.4, + "max_audio_per_prompt": 1, + "max_pdf_size_mb": 30, + "input_cost_per_image": 0, + "input_cost_per_video_per_second": 0, + "input_cost_per_audio_per_second": 0, + "input_cost_per_token": 0.00000125, + "input_cost_per_character": 0, + "input_cost_per_token_above_128k_tokens": 0, + "input_cost_per_character_above_128k_tokens": 0, + "input_cost_per_image_above_128k_tokens": 0, + "input_cost_per_video_per_second_above_128k_tokens": 0, + "input_cost_per_audio_per_second_above_128k_tokens": 0, + "output_cost_per_token": 0.000010, + "output_cost_per_character": 0, + "output_cost_per_token_above_128k_tokens": 0, + "output_cost_per_character_above_128k_tokens": 0, + "litellm_provider": "vertex_ai-language-models", + "mode": "chat", + "supports_system_messages": true, + "supports_function_calling": true, + "supports_vision": true, + "supports_audio_input": true, + "supports_video_input": true, + "supports_pdf_input": true, + "supports_response_schema": true, + "supports_tool_choice": true, + "source": "https://cloud.google.com/vertex-ai/generative-ai/pricing" + }, + "openrouter/google/gemini-2.5-pro-exp-03-25:free": { + "max_tokens": 8192, + "max_input_tokens": 1048576, + "max_output_tokens": 64000, + "max_images_per_prompt": 3000, + "max_videos_per_prompt": 10, + "max_video_length": 1, + "max_audio_length_hours": 8.4, + "max_audio_per_prompt": 1, + "max_pdf_size_mb": 30, + "input_cost_per_image": 0, + "input_cost_per_video_per_second": 0, + "input_cost_per_audio_per_second": 0, + "input_cost_per_token": 0, + "input_cost_per_character": 0, + "input_cost_per_token_above_128k_tokens": 0, + "input_cost_per_character_above_128k_tokens": 0, + "input_cost_per_image_above_128k_tokens": 0, + "input_cost_per_video_per_second_above_128k_tokens": 0, + "input_cost_per_audio_per_second_above_128k_tokens": 0, + "output_cost_per_token": 0, + "output_cost_per_character": 0, + "output_cost_per_token_above_128k_tokens": 0, + "output_cost_per_character_above_128k_tokens": 0, + "litellm_provider": "openrouter", + "mode": "chat", + "supports_system_messages": true, + "supports_function_calling": true, + "supports_vision": true, + "supports_audio_input": true, + "supports_video_input": true, + "supports_pdf_input": true, + "supports_response_schema": true, + "supports_tool_choice": true, + "source": "https://cloud.google.com/vertex-ai/generative-ai/pricing" + }, + "openrouter/x-ai/grok-3-beta": { + "max_tokens": 131072, + "max_input_tokens": 131072, + "max_output_tokens": 131072, + "input_cost_per_token": 0.000003, + "output_cost_per_token": 0.000015, + "litellm_provider": "openrouter", + "mode": "chat" + }, + "openrouter/x-ai/grok-3-mini-beta": { + "max_tokens": 131072, + "max_input_tokens": 131072, + "max_output_tokens": 131072, + "input_cost_per_token": 0.0000003, + "output_cost_per_token": 0.0000005, + "litellm_provider": "openrouter", + "mode": "chat" + }, + "openrouter/x-ai/grok-3-fast-beta": { + "max_tokens": 131072, + "max_input_tokens": 131072, + "max_output_tokens": 131072, + "input_cost_per_token": 0.000005, + "output_cost_per_token": 0.000025, + "litellm_provider": "openrouter", + "mode": "chat" + }, + "openrouter/x-ai/grok-3-mini-fast-beta": { + "max_tokens": 131072, + "max_input_tokens": 131072, + "max_output_tokens": 131072, + "input_cost_per_token": 0.0000006, + "output_cost_per_token": 0.000004, + "litellm_provider": "openrouter", + "mode": "chat" + }, + "openrouter/google/gemini-2.0-flash-exp:free": { + "max_tokens": 8192, + "max_input_tokens": 1048576, + "max_output_tokens": 8192, + "max_images_per_prompt": 3000, + "max_videos_per_prompt": 10, + "max_video_length": 1, + "max_audio_length_hours": 8.4, + "max_audio_per_prompt": 1, + "max_pdf_size_mb": 30, + "litellm_provider": "openrouter", + "mode": "chat", + "supports_system_messages": true, + "supports_function_calling": true, + "supports_vision": true, + "supports_response_schema": true, + "supports_audio_output": true, + "supports_tool_choice": true + }, } diff --git a/aider/resources/model-settings.yml b/aider/resources/model-settings.yml index 378ef047d..273e77621 100644 --- a/aider/resources/model-settings.yml +++ b/aider/resources/model-settings.yml @@ -185,6 +185,7 @@ editor_edit_format: editor-diff - name: anthropic/claude-3-7-sonnet-20250219 + overeager: true edit_format: diff weak_model_name: anthropic/claude-3-5-haiku-20241022 use_repo_map: true @@ -196,8 +197,10 @@ cache_control: true editor_model_name: anthropic/claude-3-7-sonnet-20250219 editor_edit_format: editor-diff + accepts_settings: ["thinking_tokens"] - name: anthropic/claude-3-7-sonnet-latest + overeager: true edit_format: diff weak_model_name: anthropic/claude-3-5-haiku-20241022 use_repo_map: true @@ -209,6 +212,7 @@ cache_control: true editor_model_name: anthropic/claude-3-7-sonnet-latest editor_edit_format: editor-diff + accepts_settings: ["thinking_tokens"] - name: claude-3-7-sonnet-20250219 edit_format: diff @@ -222,8 +226,10 @@ cache_control: true editor_model_name: claude-3-7-sonnet-20250219 editor_edit_format: editor-diff + accepts_settings: ["thinking_tokens"] - name: claude-3-7-sonnet-latest + overeager: true edit_format: diff weak_model_name: claude-3-5-haiku-20241022 use_repo_map: true @@ -235,8 +241,10 @@ cache_control: true editor_model_name: claude-3-7-sonnet-latest editor_edit_format: editor-diff + accepts_settings: ["thinking_tokens"] - name: bedrock/anthropic.claude-3-7-sonnet-20250219-v1:0 + overeager: true edit_format: diff weak_model_name: bedrock/anthropic.claude-3-5-haiku-20241022-v1:0 use_repo_map: true @@ -248,8 +256,10 @@ cache_control: true editor_model_name: bedrock/anthropic.claude-3-7-sonnet-20250219-v1:0 editor_edit_format: editor-diff + accepts_settings: ["thinking_tokens"] - name: bedrock/us.anthropic.claude-3-7-sonnet-20250219-v1:0 + overeager: true edit_format: diff weak_model_name: bedrock/us.anthropic.claude-3-5-haiku-20241022-v1:0 use_repo_map: true @@ -261,8 +271,10 @@ cache_control: true editor_model_name: bedrock/us.anthropic.claude-3-7-sonnet-20250219-v1:0 editor_edit_format: editor-diff + accepts_settings: ["thinking_tokens"] - name: bedrock_converse/anthropic.claude-3-7-sonnet-20250219-v1:0 + overeager: true edit_format: diff weak_model_name: bedrock_converse/anthropic.claude-3-5-haiku-20241022-v1:0 use_repo_map: true @@ -274,8 +286,10 @@ cache_control: true editor_model_name: bedrock_converse/anthropic.claude-3-7-sonnet-20250219-v1:0 editor_edit_format: editor-diff + accepts_settings: ["thinking_tokens"] - name: bedrock_converse/us.anthropic.claude-3-7-sonnet-20250219-v1:0 + overeager: true edit_format: diff weak_model_name: bedrock_converse/us.anthropic.claude-3-5-haiku-20241022-v1:0 use_repo_map: true @@ -287,8 +301,10 @@ cache_control: true editor_model_name: bedrock_converse/us.anthropic.claude-3-7-sonnet-20250219-v1:0 editor_edit_format: editor-diff + accepts_settings: ["thinking_tokens"] - name: vertex_ai/claude-3-7-sonnet@20250219 + overeager: true edit_format: diff weak_model_name: vertex_ai/claude-3-5-haiku@20241022 use_repo_map: true @@ -297,8 +313,10 @@ max_tokens: 64000 editor_model_name: vertex_ai/claude-3-7-sonnet@20250219 editor_edit_format: editor-diff + accepts_settings: ["thinking_tokens"] - name: vertex_ai-anthropic_models/vertex_ai/claude-3-7-sonnet@20250219 + overeager: true edit_format: diff weak_model_name: vertex_ai/claude-3-5-haiku@20241022 use_repo_map: true @@ -307,8 +325,10 @@ max_tokens: 64000 editor_model_name: vertex_ai-anthropic_models/vertex_ai/claude-3-7-sonnet@20250219 editor_edit_format: editor-diff + accepts_settings: ["thinking_tokens"] - name: openrouter/anthropic/claude-3.7-sonnet + overeager: true edit_format: diff weak_model_name: openrouter/anthropic/claude-3-5-haiku use_repo_map: true @@ -320,8 +340,10 @@ cache_control: true editor_model_name: openrouter/anthropic/claude-3.7-sonnet editor_edit_format: editor-diff + accepts_settings: ["thinking_tokens"] - name: openrouter/anthropic/claude-3.7-sonnet:beta + overeager: true edit_format: diff weak_model_name: openrouter/anthropic/claude-3-5-haiku use_repo_map: true @@ -333,6 +355,7 @@ cache_control: true editor_model_name: openrouter/anthropic/claude-3.7-sonnet editor_edit_format: editor-diff + accepts_settings: ["thinking_tokens"] - name: bedrock/anthropic.claude-3-5-sonnet-20241022-v2:0 edit_format: diff @@ -547,8 +570,8 @@ examples_as_sys_msg: true extra_params: max_tokens: 8192 + include_reasoning: true caches_by_default: true - use_temperature: false editor_model_name: openrouter/deepseek/deepseek-chat editor_edit_format: editor-diff @@ -560,6 +583,16 @@ extra_params: max_tokens: 8192 caches_by_default: true + +- name: openrouter/deepseek/deepseek-chat-v3-0324:free + edit_format: diff + weak_model_name: openrouter/deepseek/deepseek-chat-v3-0324:free + use_repo_map: true + examples_as_sys_msg: true + caches_by_default: true + use_temperature: false + editor_model_name: openrouter/deepseek/deepseek-chat-v3-0324:free + editor_edit_format: editor-diff use_temperature: false editor_model_name: openrouter/deepseek/deepseek-r1:free editor_edit_format: editor-diff @@ -585,6 +618,18 @@ max_tokens: 8192 caches_by_default: true +- name: openrouter/deepseek/deepseek-chat:free + edit_format: diff + weak_model_name: openrouter/deepseek/deepseek-chat:free + use_repo_map: true + examples_as_sys_msg: true + extra_params: + max_tokens: 8192 + caches_by_default: true + use_temperature: false + editor_model_name: openrouter/deepseek/deepseek-chat:free + editor_edit_format: editor-diff + - name: deepseek/deepseek-coder edit_format: diff use_repo_map: true @@ -623,6 +668,15 @@ reminder: sys examples_as_sys_msg: true +- name: openrouter/deepseek/deepseek-chat-v3-0324 + edit_format: diff + use_repo_map: true + reminder: sys + examples_as_sys_msg: true + extra_params: + max_tokens: 8192 + caches_by_default: true + - name: openrouter/openai/gpt-4o edit_format: diff weak_model_name: openrouter/openai/gpt-4o-mini @@ -682,6 +736,7 @@ streaming: false editor_model_name: azure/gpt-4o editor_edit_format: editor-diff + accepts_settings: ["reasoning_effort"] - name: o1-preview edit_format: architect @@ -720,6 +775,7 @@ editor_model_name: openrouter/openai/gpt-4o editor_edit_format: editor-diff system_prompt_prefix: "Formatting re-enabled. " + accepts_settings: ["reasoning_effort"] - name: openai/o1 edit_format: diff @@ -730,6 +786,7 @@ editor_model_name: openai/gpt-4o editor_edit_format: editor-diff system_prompt_prefix: "Formatting re-enabled. " + accepts_settings: ["reasoning_effort"] - name: o1 edit_format: diff @@ -740,6 +797,7 @@ editor_model_name: gpt-4o editor_edit_format: editor-diff system_prompt_prefix: "Formatting re-enabled. " + accepts_settings: ["reasoning_effort"] - name: openrouter/qwen/qwen-2.5-coder-32b-instruct edit_format: diff @@ -759,7 +817,7 @@ use_temperature: false editor_model_name: openrouter/deepseek/deepseek-chat editor_edit_format: editor-diff - + - name: fireworks_ai/accounts/fireworks/models/deepseek-r1 edit_format: diff weak_model_name: fireworks_ai/accounts/fireworks/models/deepseek-v3 @@ -768,7 +826,7 @@ streaming: true editor_model_name: fireworks_ai/accounts/fireworks/models/deepseek-v3 editor_edit_format: editor-diff - remove_reasoning: think + reasoning_tag: think extra_params: max_tokens: 160000 @@ -780,6 +838,14 @@ extra_params: max_tokens: 128000 +- name: fireworks_ai/accounts/fireworks/models/deepseek-v3-0324 + edit_format: diff + use_repo_map: true + reminder: sys + examples_as_sys_msg: true + extra_params: + max_tokens: 160000 + - name: openai/o3-mini edit_format: diff weak_model_name: gpt-4o-mini @@ -788,7 +854,8 @@ editor_model_name: gpt-4o editor_edit_format: editor-diff system_prompt_prefix: "Formatting re-enabled. " - + accepts_settings: ["reasoning_effort"] + - name: o3-mini edit_format: diff weak_model_name: gpt-4o-mini @@ -797,6 +864,7 @@ editor_model_name: gpt-4o editor_edit_format: editor-diff system_prompt_prefix: "Formatting re-enabled. " + accepts_settings: ["reasoning_effort"] - name: openrouter/openai/o3-mini edit_format: diff @@ -806,6 +874,7 @@ editor_model_name: openrouter/openai/gpt-4o editor_edit_format: editor-diff system_prompt_prefix: "Formatting re-enabled. " + accepts_settings: ["reasoning_effort"] - name: openrouter/openai/o3-mini-high edit_format: diff @@ -815,6 +884,7 @@ editor_model_name: openrouter/openai/gpt-4o editor_edit_format: editor-diff system_prompt_prefix: "Formatting re-enabled. " + accepts_settings: ["reasoning_effort"] - name: azure/o3-mini edit_format: diff @@ -824,6 +894,7 @@ editor_model_name: azure/gpt-4o editor_edit_format: editor-diff system_prompt_prefix: "Formatting re-enabled. " + accepts_settings: ["reasoning_effort"] - name: gpt-4.5-preview edit_format: diff @@ -834,7 +905,7 @@ examples_as_sys_msg: true editor_model_name: gpt-4o editor_edit_format: editor-diff - + - name: openai/gpt-4.5-preview edit_format: diff weak_model_name: gpt-4o-mini @@ -844,4 +915,474 @@ examples_as_sys_msg: true editor_model_name: openai/gpt-4o editor_edit_format: editor-diff - \ No newline at end of file + +- name: fireworks_ai/accounts/fireworks/models/qwq-32b + reasoning_tag: think + edit_format: diff + weak_model_name: fireworks_ai/accounts/fireworks/models/qwen2p5-coder-32b-instruct + use_repo_map: true + editor_model_name: fireworks_ai/accounts/fireworks/models/qwen2p5-coder-32b-instruct + editor_edit_format: editor-diff + reminder: user + examples_as_sys_msg: true + use_temperature: 0.6 + extra_params: + max_tokens: 32000 + top_p: 0.95 + +- name: groq/qwen-qwq-32b + reasoning_tag: think + edit_format: diff + weak_model_name: groq/qwen-2.5-coder-32b + use_repo_map: true + editor_model_name: groq/qwen-2.5-coder-32b + editor_edit_format: editor-diff + use_temperature: 0.6 + extra_params: + max_tokens: 128000 + top_p: 0.95 + +- name: cohere_chat/command-a-03-2025 + examples_as_sys_msg: true + +- name: openrouter/cohere/command-a-03-2025 + examples_as_sys_msg: true + +- name: gemini/gemma-3-27b-it + use_system_prompt: false + +- name: openrouter/google/gemma-3-27b-it:free + use_system_prompt: false + +- name: openrouter/google/gemma-3-27b-it + use_system_prompt: false + +- name: gemini/gemini-2.5-pro-preview-03-25 + overeager: true + edit_format: diff-fenced + use_repo_map: true + weak_model_name: gemini/gemini-2.0-flash + +- name: gemini/gemini-2.5-pro-exp-03-25 + edit_format: diff-fenced + use_repo_map: true + overeager: true + weak_model_name: gemini/gemini-2.5-flash-preview-04-17 + +- name: openrouter/google/gemini-2.5-pro-exp-03-25:free + edit_format: diff-fenced + overeager: true + use_repo_map: true + weak_model_name: openrouter/google/gemini-2.0-flash-exp:free + +- name: vertex_ai/gemini-2.5-pro-exp-03-25 + edit_format: diff-fenced + use_repo_map: true + weak_model_name: vertex_ai-language-models/gemini-2.5-flash-preview-04-17 + overeager: true + editor_model_name: vertex_ai-language-models/gemini-2.5-flash-preview-04-17 + +- name: vertex_ai/gemini-2.5-pro-preview-03-25 + edit_format: diff-fenced + use_repo_map: true + weak_model_name: vertex_ai-language-models/gemini-2.5-flash-preview-04-17 + overeager: true + editor_model_name: vertex_ai-language-models/gemini-2.5-flash-preview-04-17 + +- name: openrouter/openrouter/quasar-alpha + use_repo_map: true + edit_format: diff + examples_as_sys_msg: true + +- name: openrouter/x-ai/grok-3-beta + use_repo_map: true + edit_format: diff + +- name: xai/grok-3-beta + use_repo_map: true + edit_format: diff + +- name: openrouter/x-ai/grok-3-mini-beta + use_repo_map: true + edit_format: whole + accepts_settings: + - reasoning_effort + #extra_params: + # extra_body: + # reasoning_effort: high + +- name: openai/o4-mini + edit_format: diff + weak_model_name: openai/gpt-4.1-mini + use_repo_map: true + use_temperature: false + editor_model_name: openai/gpt-4.1 + editor_edit_format: editor-diff + system_prompt_prefix: "Formatting re-enabled. " + accepts_settings: ["reasoning_effort"] + examples_as_sys_msg: true + #extra_params: + # extra_body: + # reasoning_effort: high + +- name: openrouter/openai/o4-mini + edit_format: diff + weak_model_name: openrouter/openai/gpt-4.1-mini + use_repo_map: true + use_temperature: false + editor_model_name: openrouter/openai/gpt-4.1 + editor_edit_format: editor-diff + system_prompt_prefix: "Formatting re-enabled. " + accepts_settings: ["reasoning_effort"] + examples_as_sys_msg: true + #extra_params: + # extra_body: + # reasoning_effort: high + +- name: azure/o4-mini + edit_format: diff + weak_model_name: azure/gpt-4.1-mini + use_repo_map: true + use_temperature: false + editor_model_name: azure/gpt-4.1 + editor_edit_format: editor-diff + system_prompt_prefix: "Formatting re-enabled. " + accepts_settings: ["reasoning_effort"] + examples_as_sys_msg: true + #extra_params: + # extra_body: + # reasoning_effort: high + +- name: xai/grok-3-mini-beta + use_repo_map: true + edit_format: whole + accepts_settings: + - reasoning_effort + #extra_params: + # extra_body: + # reasoning_effort: low + +- name: openrouter/x-ai/grok-3-fast-beta + use_repo_map: true + edit_format: diff + +- name: xai/grok-3-fast-beta + use_repo_map: true + edit_format: diff + +- name: openrouter/x-ai/grok-3-mini-fast-beta + use_repo_map: true + edit_format: whole + accepts_settings: + - reasoning_effort + +- name: xai/grok-3-mini-fast-beta + use_repo_map: true + edit_format: whole + accepts_settings: + - reasoning_effort + +- name: openrouter/openrouter/optimus-alpha + use_repo_map: true + edit_format: diff + examples_as_sys_msg: true + +- name: gpt-4.1 + edit_format: diff + weak_model_name: gpt-4.1-mini + use_repo_map: true + reminder: sys # user: 52.x%/96.9% + examples_as_sys_msg: false # true: 51.6% correct, 95.6% well formed; false: 52.4%/98.2% + editor_model_name: gpt-4.1-mini + +- name: openai/gpt-4.1 + edit_format: diff + weak_model_name: openai/gpt-4.1-mini + use_repo_map: true + reminder: sys + examples_as_sys_msg: false + editor_model_name: openai/gpt-4.1-mini + +- name: azure/gpt-4.1 + edit_format: diff + weak_model_name: azure/gpt-4.1-mini + use_repo_map: true + reminder: sys + examples_as_sys_msg: false + editor_model_name: azure/gpt-4.1-mini + +- name: openrouter/openai/gpt-4.1 + edit_format: diff + weak_model_name: openrouter/openai/gpt-4.1-mini + use_repo_map: true + reminder: sys + examples_as_sys_msg: false + editor_model_name: openrouter/openai/gpt-4.1-mini + +- name: gpt-4.1-mini + edit_format: diff + use_repo_map: true + reminder: sys + examples_as_sys_msg: false # false: 32.x%/92.4% (60+ malformed responses); true: 31.7/90.2/60+ + +- name: openai/gpt-4.1-mini + edit_format: diff + use_repo_map: true + reminder: sys + examples_as_sys_msg: false + +- name: azure/gpt-4.1-mini + edit_format: diff + use_repo_map: true + reminder: sys + examples_as_sys_msg: false + +- name: openrouter/openai/gpt-4.1-mini + edit_format: diff + use_repo_map: true + reminder: sys + examples_as_sys_msg: false + +- name: o3 + streaming: false + edit_format: diff + weak_model_name: gpt-4.1-mini + use_repo_map: true + editor_model_name: gpt-4.1 + editor_edit_format: editor-diff + system_prompt_prefix: "Formatting re-enabled. " + accepts_settings: ["reasoning_effort"] + examples_as_sys_msg: true + #extra_params: + # extra_body: + # reasoning_effort: high + +- name: openai/o4-mini + edit_format: diff + weak_model_name: openai/gpt-4.1-mini + use_repo_map: true + use_temperature: false + editor_model_name: openai/gpt-4.1 + editor_edit_format: editor-diff + system_prompt_prefix: "Formatting re-enabled. " + accepts_settings: ["reasoning_effort"] + examples_as_sys_msg: true + #extra_params: + # extra_body: + # reasoning_effort: high + +- name: openrouter/openai/o4-mini + edit_format: diff + weak_model_name: openrouter/openai/gpt-4.1-mini + use_repo_map: true + use_temperature: false + editor_model_name: openrouter/openai/gpt-4.1 + editor_edit_format: editor-diff + system_prompt_prefix: "Formatting re-enabled. " + accepts_settings: ["reasoning_effort"] + examples_as_sys_msg: true + #extra_params: + # extra_body: + # reasoning_effort: high + +- name: azure/o4-mini + edit_format: diff + weak_model_name: azure/gpt-4.1-mini + use_repo_map: true + use_temperature: false + editor_model_name: azure/gpt-4.1 + editor_edit_format: editor-diff + system_prompt_prefix: "Formatting re-enabled. " + accepts_settings: ["reasoning_effort"] + examples_as_sys_msg: true + #extra_params: + # extra_body: + # reasoning_effort: high + +- name: openai/o3 + streaming: false + edit_format: diff + weak_model_name: openai/gpt-4.1-mini + use_repo_map: true + editor_model_name: openai/gpt-4.1 + editor_edit_format: editor-diff + system_prompt_prefix: "Formatting re-enabled. " + accepts_settings: ["reasoning_effort"] + examples_as_sys_msg: true + #extra_params: + # extra_body: + # reasoning_effort: high + +- name: openai/o4-mini + edit_format: diff + weak_model_name: openai/gpt-4.1-mini + use_repo_map: true + use_temperature: false + editor_model_name: openai/gpt-4.1 + editor_edit_format: editor-diff + system_prompt_prefix: "Formatting re-enabled. " + accepts_settings: ["reasoning_effort"] + examples_as_sys_msg: true + #extra_params: + # extra_body: + # reasoning_effort: high + +- name: openrouter/openai/o4-mini + edit_format: diff + weak_model_name: openrouter/openai/gpt-4.1-mini + use_repo_map: true + use_temperature: false + editor_model_name: openrouter/openai/gpt-4.1 + editor_edit_format: editor-diff + system_prompt_prefix: "Formatting re-enabled. " + accepts_settings: ["reasoning_effort"] + examples_as_sys_msg: true + #extra_params: + # extra_body: + # reasoning_effort: high + +- name: azure/o4-mini + edit_format: diff + weak_model_name: azure/gpt-4.1-mini + use_repo_map: true + use_temperature: false + editor_model_name: azure/gpt-4.1 + editor_edit_format: editor-diff + system_prompt_prefix: "Formatting re-enabled. " + accepts_settings: ["reasoning_effort"] + examples_as_sys_msg: true + #extra_params: + # extra_body: + # reasoning_effort: high + +- name: openrouter/openai/o3 + streaming: false + edit_format: diff + weak_model_name: openrouter/openai/gpt-4.1-mini + use_repo_map: true + editor_model_name: openrouter/openai/gpt-4.1 + editor_edit_format: editor-diff + system_prompt_prefix: "Formatting re-enabled. " + accepts_settings: ["reasoning_effort"] + examples_as_sys_msg: true + #extra_params: + # extra_body: + # reasoning_effort: high + +- name: openai/o4-mini + edit_format: diff + weak_model_name: openai/gpt-4.1-mini + use_repo_map: true + use_temperature: false + editor_model_name: openai/gpt-4.1 + editor_edit_format: editor-diff + system_prompt_prefix: "Formatting re-enabled. " + accepts_settings: ["reasoning_effort"] + examples_as_sys_msg: true + #extra_params: + # extra_body: + # reasoning_effort: high + +- name: openrouter/openai/o4-mini + edit_format: diff + weak_model_name: openrouter/openai/gpt-4.1-mini + use_repo_map: true + use_temperature: false + editor_model_name: openrouter/openai/gpt-4.1 + editor_edit_format: editor-diff + system_prompt_prefix: "Formatting re-enabled. " + accepts_settings: ["reasoning_effort"] + examples_as_sys_msg: true + #extra_params: + # extra_body: + # reasoning_effort: high + +- name: azure/o4-mini + edit_format: diff + weak_model_name: azure/gpt-4.1-mini + use_repo_map: true + use_temperature: false + editor_model_name: azure/gpt-4.1 + editor_edit_format: editor-diff + system_prompt_prefix: "Formatting re-enabled. " + accepts_settings: ["reasoning_effort"] + examples_as_sys_msg: true + #extra_params: + # extra_body: + # reasoning_effort: high + +- name: azure/o3 + streaming: false + edit_format: diff + weak_model_name: azure/gpt-4.1-mini + use_repo_map: true + editor_model_name: azure/gpt-4.1 + editor_edit_format: editor-diff + system_prompt_prefix: "Formatting re-enabled. " + accepts_settings: ["reasoning_effort"] + examples_as_sys_msg: true + #extra_params: + # extra_body: + # reasoning_effort: high + +- name: openai/o4-mini + edit_format: diff + weak_model_name: openai/gpt-4.1-mini + use_repo_map: true + use_temperature: false + editor_model_name: openai/gpt-4.1 + editor_edit_format: editor-diff + system_prompt_prefix: "Formatting re-enabled. " + accepts_settings: ["reasoning_effort"] + examples_as_sys_msg: true + +- name: openrouter/openai/o4-mini + edit_format: diff + weak_model_name: openrouter/openai/gpt-4.1-mini + use_repo_map: true + use_temperature: false + editor_model_name: openrouter/openai/gpt-4.1 + editor_edit_format: editor-diff + system_prompt_prefix: "Formatting re-enabled. " + accepts_settings: ["reasoning_effort"] + examples_as_sys_msg: true + +- name: azure/o4-mini + edit_format: diff + weak_model_name: azure/gpt-4.1-mini + use_repo_map: true + use_temperature: false + editor_model_name: azure/gpt-4.1 + editor_edit_format: editor-diff + system_prompt_prefix: "Formatting re-enabled. " + accepts_settings: ["reasoning_effort"] + examples_as_sys_msg: true + +- name: o4-mini + edit_format: diff + weak_model_name: gpt-4.1-mini + use_repo_map: true + use_temperature: false + editor_model_name: gpt-4.1 + editor_edit_format: editor-diff + system_prompt_prefix: "Formatting re-enabled. " + accepts_settings: ["reasoning_effort"] + examples_as_sys_msg: true + #extra_params: + # extra_body: + # reasoning_effort: high + +- name: gemini/gemini-2.5-flash-preview-04-17 + edit_format: diff + use_repo_map: true + accepts_settings: ["thinking_tokens"] + +- name: gemini-2.5-flash-preview-04-17 + edit_format: diff + use_repo_map: true + accepts_settings: ["thinking_tokens"] + +- name: vertex_ai-language-models/gemini-2.5-flash-preview-04-17 + edit_format: diff + use_repo_map: true + accepts_settings: ["thinking_tokens"] diff --git a/aider/scrape.py b/aider/scrape.py index 7977a8548..8ab5a93ed 100755 --- a/aider/scrape.py +++ b/aider/scrape.py @@ -159,7 +159,8 @@ class Scraper: try: response = page.goto(url, wait_until="networkidle", timeout=5000) except PlaywrightTimeoutError: - self.print_error(f"Timeout while loading {url}") + print(f"Page didn't quiesce, scraping content anyway: {url}") + response = None except PlaywrightError as e: self.print_error(f"Error navigating to {url}: {str(e)}") return None, None diff --git a/aider/utils.py b/aider/utils.py index 322a44316..c6773f140 100644 --- a/aider/utils.py +++ b/aider/utils.py @@ -308,7 +308,11 @@ def find_common_root(abs_fnames): except OSError: pass - return safe_abs_path(os.getcwd()) + try: + return safe_abs_path(os.getcwd()) + except FileNotFoundError: + # Fallback if cwd is deleted + return "." def format_tokens(count): diff --git a/aider/watch.py b/aider/watch.py index 989aa8bf6..e7b115730 100644 --- a/aider/watch.py +++ b/aider/watch.py @@ -64,7 +64,7 @@ class FileWatcher: """Watches source files for changes and AI comments""" # Compiled regex pattern for AI comments - ai_comment_pattern = re.compile(r"(?:#|//|--) *(ai\b.*|ai\b.*|.*\bai[?!]?) *$", re.IGNORECASE) + ai_comment_pattern = re.compile(r"(?:#|//|--|;+) *(ai\b.*|ai\b.*|.*\bai[?!]?) *$", re.IGNORECASE) def __init__(self, coder, gitignores=None, verbose=False, analytics=None, root=None): self.coder = coder @@ -140,7 +140,10 @@ class FileWatcher: roots_to_watch = self.get_roots_to_watch() for changes in watch( - *roots_to_watch, watch_filter=self.filter_func, stop_event=self.stop_event + *roots_to_watch, + watch_filter=self.filter_func, + stop_event=self.stop_event, + ignore_permission_denied=True, ): if self.handle_changes(changes): return @@ -259,7 +262,7 @@ class FileWatcher: line_nums.append(i) comments.append(comment) comment = comment.lower() - comment = comment.lstrip("/#-") + comment = comment.lstrip("/#-;") # Added semicolon for Lisp comments comment = comment.strip() if comment.startswith("ai!") or comment.endswith("ai!"): has_action = "!" diff --git a/aider/website/HISTORY.md b/aider/website/HISTORY.md index fdb24da25..abce8bd1d 100644 --- a/aider/website/HISTORY.md +++ b/aider/website/HISTORY.md @@ -7,12 +7,13 @@ description: Release notes and stats on aider writing its own code. # Release history -{% include blame.md %} - -The above -[stats are based on the git commit history](/docs/faq.html#how-are-the-aider-wrote-xx-of-code-stats-computed) +Aider writes most of its own code, usually about 70-80% of the new code in each release. +These +[statistics are based on the git commit history](/docs/faq.html#how-are-the-aider-wrote-xx-of-code-stats-computed) of the aider repo. +{% include blame.md %} + ## Release notes + + + + diff --git a/aider/website/_includes/install.md b/aider/website/_includes/install.md new file mode 100644 index 000000000..f42be5656 --- /dev/null +++ b/aider/website/_includes/install.md @@ -0,0 +1,5 @@ + +```bash +python -m pip install aider-install +aider-install +``` diff --git a/aider/website/_includes/leaderboard.js b/aider/website/_includes/leaderboard.js index 7ee4e303b..0f991b8b7 100644 --- a/aider/website/_includes/leaderboard.js +++ b/aider/website/_includes/leaderboard.js @@ -4,7 +4,11 @@ document.addEventListener('DOMContentLoaded', function () { const redDiagonalPattern = pattern.draw('diagonal', 'rgba(255, 99, 132, 0.2)'); let displayedData = []; - const HIGHLIGHT_MODEL = '{{ highlight_model | default: "no no no" }}'; + // Get highlight model from query string or Jekyll variable + const urlParams = new URLSearchParams(window.location.search); + const queryHighlight = urlParams.get('highlight'); + const HIGHLIGHT_MODEL = queryHighlight || '{{ highlight_model | default: "no no no" }}'; + var leaderboardData = { labels: [], datasets: [{ @@ -13,14 +17,14 @@ document.addEventListener('DOMContentLoaded', function () { backgroundColor: function(context) { const row = allData[context.dataIndex]; if (row && row.edit_format === 'whole') { - return diagonalPattern; + return redDiagonalPattern; // Use red pattern for highlighted whole format } const label = leaderboardData.labels[context.dataIndex] || ''; - return (label && label.includes(HIGHLIGHT_MODEL)) ? 'rgba(255, 99, 132, 0.2)' : 'rgba(54, 162, 235, 0.2)'; + return (label && HIGHLIGHT_MODEL && label.toLowerCase().includes(HIGHLIGHT_MODEL.toLowerCase())) ? 'rgba(255, 99, 132, 0.2)' : 'rgba(54, 162, 235, 0.2)'; }, borderColor: function(context) { const label = context.chart.data.labels[context.dataIndex] || ''; - return (label && label.includes(HIGHLIGHT_MODEL)) ? 'rgba(255, 99, 132, 1)' : 'rgba(54, 162, 235, 1)'; + return (label && HIGHLIGHT_MODEL && label.toLowerCase().includes(HIGHLIGHT_MODEL.toLowerCase())) ? 'rgba(255, 99, 132, 1)' : 'rgba(54, 162, 235, 1)'; }, borderWidth: 1 }, { @@ -74,11 +78,13 @@ document.addEventListener('DOMContentLoaded', function () { leaderboardChart.render(); } - // Use displayedData in the backgroundColor callback instead of allData + // Update backgroundColor and borderColor for the main dataset based on displayedData leaderboardData.datasets[0].backgroundColor = function(context) { const row = displayedData[context.dataIndex]; const label = leaderboardData.labels[context.dataIndex] || ''; - if (label && label.includes(HIGHLIGHT_MODEL)) { + const isHighlighted = label && HIGHLIGHT_MODEL && label.toLowerCase().includes(HIGHLIGHT_MODEL.toLowerCase()); + + if (isHighlighted) { if (row && row.edit_format === 'whole') return redDiagonalPattern; else return 'rgba(255, 99, 132, 0.2)'; } else if (row && row.edit_format === 'whole') { @@ -171,6 +177,9 @@ document.addEventListener('DOMContentLoaded', function () { }, x: { ticks: { + autoSkip: false, // Prevent labels from being automatically skipped + maxRotation: 90, // Allow labels to rotate up to 90 degrees + minRotation: 0, callback: function(value, index) { const label = this.getLabelForValue(value); if (label.length <= "claude-3-5-sonnet".length) { diff --git a/aider/website/_includes/leaderboard_table.js b/aider/website/_includes/leaderboard_table.js new file mode 100644 index 000000000..8f9f40a82 --- /dev/null +++ b/aider/website/_includes/leaderboard_table.js @@ -0,0 +1,515 @@ +document.addEventListener('DOMContentLoaded', function() { + let currentMode = 'view'; // 'view', 'select', 'detail' + let selectedRows = new Set(); // Store indices of selected rows + const MAX_DISPLAY_COST_CAP = 75; // Define the constant here + + const allMainRows = document.querySelectorAll('tr[id^="main-row-"]'); + const allDetailsRows = document.querySelectorAll('tr[id^="details-"]'); + const searchInput = document.getElementById('editSearchInput'); + const modeViewButton = document.getElementById('mode-view-btn'); + const modeDetailButton = document.getElementById('mode-detail-btn'); + const modeSelectButton = document.getElementById('mode-select-btn'); + const modeButtons = [modeViewButton, modeSelectButton, modeDetailButton]; + const selectAllCheckbox = document.getElementById('select-all-checkbox'); + const leaderboardTitle = document.getElementById('leaderboard-title'); // Get title element + const defaultTitle = "Aider polyglot coding leaderboard"; + const filteredTitle = "Aider polyglot coding benchmark results (selected)"; + + function applySearchFilter() { + const searchTerm = searchInput.value.toLowerCase(); + allMainRows.forEach(row => { + const textContent = row.textContent.toLowerCase(); + const detailsRow = document.getElementById(row.id.replace('main-row-', 'details-')); + const matchesSearch = textContent.includes(searchTerm); + + if (matchesSearch) { + row.classList.remove('hidden-by-search'); + if (detailsRow) detailsRow.classList.remove('hidden-by-search'); + } else { + row.classList.add('hidden-by-search'); + if (detailsRow) detailsRow.classList.add('hidden-by-search'); + } + }); + // After applying search filter, re-apply view mode filter and update select-all state + updateTableView(currentMode); + if (currentMode === 'select') { + updateSelectAllCheckboxState(); + } + + // Update cost bars and ticks since visible rows may have changed + updateCostBars(); + updateCostTicks(); + } + + function getVisibleMainRows() { + // Helper to get rows currently visible (not hidden by search or mode) + return Array.from(allMainRows).filter(row => + !row.classList.contains('hidden-by-search') && !row.classList.contains('hidden-by-mode') + ); + } + + function updateSelectAllCheckboxState() { + // Update the header checkbox based on the selection state of *visible* rows + if (currentMode !== 'select') return; // Only relevant in select mode + + const visibleRows = getVisibleMainRows(); + const visibleRowCount = visibleRows.length; + const selectedVisibleRowCount = visibleRows.filter(row => selectedRows.has(row.querySelector('.row-selector')?.dataset.rowIndex)).length; + + if (visibleRowCount === 0) { + selectAllCheckbox.checked = false; + selectAllCheckbox.indeterminate = false; + } else if (selectedVisibleRowCount === visibleRowCount) { + selectAllCheckbox.checked = true; + selectAllCheckbox.indeterminate = false; + } else if (selectedVisibleRowCount > 0) { + selectAllCheckbox.checked = false; + selectAllCheckbox.indeterminate = true; + } else { + selectAllCheckbox.checked = false; + selectAllCheckbox.indeterminate = false; + } + } + + + function updateTableView(mode) { + currentMode = mode; // Update global state ('view', 'select', 'detail') + + // Update button styles first + modeButtons.forEach(btn => { + btn.classList.remove('active'); + // Reset specific styles potentially added by .active + btn.style.backgroundColor = ''; + btn.style.color = ''; + }); + let activeButton; + if (mode === 'view') activeButton = modeViewButton; + else if (mode === 'select') activeButton = modeSelectButton; + else if (mode === 'detail') activeButton = modeDetailButton; + + activeButton.classList.add('active'); + activeButton.style.backgroundColor = '#e7f3ff'; // Use selected row highlight blue + activeButton.style.color = '#495057'; // Use dark text for contrast on light blue + + // Get the first header cell (for the toggle/checkbox column) + const firstHeaderCell = document.querySelector('table thead th:first-child'); + + // Show/hide header checkbox based on mode + selectAllCheckbox.style.display = mode === 'select' ? 'inline-block' : 'none'; + + allMainRows.forEach(row => { + const rowIndex = row.querySelector('.row-selector')?.dataset.rowIndex; + const toggleButton = row.querySelector('.toggle-details'); + const selectorCheckbox = row.querySelector('.row-selector'); + const firstCell = row.querySelector('td:first-child'); // Get the first cell of the main row + const detailsRow = document.getElementById(`details-${rowIndex}`); + const isSelected = selectedRows.has(rowIndex); + + // Reset visibility classes before applying mode logic + row.classList.remove('hidden-by-mode'); + if (detailsRow) detailsRow.classList.remove('hidden-by-mode'); + + // Show/hide the first column (header and data cells) based on mode + if (firstHeaderCell) { + firstHeaderCell.style.display = mode === 'view' ? 'none' : ''; + } + if (firstCell) { + firstCell.style.display = mode === 'view' ? 'none' : ''; + } + + // Apply mode-specific logic + if (mode === 'view') { // --- VIEW MODE --- + toggleButton.style.display = 'none'; // Hide toggle in view mode + selectorCheckbox.style.display = 'none'; + row.classList.remove('row-selected'); // Ensure no selection highlight + // view-highlighted is handled by row click listener + + // In 'view' mode, hide row if selections exist AND this row is NOT selected + if (selectedRows.size > 0 && !isSelected) { + row.classList.add('hidden-by-mode'); + if (detailsRow) detailsRow.classList.add('hidden-by-mode'); + } else { + // Ensure row is not hidden by mode if it's selected or no selections exist + // This is handled by the reset at the start of the loop: + // row.classList.remove('hidden-by-mode'); + // if (detailsRow) detailsRow.classList.remove('hidden-by-mode'); + } + // Always hide details row content in view mode regardless of visibility class + if (detailsRow) { + detailsRow.style.display = 'none'; + } + + } else if (mode === 'select') { // --- SELECT MODE --- + toggleButton.style.display = 'none'; + selectorCheckbox.style.display = 'inline-block'; + selectorCheckbox.checked = isSelected; + row.classList.toggle('row-selected', isSelected); + row.classList.remove('view-highlighted'); // Clear view highlight when switching to select + // Always hide details row in select mode + if (detailsRow) detailsRow.style.display = 'none'; + + // In 'select' mode, no rows should be hidden based on selection status + row.classList.remove('hidden-by-mode'); + if (detailsRow) detailsRow.classList.remove('hidden-by-mode'); + + } else { // --- DETAIL MODE --- (mode === 'detail') + toggleButton.style.display = 'inline-block'; // Show toggle + selectorCheckbox.style.display = 'none'; + row.classList.remove('row-selected'); // Clear selection highlight + row.classList.remove('view-highlighted'); // Clear view highlight when switching to detail + // Details row visibility is controlled by the toggle button state, don't force hide/show here + // Ensure main row is visible if not hidden by search + row.classList.remove('hidden-by-mode'); + if (detailsRow) { + detailsRow.classList.remove('hidden-by-mode'); + // Preserve existing display state (controlled by toggle) unless hidden by search + if (detailsRow.classList.contains('hidden-by-search')) { + detailsRow.style.display = 'none'; + } + } + } + + + // Ensure rows hidden by search remain hidden regardless of mode + if (row.classList.contains('hidden-by-search')) { + row.style.display = 'none'; + if (detailsRow) detailsRow.style.display = 'none'; + } else if (!row.classList.contains('hidden-by-mode')) { + // Make row visible if not hidden by search or mode + row.style.display = ''; // Or 'table-row' if needed, but '' usually works + } else { + // Row is hidden by mode, ensure it's hidden + row.style.display = 'none'; + if (detailsRow) detailsRow.style.display = 'none'; + } + + + }); + + // Update the leaderboard title based on mode and selection + if (leaderboardTitle) { + if (currentMode === 'view' && selectedRows.size > 0) { + leaderboardTitle.textContent = filteredTitle; + } else { + leaderboardTitle.textContent = defaultTitle; + } + } + + // Update the select-all checkbox state after updating the view + updateSelectAllCheckboxState(); + + // Update cost bars and ticks since visible/selected rows may have changed + updateCostBars(); + updateCostTicks(); + } + + + // --- Existing Initializations --- + // Add percentage ticks + const percentCells = document.querySelectorAll('.bar-cell:not(.cost-bar-cell)'); + percentCells.forEach(cell => { + // Add ticks at 0%, 10%, 20%, ..., 100% + for (let i = 0; i <= 100; i += 10) { + const tick = document.createElement('div'); + tick.className = 'percent-tick'; + tick.style.left = `${i}%`; + cell.appendChild(tick); + } + }); + + // Function to calculate the appropriate max display cost based on visible/selected entries + function calculateDisplayMaxCost() { + // Get the appropriate set of rows based on the current mode and selection state + let rowsToConsider; + + if (currentMode === 'view' && selectedRows.size > 0) { + // In view mode with selections, only consider selected rows + rowsToConsider = Array.from(allMainRows).filter(row => { + const rowIndex = row.querySelector('.row-selector')?.dataset.rowIndex; + return rowIndex && selectedRows.has(rowIndex) && !row.classList.contains('hidden-by-search'); + }); + } else { + // In other modes or without selections, consider all visible rows + rowsToConsider = getVisibleMainRows(); + } + + // Find the maximum cost among the rows to consider + let maxCost = 0; + rowsToConsider.forEach(row => { + const costBar = row.querySelector('.cost-bar'); + if (costBar) { + const cost = parseFloat(costBar.dataset.cost || '0'); + if (cost > maxCost) maxCost = cost; + } + }); + + // Cap at MAX_DISPLAY_COST_CAP if any entries exceed that amount, otherwise use actual max + return maxCost > MAX_DISPLAY_COST_CAP ? MAX_DISPLAY_COST_CAP : Math.max(1, maxCost); // Ensure at least 1 to avoid division by zero + } + + // Process cost bars with dynamic scale + function updateCostBars() { + const costBars = document.querySelectorAll('.cost-bar'); + const currentMaxDisplayCost = calculateDisplayMaxCost(); + + // Remove existing special indicators first + document.querySelectorAll('.dark-section, .tear-line').forEach(el => el.remove()); + + costBars.forEach(bar => { + const cost = parseFloat(bar.dataset.cost); + + if (cost > 0) { + // Calculate percentage based on the dynamic display max + const percent = Math.min(cost, currentMaxDisplayCost) / currentMaxDisplayCost * 100; + // Clamp percentage between 0 and 100 + bar.style.width = Math.max(0, Math.min(100, percent)) + '%'; + + // Mark bars that exceed the limit (only if our display max is capped at 50) + if (currentMaxDisplayCost === MAX_DISPLAY_COST_CAP && cost > MAX_DISPLAY_COST_CAP) { + // Create a darker section at the end with diagonal stripes + const darkSection = document.createElement('div'); + darkSection.className = 'bar-viz dark-section'; + darkSection.style.width = '15%'; // From 85% to 100% + darkSection.style.left = '85%'; + darkSection.style.backgroundColor = 'rgba(13, 110, 253, 0.6)'; // Darker blue + darkSection.style.borderRight = '1px solid rgba(13, 110, 253, 0.8)'; + darkSection.style.zIndex = '1'; + // Add diagonal stripes with CSS background + darkSection.style.backgroundImage = 'repeating-linear-gradient(45deg, rgba(255,255,255,0.3), rgba(255,255,255,0.3) 5px, transparent 5px, transparent 10px)'; + bar.parentNode.appendChild(darkSection); + + // Add a dashed "tear line" at the transition point + const tearLine = document.createElement('div'); + tearLine.className = 'tear-line'; + tearLine.style.position = 'absolute'; + tearLine.style.left = '85%'; + // Center the tear line vertically and make it 1.5x as tall as the bar + tearLine.style.top = '50%'; + tearLine.style.transform = 'translateY(-50%)'; + tearLine.style.height = '54px'; // 1.5x the bar height (36px) + tearLine.style.width = '2px'; + tearLine.style.backgroundColor = 'white'; + tearLine.style.borderLeft = '2px dashed rgba(0, 0, 0, 0.3)'; + tearLine.style.zIndex = '2'; // Above the bar + bar.parentNode.appendChild(tearLine); + } + } else { + // Set width to 0 if cost is 0 or negative + bar.style.width = '0%'; + } + }); + } + + // Call this initially to set up the bars + updateCostBars(); + + // Update cost ticks dynamically based on current max display cost + function updateCostTicks() { + const costCells = document.querySelectorAll('.cost-bar-cell'); + if (costCells.length === 0) return; + + const currentMaxDisplayCost = calculateDisplayMaxCost(); + + // Remove existing ticks first + document.querySelectorAll('.cost-tick').forEach(tick => tick.remove()); + + // Generate appropriate tick values based on current max + let tickValues = []; + + // Always use $10 increments, regardless of the max + const maxTickValue = Math.ceil(currentMaxDisplayCost / 10) * 10; // Round up to nearest $10 + + for (let i = 0; i <= maxTickValue; i += 10) { + tickValues.push(i); + } + + // Calculate percentage positions for each tick + const tickPercentages = tickValues.map(tickCost => { + return (tickCost / currentMaxDisplayCost) * 100; + }); + + // Add tick divs to each cost cell + costCells.forEach(cell => { + const costBar = cell.querySelector('.cost-bar'); + // Use optional chaining and provide '0' as fallback if costBar or dataset.cost is missing + const cost = parseFloat(costBar?.dataset?.cost || '0'); + + // Only add ticks if the cost is actually greater than 0 + if (cost > 0) { + tickPercentages.forEach((percent, index) => { + // Ensure percentage is within valid range + if (percent >= 0 && percent <= 100) { + const tick = document.createElement('div'); + tick.className = 'cost-tick'; + tick.style.left = `${percent}%`; + cell.appendChild(tick); + } + }); + } + }); + } + + // Call this initially to set up the ticks + updateCostTicks(); + + + // --- New Event Listeners --- + + // Listener for mode toggle buttons + modeButtons.forEach(button => { + button.addEventListener('click', function(event) { + const newMode = this.dataset.mode; + if (newMode !== currentMode) { + // Update active button style + modeButtons.forEach(btn => { + btn.classList.remove('active'); + // Reset specific styles potentially added by .active + btn.style.backgroundColor = ''; + btn.style.color = ''; + }); + this.classList.add('active'); + // Apply active styles directly as inline styles might interfere + this.style.backgroundColor = '#e7f3ff'; // Use selected row highlight blue + this.style.color = '#495057'; // Use dark text for contrast on light blue + + // Update table view and apply filters + updateTableView(newMode); + applySearchFilter(); // Re-apply search filter when mode changes + } + }); + }); + + // Listener for row selector checkboxes (using event delegation on table body) + const tableBody = document.querySelector('table tbody'); + tableBody.addEventListener('change', function(event) { + if (event.target.classList.contains('row-selector') && currentMode === 'select') { + const checkbox = event.target; + const rowIndex = checkbox.dataset.rowIndex; + const mainRow = checkbox.closest('tr'); + + if (checkbox.checked) { + selectedRows.add(rowIndex); + mainRow.classList.add('row-selected'); + } else { + selectedRows.delete(rowIndex); + mainRow.classList.remove('row-selected'); + } + // Update select-all checkbox state + updateSelectAllCheckboxState(); + + // Update cost bars and ticks if in view mode, as selection affects what's shown + if (currentMode === 'view') { + updateCostBars(); + updateCostTicks(); + } + } + }); // End of tableBody listener + + // Listener for Select All checkbox + selectAllCheckbox.addEventListener('change', function() { + if (currentMode !== 'select') return; + + const isChecked = selectAllCheckbox.checked; + // Select/deselect only the rows that are currently visible + const visibleRows = getVisibleMainRows(); + + visibleRows.forEach(row => { + const checkbox = row.querySelector('.row-selector'); + const rowIndex = checkbox?.dataset.rowIndex; + if (!checkbox || !rowIndex) return; // Skip if no checkbox/index found + + // Only change state if it differs from target state + if (checkbox.checked !== isChecked) { + checkbox.checked = isChecked; + row.classList.toggle('row-selected', isChecked); + if (isChecked) { + selectedRows.add(rowIndex); + } else { + selectedRows.delete(rowIndex); + } + } + }); + // After bulk change, ensure the selectAll checkbox state is correct (not indeterminate) + updateSelectAllCheckboxState(); + + // Update cost bars and ticks after selection changes + updateCostBars(); + updateCostTicks(); + }); + + // Listener for search input + searchInput.addEventListener('input', applySearchFilter); + + // Add toggle functionality for details (Modified to respect modes) + const toggleButtons = document.querySelectorAll('.toggle-details'); + toggleButtons.forEach(button => { + button.addEventListener('click', function() { + // Only allow toggling in 'detail' mode + if (currentMode !== 'detail') return; + + const targetId = this.getAttribute('data-target'); + const targetRow = document.getElementById(targetId); + const mainRow = this.closest('tr'); // Get the main row associated with this button + + if (targetRow && !mainRow.classList.contains('hidden-by-mode') && !mainRow.classList.contains('hidden-by-search')) { + const isVisible = targetRow.style.display !== 'none'; + targetRow.style.display = isVisible ? 'none' : 'table-row'; + this.textContent = isVisible ? '▶' : '▼'; + } + }); + }); + + // Listener for clicking anywhere on a row + tableBody.addEventListener('click', function(event) { + const clickedRow = event.target.closest('tr'); + + // Ensure it's a main row and not a details row or header/footer + if (!clickedRow || !clickedRow.id.startsWith('main-row-')) return; + + // --- START conditional logic --- + if (currentMode === 'select') { + // --- SELECT MODE LOGIC (Existing) --- + // Find the checkbox within this row + const checkbox = clickedRow.querySelector('.row-selector'); + if (!checkbox) return; // No checkbox found in this row + + // If the click was directly on the checkbox or its label (if any), + // let the default behavior and the 'change' event listener handle it. + // Otherwise, toggle the checkbox state programmatically. + if (event.target !== checkbox && event.target.tagName !== 'LABEL' /* Add if you use labels */) { + checkbox.checked = !checkbox.checked; + // Manually trigger the change event to update state and UI + checkbox.dispatchEvent(new Event('change', { bubbles: true })); + } + // --- END SELECT MODE LOGIC --- + + } else if (currentMode === 'view') { + // --- VIEW MODE LOGIC (New) --- + // Don't highlight if the click was on the details toggle button + if (event.target.classList.contains('toggle-details')) { + return; + } + // Toggle the highlight class on the clicked row + clickedRow.classList.toggle('view-highlighted'); + // --- END VIEW MODE LOGIC --- + } + // --- END conditional logic --- + }); + + + // --- Initial Setup --- + updateTableView('view'); // Initialize view to 'view' mode + applySearchFilter(); // Apply initial search filter (if any text is pre-filled or just to set initial state) + +// Close button functionality +const closeControlsBtn = document.getElementById('close-controls-btn'); +if (closeControlsBtn) { + closeControlsBtn.addEventListener('click', function() { + const controlsContainer = document.getElementById('controls-container'); + if (controlsContainer) { + controlsContainer.style.display = 'none'; + } + }); +} + +}); diff --git a/aider/website/_includes/multi-line.md b/aider/website/_includes/multi-line.md index b1fe74211..079113b14 100644 --- a/aider/website/_includes/multi-line.md +++ b/aider/website/_includes/multi-line.md @@ -4,7 +4,7 @@ You can send long, multi-line messages in the chat in a few ways: - Or, start with `{tag` (where "tag" is any sequence of letters/numbers) and end with `tag}`. This is useful when you need to include closing braces `}` in your message. - Use Meta-ENTER to start a new line without sending the message (Esc+ENTER in some environments). - Use `/paste` to paste text from the clipboard into the chat. - - Use the `/editor` command to open your editor to create the next chat message. See [editor configuration docs](/docs/config/editor.html) for more info. + - Use the `/editor` command (or press `Ctrl-X Ctrl-E` if your terminal allows) to open your editor to create the next chat message. See [editor configuration docs](/docs/config/editor.html) for more info. - Use multiline-mode, which swaps the function of Meta-Enter and Enter, so that Enter inserts a newline, and Meta-Enter submits your command. To enable multiline mode: - Use the `/multiline-mode` command to toggle it during a session. - Use the `--multiline` switch. diff --git a/aider/website/_includes/recording.css b/aider/website/_includes/recording.css new file mode 100644 index 000000000..292407d2a --- /dev/null +++ b/aider/website/_includes/recording.css @@ -0,0 +1,228 @@ +/* Terminal header styling */ +.terminal-header { + background-color: #e0e0e0; + border-top-left-radius: 6px; + border-top-right-radius: 6px; + padding: 4px 10px; + display: flex; + align-items: center; + border-bottom: 1px solid #c0c0c0; +} + +.terminal-buttons { + display: flex; + gap: 4px; + margin-right: 10px; +} + +.terminal-button { + width: 10px; + height: 10px; + border-radius: 50%; +} + +.terminal-close { + background-color: #ff5f56; + border: 1px solid #e0443e; +} + +.terminal-minimize { + background-color: #ffbd2e; + border: 1px solid #dea123; +} + +.terminal-expand { + background-color: #27c93f; + border: 1px solid #1aab29; +} + +.terminal-title { + flex-grow: 1; + text-align: center; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; + font-size: 11px; + color: #666; +} + +/* Toast notification styling */ +.toast-container { + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + z-index: 9999; + pointer-events: none; +} + +.toast-notification { + background-color: rgba(0, 0, 0, 0.7); + color: white; + padding: 12px 25px; + border-radius: 8px; + margin-bottom: 10px; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2); + opacity: 0; + transition: opacity 0.3s ease-in-out; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; + font-size: 18px; + text-align: center; + display: inline-block; + min-width: 200px; + max-width: 90%; +} + +/* Page container styling */ +.page-container { + max-width: 950px; + margin-left: auto; + margin-right: auto; + position: relative; +} + +/* macOS backdrop styling */ +.macos-backdrop { + background: linear-gradient(135deg, #ff9966, #ff5e62, #6666ff, #0066ff); + border-radius: 12px; + padding: clamp(5px, 5vw, 50px) clamp(5px, 2.5vw, 50px); + margin: 20px 0; + box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2); + position: relative; + overflow: hidden; +} + +/* Add subtle wave animation to backdrop */ +.macos-backdrop::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: radial-gradient(circle at center, rgba(255,255,255,0.1) 0%, rgba(255,255,255,0) 70%); + opacity: 0.7; + pointer-events: none; +} + +/* Add decorative curved lines to the backdrop */ +.macos-backdrop::after { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-image: + radial-gradient(circle at 20% 30%, transparent 0%, transparent 60%, rgba(255,255,255,0.2) 61%, transparent 62%), + radial-gradient(circle at 80% 70%, transparent 0%, transparent 40%, rgba(255,255,255,0.2) 41%, transparent 42%), + radial-gradient(circle at 40% 90%, transparent 0%, transparent 70%, rgba(255,255,255,0.2) 71%, transparent 72%), + radial-gradient(circle at 60% 10%, transparent 0%, transparent 50%, rgba(255,255,255,0.2) 51%, transparent 52%); + background-size: 100% 100%; + opacity: 1; + pointer-events: none; + z-index: 0; +} + +.terminal-container { + border-radius: 8px; + overflow: hidden; + box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2); + margin-top: 0; + margin-bottom: 0; + position: relative; + background-color: white; /* Add background color to terminal container */ + z-index: 2; /* Ensure terminal appears above the backdrop effects */ +} + +/* Timestamp link styling */ +.timestamp-link { + color: #0366d6; + text-decoration: none; + font-weight: bold; + cursor: pointer; +} + +.timestamp-link:hover { + text-decoration: underline; +} + +/* Active timestamp styling */ +.timestamp-active { + background-color: #f0f8ff; /* Light blue background */ + border-radius: 3px; + padding: 2px 4px; + margin: -2px -4px; +} + +/* Highlight the list item containing the active timestamp */ +li.active-marker { + background-color: #f6f8fa; + border-radius: 4px; + padding: 4px 8px; + margin-left: -8px; +} + +/* Make list items clickable */ +.transcript-item { + cursor: pointer; + transition: background-color 0.2s ease; + padding: 4px 8px; + margin-left: -8px; + border-radius: 4px; +} + +.transcript-item:hover { + background-color: #f0f0f0; +} + +/* Keyboard shortcuts styling */ +.keyboard-shortcuts { + text-align: center; + font-size: 14px; + color: #666; + margin-top: 10px; + margin-bottom: 20px; +} + +/* Hide keyboard shortcuts on devices likely without physical keyboards */ +.no-physical-keyboard .keyboard-shortcuts { + display: none; +} + +.keyboard-shortcuts kbd { + background-color: #f7f7f7; + border: 1px solid #ccc; + border-radius: 3px; + box-shadow: 0 1px 0 rgba(0,0,0,0.2); + color: #333; + display: inline-block; + font-family: monospace; + line-height: 1; + margin: 0 2px; + padding: 3px 5px; + white-space: nowrap; +} +.asciinema-player-theme-aider { + /* Foreground (default text) color */ + --term-color-foreground: #444444; /* colour238 */ + + /* Background color */ + --term-color-background: #dadada; /* colour253 */ + + /* Palette of 16 standard ANSI colors */ + --term-color-0: #21222c; + --term-color-1: #ff5555; + --term-color-2: #50fa7b; + --term-color-3: #f1fa8c; + --term-color-4: #bd93f9; + --term-color-5: #ff79c6; + --term-color-6: #8be9fd; + --term-color-7: #f8f8f2; + --term-color-8: #6272a4; + --term-color-9: #ff6e6e; + --term-color-10: #69ff94; + --term-color-11: #ffffa5; + --term-color-12: #d6acff; + --term-color-13: #ff92df; + --term-color-14: #a4ffff; + --term-color-15: #ffffff; +} diff --git a/aider/website/_includes/recording.js b/aider/website/_includes/recording.js new file mode 100644 index 000000000..a2f8cf627 --- /dev/null +++ b/aider/website/_includes/recording.js @@ -0,0 +1,428 @@ +document.addEventListener('DOMContentLoaded', function() { + let player; // Store player reference to make it accessible to click handlers + let globalAudio; // Global audio element to be reused + + // Detect if device likely has no physical keyboard + function detectNoKeyboard() { + // Check if it's a touch device (most mobile devices) + const isTouchDevice = ('ontouchstart' in window) || + (navigator.maxTouchPoints > 0) || + (navigator.msMaxTouchPoints > 0); + + // Check common mobile user agents as additional signal + const isMobileUA = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent); + + // If it's a touch device and has a mobile user agent, likely has no physical keyboard + if (isTouchDevice && isMobileUA) { + document.body.classList.add('no-physical-keyboard'); + } + } + + // Run detection + detectNoKeyboard(); + + // Parse the transcript section to create markers and convert timestamps to links + function parseTranscript() { + const markers = []; + // Find the Commentary heading + const transcriptHeading = Array.from(document.querySelectorAll('h2')).find(el => el.textContent.trim() === 'Commentary'); + + if (transcriptHeading) { + // Get all list items after the transcript heading + let currentElement = transcriptHeading.nextElementSibling; + + while (currentElement && currentElement.tagName === 'UL') { + const listItems = currentElement.querySelectorAll('li'); + + listItems.forEach(item => { + const text = item.textContent.trim(); + const match = text.match(/(\d+):(\d+)\s+(.*)/); + + if (match) { + const minutes = parseInt(match[1], 10); + const seconds = parseInt(match[2], 10); + const timeInSeconds = minutes * 60 + seconds; + const formattedTime = `${minutes}:${seconds.toString().padStart(2, '0')}`; + const message = match[3].trim(); + + // Create link for the timestamp + const timeLink = document.createElement('a'); + timeLink.href = '#'; + timeLink.textContent = formattedTime; + timeLink.className = 'timestamp-link'; + timeLink.dataset.time = timeInSeconds; + timeLink.dataset.message = message; + + // Add click event to seek the player + timeLink.addEventListener('click', function(e) { + e.preventDefault(); + if (player && typeof player.seek === 'function') { + player.seek(timeInSeconds); + player.play(); + + // Also trigger toast and speech + showToast(message); + speakText(message, timeInSeconds); + + // Highlight this timestamp + highlightTimestamp(timeInSeconds); + } + }); + + // Replace text with the link + message + item.textContent = ''; + item.appendChild(timeLink); + item.appendChild(document.createTextNode(' ' + message)); + + // Add class and click handler to the entire list item + item.classList.add('transcript-item'); + item.dataset.time = timeInSeconds; + item.dataset.message = message; + + item.addEventListener('click', function(e) { + // Prevent click event if the user clicked directly on the timestamp link + // This prevents double-firing of the event + if (e.target !== timeLink) { + e.preventDefault(); + if (player && typeof player.seek === 'function') { + player.seek(timeInSeconds); + player.play(); + + // Also trigger toast and speech + showToast(message); + speakText(message, timeInSeconds); + + // Highlight this timestamp + highlightTimestamp(timeInSeconds); + } + } + }); + + markers.push([timeInSeconds, message]); + } + }); + + currentElement = currentElement.nextElementSibling; + } + } + + return markers; + } + + // Parse transcript and create markers + const markers = parseTranscript(); + + // Create player with a single call + player = AsciinemaPlayer.create( + recording_url, + document.getElementById('demo'), + { + speed: 1.25, + idleTimeLimit: 1, + theme: "aider", + poster: "npt:0:01", + markers: markers, + controls: true + } + ); + + // Focus on the player element so keyboard shortcuts work immediately + setTimeout(() => { + // Use setTimeout to ensure the player is fully initialized + if (player && typeof player.focus === 'function') { + player.focus(); + } else { + // If player doesn't have a focus method, try to find and focus the terminal element + const playerElement = document.querySelector('.asciinema-terminal'); + if (playerElement) { + playerElement.focus(); + } else { + // Last resort - try to find element with tabindex + const tabbableElement = document.querySelector('[tabindex]'); + if (tabbableElement) { + tabbableElement.focus(); + } + } + } + }, 100); + + // Track active toast elements + let activeToast = null; + + // Function to display toast notification + function showToast(text) { + // Get the appropriate container based on fullscreen state + let container = document.getElementById('toast-container'); + const isFullscreen = document.fullscreenElement || + document.webkitFullscreenElement || + document.mozFullScreenElement || + document.msFullscreenElement; + + // If in fullscreen, check if we need to create a fullscreen toast container + if (isFullscreen) { + // Target the fullscreen element as the container parent + const fullscreenElement = document.fullscreenElement || + document.webkitFullscreenElement || + document.mozFullScreenElement || + document.msFullscreenElement; + + // Look for an existing fullscreen toast container + let fsContainer = fullscreenElement.querySelector('.fs-toast-container'); + + if (!fsContainer) { + // Create a new container for fullscreen mode + fsContainer = document.createElement('div'); + fsContainer.className = 'toast-container fs-toast-container'; + fsContainer.id = 'fs-toast-container'; + fullscreenElement.appendChild(fsContainer); + } + + container = fsContainer; + } + + // Remove any existing toast + if (activeToast) { + hideToast(activeToast); + } + + // Create toast element + const toast = document.createElement('div'); + toast.className = 'toast-notification'; + toast.textContent = text; + + // Add to container + container.appendChild(toast); + + // Store reference to active toast + activeToast = { + element: toast, + container: container + }; + + // Trigger animation + setTimeout(() => { + toast.style.opacity = '1'; + }, 10); + + return activeToast; + } + + // Function to hide a toast + function hideToast(toastInfo) { + if (!toastInfo || !toastInfo.element) return; + + toastInfo.element.style.opacity = '0'; + setTimeout(() => { + if (toastInfo.container && toastInfo.container.contains(toastInfo.element)) { + toastInfo.container.removeChild(toastInfo.element); + } + + // If this was the active toast, clear the reference + if (activeToast === toastInfo) { + activeToast = null; + } + }, 300); // Wait for fade out animation + } + + // Track if TTS is currently in progress to prevent duplicates + let ttsInProgress = false; + let currentToast = null; + + // Improved browser TTS function + function useBrowserTTS(text) { + // Don't start new speech if already in progress + if (ttsInProgress) { + console.log('Speech synthesis already in progress, skipping'); + return false; + } + + if ('speechSynthesis' in window) { + console.log('Using browser TTS fallback'); + + // Set flag to prevent duplicate speech + ttsInProgress = true; + + // Cancel any ongoing speech + window.speechSynthesis.cancel(); + + const utterance = new SpeechSynthesisUtterance(text); + utterance.rate = 1.0; + utterance.pitch = 1.0; + utterance.volume = 1.0; + + // For iOS, use a shorter utterance if possible + if (/iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream) { + utterance.text = text.length > 100 ? text.substring(0, 100) + '...' : text; + } + + utterance.onstart = () => console.log('Speech started'); + utterance.onend = () => { + console.log('Speech ended'); + ttsInProgress = false; // Reset flag when speech completes + + // Hide toast when speech ends + if (currentToast) { + hideToast(currentToast); + currentToast = null; + } + }; + utterance.onerror = (e) => { + console.warn('Speech error:', e); + ttsInProgress = false; // Reset flag on error + + // Also hide toast on error + if (currentToast) { + hideToast(currentToast); + currentToast = null; + } + }; + + window.speechSynthesis.speak(utterance); + return true; + } + console.warn('SpeechSynthesis not supported'); + return false; + } + + // Function to play pre-generated TTS audio files + function speakText(text, timeInSeconds) { + // Show the toast and keep reference + currentToast = showToast(text); + + // Format time for filename (MM-SS) + const minutes = Math.floor(timeInSeconds / 60); + const seconds = timeInSeconds % 60; + const formattedTime = `${minutes.toString().padStart(2, '0')}-${seconds.toString().padStart(2, '0')}`; + + // Get recording_id from the page or use default from the URL + const recordingId = typeof recording_id !== 'undefined' ? recording_id : + window.location.pathname.split('/').pop().replace('.html', ''); + + // Construct audio file path + const audioPath = `/assets/audio/${recordingId}/${formattedTime}.mp3`; + + // Log for debugging + console.log(`Attempting to play audio: ${audioPath}`); + + // Detect iOS + const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream; + console.log(`Device is iOS: ${isIOS}`); + + // Flag to track if we've already fallen back to TTS + let fallenBackToTTS = false; + + try { + // Create or reuse audio element + if (!globalAudio) { + globalAudio = new Audio(); + console.log("Created new global Audio element"); + } + + // Set up event handlers + globalAudio.onended = () => { + console.log('Audio playback ended'); + // Hide toast when audio ends + if (currentToast) { + hideToast(currentToast); + currentToast = null; + } + }; + + globalAudio.onerror = (e) => { + console.warn(`Audio error: ${e.type}`, e); + if (!fallenBackToTTS) { + fallenBackToTTS = true; + useBrowserTTS(text); + } else if (currentToast) { + // If we've already tried TTS and that failed too, hide the toast + hideToast(currentToast); + currentToast = null; + } + }; + + // For iOS, preload might help with subsequent plays + if (isIOS) { + globalAudio.preload = "auto"; + } + + // Set the new source + globalAudio.src = audioPath; + + // Play with proper error handling + const playPromise = globalAudio.play(); + + if (playPromise !== undefined) { + playPromise.catch(error => { + console.warn(`Play error: ${error.message}`); + + // On iOS, a user gesture might be required + if (isIOS) { + console.log("iOS playback failed, trying SpeechSynthesis"); + } + + if (!fallenBackToTTS) { + fallenBackToTTS = true; + useBrowserTTS(text); + } + }); + } + } catch (e) { + console.error(`Exception in audio playback: ${e.message}`); + useBrowserTTS(text); + } + } + + // Function to highlight the active timestamp in the transcript + function highlightTimestamp(timeInSeconds) { + // Remove previous highlights + document.querySelectorAll('.timestamp-active').forEach(el => { + el.classList.remove('timestamp-active'); + }); + + document.querySelectorAll('.active-marker').forEach(el => { + el.classList.remove('active-marker'); + }); + + // Find the timestamp link with matching time + const timestampLinks = document.querySelectorAll('.timestamp-link'); + let activeLink = null; + + for (const link of timestampLinks) { + if (parseInt(link.dataset.time) === timeInSeconds) { + activeLink = link; + break; + } + } + + if (activeLink) { + // Add highlight class to the link + activeLink.classList.add('timestamp-active'); + + // Also highlight the parent list item + const listItem = activeLink.closest('li'); + if (listItem) { + listItem.classList.add('active-marker'); + + // No longer scrolling into view to avoid shifting focus + } + } + } + + // Add event listener with safety checks + if (player && typeof player.addEventListener === 'function') { + player.addEventListener('marker', function(event) { + try { + const { index, time, label } = event; + console.log(`marker! ${index} - ${time} - ${label}`); + + // Speak the marker label (toast is now shown within speakText) + speakText(label, time); + + // Highlight the corresponding timestamp in the transcript + highlightTimestamp(time); + } catch (error) { + console.error('Error in marker event handler:', error); + } + }); + } +}); diff --git a/aider/website/_includes/recording.md b/aider/website/_includes/recording.md new file mode 100644 index 000000000..f4fc346de --- /dev/null +++ b/aider/website/_includes/recording.md @@ -0,0 +1,34 @@ + + + + + + + +
+
+ +
+
+
+
+
+
+
+
+
aider
+
+
+
+
+
+ +
+ Space Play/pause — + f Fullscreen — + ±5s +
diff --git a/aider/website/_posts/2024-05-02-browser.md b/aider/website/_posts/2024-05-02-browser.md index 8eca20ed2..f48d363da 100644 --- a/aider/website/_posts/2024-05-02-browser.md +++ b/aider/website/_posts/2024-05-02-browser.md @@ -39,9 +39,7 @@ Aider will directly edit the code in your local source files, and [git commit the changes](https://aider.chat/docs/git.html) with sensible commit messages. You can start a new project or work with an existing git repo. -Aider works well with GPT 3.5, GPT-4, GPT-4 Turbo with Vision, -and Claude 3 Opus. -It also supports [connecting to almost any LLM](https://aider.chat/docs/llms.html). +{% include works-best.md %} Use the `--browser` switch to launch the browser version of aider: diff --git a/aider/website/assets/Glass_TTY_VT220.ttf b/aider/website/assets/Glass_TTY_VT220.ttf new file mode 100644 index 000000000..ed8fd8505 Binary files /dev/null and b/aider/website/assets/Glass_TTY_VT220.ttf differ diff --git a/aider/website/assets/asciinema/asciinema-player.css b/aider/website/assets/asciinema/asciinema-player.css new file mode 100644 index 000000000..24fbf3eda --- /dev/null +++ b/aider/website/assets/asciinema/asciinema-player.css @@ -0,0 +1,2366 @@ +div.ap-wrapper { + outline: none; + height: 100%; + display: flex; + justify-content: center; +} +div.ap-wrapper .title-bar { + display: none; + top: -78px; + transition: top 0.15s linear; + position: absolute; + left: 0; + right: 0; + box-sizing: content-box; + font-size: 20px; + line-height: 1em; + padding: 15px; + font-family: sans-serif; + color: white; + background-color: rgba(0, 0, 0, 0.8); +} +div.ap-wrapper .title-bar img { + vertical-align: middle; + height: 48px; + margin-right: 16px; +} +div.ap-wrapper .title-bar a { + color: white; + text-decoration: underline; +} +div.ap-wrapper .title-bar a:hover { + text-decoration: none; +} +div.ap-wrapper:fullscreen { + background-color: #000; + width: 100%; + align-items: center; +} +div.ap-wrapper:fullscreen .title-bar { + display: initial; +} +div.ap-wrapper:fullscreen.hud .title-bar { + top: 0; +} +div.ap-wrapper div.ap-player { + text-align: left; + display: inline-block; + padding: 0px; + position: relative; + box-sizing: content-box; + overflow: hidden; + max-width: 100%; + border-radius: 4px; + font-size: 15px; + background-color: var(--term-color-background); +} +.ap-player { + --term-color-foreground: #ffffff; + --term-color-background: #000000; + --term-color-0: var(--term-color-foreground); + --term-color-1: var(--term-color-foreground); + --term-color-2: var(--term-color-foreground); + --term-color-3: var(--term-color-foreground); + --term-color-4: var(--term-color-foreground); + --term-color-5: var(--term-color-foreground); + --term-color-6: var(--term-color-foreground); + --term-color-7: var(--term-color-foreground); + --term-color-8: var(--term-color-0); + --term-color-9: var(--term-color-1); + --term-color-10: var(--term-color-2); + --term-color-11: var(--term-color-3); + --term-color-12: var(--term-color-4); + --term-color-13: var(--term-color-5); + --term-color-14: var(--term-color-6); + --term-color-15: var(--term-color-7); +} +.ap-player .fg-0 { + --fg: var(--term-color-0); +} +.ap-player .bg-0 { + --bg: var(--term-color-0); +} +.ap-player .fg-1 { + --fg: var(--term-color-1); +} +.ap-player .bg-1 { + --bg: var(--term-color-1); +} +.ap-player .fg-2 { + --fg: var(--term-color-2); +} +.ap-player .bg-2 { + --bg: var(--term-color-2); +} +.ap-player .fg-3 { + --fg: var(--term-color-3); +} +.ap-player .bg-3 { + --bg: var(--term-color-3); +} +.ap-player .fg-4 { + --fg: var(--term-color-4); +} +.ap-player .bg-4 { + --bg: var(--term-color-4); +} +.ap-player .fg-5 { + --fg: var(--term-color-5); +} +.ap-player .bg-5 { + --bg: var(--term-color-5); +} +.ap-player .fg-6 { + --fg: var(--term-color-6); +} +.ap-player .bg-6 { + --bg: var(--term-color-6); +} +.ap-player .fg-7 { + --fg: var(--term-color-7); +} +.ap-player .bg-7 { + --bg: var(--term-color-7); +} +.ap-player .fg-8 { + --fg: var(--term-color-8); +} +.ap-player .bg-8 { + --bg: var(--term-color-8); +} +.ap-player .fg-9 { + --fg: var(--term-color-9); +} +.ap-player .bg-9 { + --bg: var(--term-color-9); +} +.ap-player .fg-10 { + --fg: var(--term-color-10); +} +.ap-player .bg-10 { + --bg: var(--term-color-10); +} +.ap-player .fg-11 { + --fg: var(--term-color-11); +} +.ap-player .bg-11 { + --bg: var(--term-color-11); +} +.ap-player .fg-12 { + --fg: var(--term-color-12); +} +.ap-player .bg-12 { + --bg: var(--term-color-12); +} +.ap-player .fg-13 { + --fg: var(--term-color-13); +} +.ap-player .bg-13 { + --bg: var(--term-color-13); +} +.ap-player .fg-14 { + --fg: var(--term-color-14); +} +.ap-player .bg-14 { + --bg: var(--term-color-14); +} +.ap-player .fg-15 { + --fg: var(--term-color-15); +} +.ap-player .bg-15 { + --bg: var(--term-color-15); +} +.ap-player .fg-8, +.ap-player .fg-9, +.ap-player .fg-10, +.ap-player .fg-11, +.ap-player .fg-12, +.ap-player .fg-13, +.ap-player .fg-14, +.ap-player .fg-15 { + font-weight: bold; +} +pre.ap-terminal { + box-sizing: content-box; + overflow: hidden; + padding: 0; + margin: 0px; + display: block; + white-space: pre; + word-wrap: normal; + word-break: normal; + border-radius: 0; + border-style: solid; + cursor: text; + border-width: 0.75em; + color: var(--term-color-foreground); + background-color: var(--term-color-background); + border-color: var(--term-color-background); + outline: none; + line-height: var(--term-line-height); + font-family: Consolas, Menlo, 'Bitstream Vera Sans Mono', monospace, 'Powerline Symbols'; + font-variant-ligatures: none; +} +pre.ap-terminal .ap-line { + letter-spacing: normal; + overflow: hidden; +} +pre.ap-terminal .ap-line span { + padding: 0; + display: inline-block; + height: 100%; +} +pre.ap-terminal .ap-line { + display: block; + width: 100%; + height: var(--term-line-height); + position: relative; +} +pre.ap-terminal .ap-line span { + position: absolute; + left: calc(100% * var(--offset) / var(--term-cols)); + color: var(--fg); + background-color: var(--bg); +} +pre.ap-terminal .ap-line .ap-inverse { + color: var(--bg); + background-color: var(--fg); +} +pre.ap-terminal .ap-line .cp-2580 { + border-top: calc(0.5 * var(--term-line-height)) solid var(--fg); + box-sizing: border-box; +} +pre.ap-terminal .ap-line .cp-2581 { + border-bottom: calc(0.125 * var(--term-line-height)) solid var(--fg); + box-sizing: border-box; +} +pre.ap-terminal .ap-line .cp-2582 { + border-bottom: calc(0.25 * var(--term-line-height)) solid var(--fg); + box-sizing: border-box; +} +pre.ap-terminal .ap-line .cp-2583 { + border-bottom: calc(0.375 * var(--term-line-height)) solid var(--fg); + box-sizing: border-box; +} +pre.ap-terminal .ap-line .cp-2584 { + border-bottom: calc(0.5 * var(--term-line-height)) solid var(--fg); + box-sizing: border-box; +} +pre.ap-terminal .ap-line .cp-2585 { + border-bottom: calc(0.625 * var(--term-line-height)) solid var(--fg); + box-sizing: border-box; +} +pre.ap-terminal .ap-line .cp-2586 { + border-bottom: calc(0.75 * var(--term-line-height)) solid var(--fg); + box-sizing: border-box; +} +pre.ap-terminal .ap-line .cp-2587 { + border-bottom: calc(0.875 * var(--term-line-height)) solid var(--fg); + box-sizing: border-box; +} +pre.ap-terminal .ap-line .cp-2588 { + background-color: var(--fg); +} +pre.ap-terminal .ap-line .cp-2589 { + border-left: 0.875ch solid var(--fg); + box-sizing: border-box; +} +pre.ap-terminal .ap-line .cp-258a { + border-left: 0.75ch solid var(--fg); + box-sizing: border-box; +} +pre.ap-terminal .ap-line .cp-258b { + border-left: 0.625ch solid var(--fg); + box-sizing: border-box; +} +pre.ap-terminal .ap-line .cp-258c { + border-left: 0.5ch solid var(--fg); + box-sizing: border-box; +} +pre.ap-terminal .ap-line .cp-258d { + border-left: 0.375ch solid var(--fg); + box-sizing: border-box; +} +pre.ap-terminal .ap-line .cp-258e { + border-left: 0.25ch solid var(--fg); + box-sizing: border-box; +} +pre.ap-terminal .ap-line .cp-258f { + border-left: 0.125ch solid var(--fg); + box-sizing: border-box; +} +pre.ap-terminal .ap-line .cp-2590 { + border-right: 0.5ch solid var(--fg); + box-sizing: border-box; +} +pre.ap-terminal .ap-line .cp-2591 { + background-color: color-mix(in srgb, var(--fg) 25%, var(--bg)); +} +pre.ap-terminal .ap-line .cp-2592 { + background-color: color-mix(in srgb, var(--fg) 50%, var(--bg)); +} +pre.ap-terminal .ap-line .cp-2593 { + background-color: color-mix(in srgb, var(--fg) 75%, var(--bg)); +} +pre.ap-terminal .ap-line .cp-2594 { + border-top: calc(0.125 * var(--term-line-height)) solid var(--fg); + box-sizing: border-box; +} +pre.ap-terminal .ap-line .cp-2595 { + border-right: 0.125ch solid var(--fg); + box-sizing: border-box; +} +pre.ap-terminal .ap-line .cp-2596 { + border-right: 0.5ch solid var(--bg); + border-top: calc(0.5 * var(--term-line-height)) solid var(--bg); + background-color: var(--fg); + box-sizing: border-box; +} +pre.ap-terminal .ap-line .cp-2597 { + border-left: 0.5ch solid var(--bg); + border-top: calc(0.5 * var(--term-line-height)) solid var(--bg); + background-color: var(--fg); + box-sizing: border-box; +} +pre.ap-terminal .ap-line .cp-2598 { + border-right: 0.5ch solid var(--bg); + border-bottom: calc(0.5 * var(--term-line-height)) solid var(--bg); + background-color: var(--fg); + box-sizing: border-box; +} +pre.ap-terminal .ap-line .cp-2599 { + border-left: 0.5ch solid var(--fg); + border-bottom: calc(0.5 * var(--term-line-height)) solid var(--fg); + box-sizing: border-box; +} +pre.ap-terminal .ap-line .cp-259a { + box-sizing: border-box; +} +pre.ap-terminal .ap-line .cp-259a::before, +pre.ap-terminal .ap-line .cp-259a::after { + content: ''; + position: absolute; + width: 0.5ch; + height: calc(0.5 * var(--term-line-height)); + background-color: var(--fg); +} +pre.ap-terminal .ap-line .cp-259a::before { + top: 0; + left: 0; +} +pre.ap-terminal .ap-line .cp-259a::after { + bottom: 0; + right: 0; +} +pre.ap-terminal .ap-line .cp-259b { + border-left: 0.5ch solid var(--fg); + border-top: calc(0.5 * var(--term-line-height)) solid var(--fg); + box-sizing: border-box; +} +pre.ap-terminal .ap-line .cp-259c { + border-right: 0.5ch solid var(--fg); + border-top: calc(0.5 * var(--term-line-height)) solid var(--fg); + box-sizing: border-box; +} +pre.ap-terminal .ap-line .cp-259d { + border-left: 0.5ch solid var(--bg); + border-bottom: calc(0.5 * var(--term-line-height)) solid var(--bg); + background-color: var(--fg); + box-sizing: border-box; +} +pre.ap-terminal .ap-line .cp-259e { + box-sizing: border-box; +} +pre.ap-terminal .ap-line .cp-259e::before, +pre.ap-terminal .ap-line .cp-259e::after { + content: ''; + position: absolute; + width: 0.5ch; + height: calc(0.5 * var(--term-line-height)); + background-color: var(--fg); +} +pre.ap-terminal .ap-line .cp-259e::before { + top: 0; + right: 0; +} +pre.ap-terminal .ap-line .cp-259e::after { + bottom: 0; + left: 0; +} +pre.ap-terminal .ap-line .cp-259f { + border-right: 0.5ch solid var(--fg); + border-bottom: calc(0.5 * var(--term-line-height)) solid var(--fg); + box-sizing: border-box; +} +pre.ap-terminal .ap-line .cp-e0b0 { + border-left: 1ch solid var(--fg); + border-top: calc(0.5 * var(--term-line-height)) solid transparent; + border-bottom: calc(0.5 * var(--term-line-height)) solid transparent; + box-sizing: border-box; +} +pre.ap-terminal .ap-line .cp-e0b2 { + border-right: 1ch solid var(--fg); + border-top: calc(0.5 * var(--term-line-height)) solid transparent; + border-bottom: calc(0.5 * var(--term-line-height)) solid transparent; + box-sizing: border-box; +} +pre.ap-terminal.ap-cursor-on .ap-line .ap-cursor { + color: var(--bg); + background-color: var(--fg); + border-radius: 0.05em; +} +pre.ap-terminal.ap-cursor-on .ap-line .ap-cursor.ap-inverse { + color: var(--fg); + background-color: var(--bg); +} +pre.ap-terminal:not(.ap-blink) .ap-line .ap-blink { + color: transparent; + border-color: transparent; +} +pre.ap-terminal .ap-bright { + font-weight: bold; +} +pre.ap-terminal .ap-faint { + opacity: 0.5; +} +pre.ap-terminal .ap-underline { + text-decoration: underline; +} +pre.ap-terminal .ap-italic { + font-style: italic; +} +pre.ap-terminal .ap-strikethrough { + text-decoration: line-through; +} +.ap-line span { + --fg: var(--term-color-foreground); + --bg: var(--term-color-background); +} +div.ap-player div.ap-control-bar { + width: 100%; + height: 32px; + display: flex; + justify-content: space-between; + align-items: stretch; + color: var(--term-color-foreground); + box-sizing: content-box; + line-height: 1; + position: absolute; + bottom: 0; + left: 0; + opacity: 0; + transition: opacity 0.15s linear; + user-select: none; + border-top: 2px solid color-mix(in oklab, var(--term-color-background) 80%, var(--term-color-foreground)); + z-index: 30; +} +div.ap-player div.ap-control-bar * { + box-sizing: inherit; +} +div.ap-control-bar svg.ap-icon path { + fill: var(--term-color-foreground); +} +div.ap-control-bar span.ap-button { + display: flex; + flex: 0 0 auto; + cursor: pointer; +} +div.ap-control-bar span.ap-playback-button { + width: 12px; + height: 12px; + padding: 10px; +} +div.ap-control-bar span.ap-playback-button svg { + height: 12px; + width: 12px; +} +div.ap-control-bar span.ap-timer { + display: flex; + flex: 0 0 auto; + min-width: 50px; + margin: 0 10px; + height: 100%; + text-align: center; + font-size: 13px; + line-height: 100%; + cursor: default; +} +div.ap-control-bar span.ap-timer span { + font-family: Consolas, Menlo, 'Bitstream Vera Sans Mono', monospace; + font-size: inherit; + font-weight: 600; + margin: auto; +} +div.ap-control-bar span.ap-timer .ap-time-remaining { + display: none; +} +div.ap-control-bar span.ap-timer:hover .ap-time-elapsed { + display: none; +} +div.ap-control-bar span.ap-timer:hover .ap-time-remaining { + display: flex; +} +div.ap-control-bar .ap-progressbar { + display: block; + flex: 1 1 auto; + height: 100%; + padding: 0 10px; +} +div.ap-control-bar .ap-progressbar .ap-bar { + display: block; + position: relative; + cursor: default; + height: 100%; + font-size: 0; +} +div.ap-control-bar .ap-progressbar .ap-bar .ap-gutter { + display: block; + position: absolute; + top: 15px; + left: 0; + right: 0; + height: 3px; +} +div.ap-control-bar .ap-progressbar .ap-bar .ap-gutter-empty { + background-color: color-mix(in oklab, var(--term-color-foreground) 20%, var(--term-color-background)); +} +div.ap-control-bar .ap-progressbar .ap-bar .ap-gutter-full { + width: 100%; + transform-origin: left center; + background-color: var(--term-color-foreground); + border-radius: 3px; +} +div.ap-control-bar.ap-seekable .ap-progressbar .ap-bar { + cursor: pointer; +} +div.ap-control-bar .ap-fullscreen-button { + width: 14px; + height: 14px; + padding: 9px; +} +div.ap-control-bar .ap-fullscreen-button svg { + width: 14px; + height: 14px; +} +div.ap-control-bar .ap-fullscreen-button svg.ap-icon-fullscreen-on { + display: inline; +} +div.ap-control-bar .ap-fullscreen-button svg.ap-icon-fullscreen-off { + display: none; +} +div.ap-control-bar .ap-fullscreen-button .ap-tooltip { + right: 5px; + left: initial; + transform: none; +} +div.ap-control-bar .ap-kbd-button { + height: 14px; + padding: 9px; + margin: 0 4px; +} +div.ap-control-bar .ap-kbd-button svg { + width: 26px; + height: 14px; +} +div.ap-control-bar .ap-kbd-button .ap-tooltip { + right: 5px; + left: initial; + transform: none; +} +div.ap-wrapper.ap-hud .ap-control-bar { + opacity: 1; +} +div.ap-wrapper:fullscreen .ap-fullscreen-button svg.ap-icon-fullscreen-on { + display: none; +} +div.ap-wrapper:fullscreen .ap-fullscreen-button svg.ap-icon-fullscreen-off { + display: inline; +} +span.ap-progressbar span.ap-marker-container { + display: block; + top: 0; + bottom: 0; + width: 21px; + position: absolute; + margin-left: -10px; +} +span.ap-marker-container span.ap-marker { + display: block; + top: 13px; + bottom: 12px; + left: 7px; + right: 7px; + background-color: color-mix(in oklab, var(--term-color-foreground) 33%, var(--term-color-background)); + position: absolute; + transition: top 0.1s, bottom 0.1s, left 0.1s, right 0.1s, background-color 0.1s; + border-radius: 50%; +} +span.ap-marker-container span.ap-marker.ap-marker-past { + background-color: var(--term-color-foreground); +} +span.ap-marker-container span.ap-marker:hover, +span.ap-marker-container:hover span.ap-marker { + background-color: var(--term-color-foreground); + top: 11px; + bottom: 10px; + left: 5px; + right: 5px; +} +.ap-tooltip-container span.ap-tooltip { + visibility: hidden; + background-color: var(--term-color-foreground); + color: var(--term-color-background); + font-family: Consolas, Menlo, 'Bitstream Vera Sans Mono', monospace; + font-weight: bold; + text-align: center; + padding: 0 0.5em; + border-radius: 4px; + position: absolute; + z-index: 1; + white-space: nowrap; + /* Prevents the text from wrapping and makes sure the tooltip width adapts to the text length */ + font-size: 13px; + line-height: 2em; + bottom: 100%; + left: 50%; + transform: translateX(-50%); +} +.ap-tooltip-container:hover span.ap-tooltip { + visibility: visible; +} +.ap-player .ap-overlay { + z-index: 10; + background-repeat: no-repeat; + background-position: center; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + display: flex; + justify-content: center; + align-items: center; +} +.ap-player .ap-overlay-start { + cursor: pointer; +} +.ap-player .ap-overlay-start .ap-play-button { + font-size: 0px; + position: absolute; + left: 0; + top: 0; + right: 0; + bottom: 0; + text-align: center; + color: white; + height: 80px; + max-height: 66%; + margin: auto; +} +.ap-player .ap-overlay-start .ap-play-button div { + height: 100%; +} +.ap-player .ap-overlay-start .ap-play-button div span { + height: 100%; + display: block; +} +.ap-player .ap-overlay-start .ap-play-button div span svg { + height: 100%; +} +.ap-player .ap-overlay-start .ap-play-button svg { + filter: drop-shadow(0px 0px 5px rgba(0, 0, 0, 0.4)); +} +.ap-player .ap-overlay-loading .ap-loader { + width: 48px; + height: 48px; + border-radius: 50%; + display: inline-block; + position: relative; + border: 10px solid; + border-color: rgba(255, 255, 255, 0.3) rgba(255, 255, 255, 0.5) rgba(255, 255, 255, 0.7) #ffffff; + border-color: color-mix(in srgb, var(--term-color-foreground) 30%, var(--term-color-background)) color-mix(in srgb, var(--term-color-foreground) 50%, var(--term-color-background)) color-mix(in srgb, var(--term-color-foreground) 70%, var(--term-color-background)) color-mix(in srgb, var(--term-color-foreground) 100%, var(--term-color-background)); + box-sizing: border-box; + animation: ap-loader-rotation 1s linear infinite; +} +.ap-player .ap-overlay-info { + background-color: var(--term-color-background); +} +.ap-player .ap-overlay-info span { + font-family: Consolas, Menlo, 'Bitstream Vera Sans Mono', monospace, 'Powerline Symbols'; + font-variant-ligatures: none; + font-size: 2em; + color: var(--term-color-foreground); +} +.ap-player .ap-overlay-info span .ap-line { + letter-spacing: normal; + overflow: hidden; +} +.ap-player .ap-overlay-info span .ap-line span { + padding: 0; + display: inline-block; + height: 100%; +} +.ap-player .ap-overlay-help { + background-color: rgba(0, 0, 0, 0.8); + container-type: inline-size; +} +.ap-player .ap-overlay-help > div { + font-family: Consolas, Menlo, 'Bitstream Vera Sans Mono', monospace, 'Powerline Symbols'; + font-variant-ligatures: none; + max-width: 85%; + max-height: 85%; + font-size: 18px; + color: var(--term-color-foreground); + box-sizing: border-box; + margin-bottom: 32px; +} +.ap-player .ap-overlay-help > div .ap-line { + letter-spacing: normal; + overflow: hidden; +} +.ap-player .ap-overlay-help > div .ap-line span { + padding: 0; + display: inline-block; + height: 100%; +} +.ap-player .ap-overlay-help > div div { + padding: calc(min(4cqw, 40px)); + font-size: calc(min(1.9cqw, 18px)); + background-color: var(--term-color-background); + border: 1px solid color-mix(in oklab, var(--term-color-background) 90%, var(--term-color-foreground)); + border-radius: 6px; +} +.ap-player .ap-overlay-help > div div p { + font-weight: bold; + margin: 0 0 2em 0; +} +.ap-player .ap-overlay-help > div div ul { + list-style: none; + padding: 0; +} +.ap-player .ap-overlay-help > div div ul li { + margin: 0 0 0.75em 0; +} +.ap-player .ap-overlay-help > div div kbd { + color: var(--term-color-background); + background-color: var(--term-color-foreground); + padding: 0.2em 0.5em; + border-radius: 0.2em; + font-family: inherit; + font-size: 0.85em; + border: none; + margin: 0; +} +.ap-player .ap-overlay-error span { + font-size: 8em; +} +@keyframes ap-loader-rotation { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } +} +.ap-terminal .fg-16 { + --fg: #000000; +} +.ap-terminal .bg-16 { + --bg: #000000; +} +.ap-terminal .fg-17 { + --fg: #00005f; +} +.ap-terminal .bg-17 { + --bg: #00005f; +} +.ap-terminal .fg-18 { + --fg: #000087; +} +.ap-terminal .bg-18 { + --bg: #000087; +} +.ap-terminal .fg-19 { + --fg: #0000af; +} +.ap-terminal .bg-19 { + --bg: #0000af; +} +.ap-terminal .fg-20 { + --fg: #0000d7; +} +.ap-terminal .bg-20 { + --bg: #0000d7; +} +.ap-terminal .fg-21 { + --fg: #0000ff; +} +.ap-terminal .bg-21 { + --bg: #0000ff; +} +.ap-terminal .fg-22 { + --fg: #005f00; +} +.ap-terminal .bg-22 { + --bg: #005f00; +} +.ap-terminal .fg-23 { + --fg: #005f5f; +} +.ap-terminal .bg-23 { + --bg: #005f5f; +} +.ap-terminal .fg-24 { + --fg: #005f87; +} +.ap-terminal .bg-24 { + --bg: #005f87; +} +.ap-terminal .fg-25 { + --fg: #005faf; +} +.ap-terminal .bg-25 { + --bg: #005faf; +} +.ap-terminal .fg-26 { + --fg: #005fd7; +} +.ap-terminal .bg-26 { + --bg: #005fd7; +} +.ap-terminal .fg-27 { + --fg: #005fff; +} +.ap-terminal .bg-27 { + --bg: #005fff; +} +.ap-terminal .fg-28 { + --fg: #008700; +} +.ap-terminal .bg-28 { + --bg: #008700; +} +.ap-terminal .fg-29 { + --fg: #00875f; +} +.ap-terminal .bg-29 { + --bg: #00875f; +} +.ap-terminal .fg-30 { + --fg: #008787; +} +.ap-terminal .bg-30 { + --bg: #008787; +} +.ap-terminal .fg-31 { + --fg: #0087af; +} +.ap-terminal .bg-31 { + --bg: #0087af; +} +.ap-terminal .fg-32 { + --fg: #0087d7; +} +.ap-terminal .bg-32 { + --bg: #0087d7; +} +.ap-terminal .fg-33 { + --fg: #0087ff; +} +.ap-terminal .bg-33 { + --bg: #0087ff; +} +.ap-terminal .fg-34 { + --fg: #00af00; +} +.ap-terminal .bg-34 { + --bg: #00af00; +} +.ap-terminal .fg-35 { + --fg: #00af5f; +} +.ap-terminal .bg-35 { + --bg: #00af5f; +} +.ap-terminal .fg-36 { + --fg: #00af87; +} +.ap-terminal .bg-36 { + --bg: #00af87; +} +.ap-terminal .fg-37 { + --fg: #00afaf; +} +.ap-terminal .bg-37 { + --bg: #00afaf; +} +.ap-terminal .fg-38 { + --fg: #00afd7; +} +.ap-terminal .bg-38 { + --bg: #00afd7; +} +.ap-terminal .fg-39 { + --fg: #00afff; +} +.ap-terminal .bg-39 { + --bg: #00afff; +} +.ap-terminal .fg-40 { + --fg: #00d700; +} +.ap-terminal .bg-40 { + --bg: #00d700; +} +.ap-terminal .fg-41 { + --fg: #00d75f; +} +.ap-terminal .bg-41 { + --bg: #00d75f; +} +.ap-terminal .fg-42 { + --fg: #00d787; +} +.ap-terminal .bg-42 { + --bg: #00d787; +} +.ap-terminal .fg-43 { + --fg: #00d7af; +} +.ap-terminal .bg-43 { + --bg: #00d7af; +} +.ap-terminal .fg-44 { + --fg: #00d7d7; +} +.ap-terminal .bg-44 { + --bg: #00d7d7; +} +.ap-terminal .fg-45 { + --fg: #00d7ff; +} +.ap-terminal .bg-45 { + --bg: #00d7ff; +} +.ap-terminal .fg-46 { + --fg: #00ff00; +} +.ap-terminal .bg-46 { + --bg: #00ff00; +} +.ap-terminal .fg-47 { + --fg: #00ff5f; +} +.ap-terminal .bg-47 { + --bg: #00ff5f; +} +.ap-terminal .fg-48 { + --fg: #00ff87; +} +.ap-terminal .bg-48 { + --bg: #00ff87; +} +.ap-terminal .fg-49 { + --fg: #00ffaf; +} +.ap-terminal .bg-49 { + --bg: #00ffaf; +} +.ap-terminal .fg-50 { + --fg: #00ffd7; +} +.ap-terminal .bg-50 { + --bg: #00ffd7; +} +.ap-terminal .fg-51 { + --fg: #00ffff; +} +.ap-terminal .bg-51 { + --bg: #00ffff; +} +.ap-terminal .fg-52 { + --fg: #5f0000; +} +.ap-terminal .bg-52 { + --bg: #5f0000; +} +.ap-terminal .fg-53 { + --fg: #5f005f; +} +.ap-terminal .bg-53 { + --bg: #5f005f; +} +.ap-terminal .fg-54 { + --fg: #5f0087; +} +.ap-terminal .bg-54 { + --bg: #5f0087; +} +.ap-terminal .fg-55 { + --fg: #5f00af; +} +.ap-terminal .bg-55 { + --bg: #5f00af; +} +.ap-terminal .fg-56 { + --fg: #5f00d7; +} +.ap-terminal .bg-56 { + --bg: #5f00d7; +} +.ap-terminal .fg-57 { + --fg: #5f00ff; +} +.ap-terminal .bg-57 { + --bg: #5f00ff; +} +.ap-terminal .fg-58 { + --fg: #5f5f00; +} +.ap-terminal .bg-58 { + --bg: #5f5f00; +} +.ap-terminal .fg-59 { + --fg: #5f5f5f; +} +.ap-terminal .bg-59 { + --bg: #5f5f5f; +} +.ap-terminal .fg-60 { + --fg: #5f5f87; +} +.ap-terminal .bg-60 { + --bg: #5f5f87; +} +.ap-terminal .fg-61 { + --fg: #5f5faf; +} +.ap-terminal .bg-61 { + --bg: #5f5faf; +} +.ap-terminal .fg-62 { + --fg: #5f5fd7; +} +.ap-terminal .bg-62 { + --bg: #5f5fd7; +} +.ap-terminal .fg-63 { + --fg: #5f5fff; +} +.ap-terminal .bg-63 { + --bg: #5f5fff; +} +.ap-terminal .fg-64 { + --fg: #5f8700; +} +.ap-terminal .bg-64 { + --bg: #5f8700; +} +.ap-terminal .fg-65 { + --fg: #5f875f; +} +.ap-terminal .bg-65 { + --bg: #5f875f; +} +.ap-terminal .fg-66 { + --fg: #5f8787; +} +.ap-terminal .bg-66 { + --bg: #5f8787; +} +.ap-terminal .fg-67 { + --fg: #5f87af; +} +.ap-terminal .bg-67 { + --bg: #5f87af; +} +.ap-terminal .fg-68 { + --fg: #5f87d7; +} +.ap-terminal .bg-68 { + --bg: #5f87d7; +} +.ap-terminal .fg-69 { + --fg: #5f87ff; +} +.ap-terminal .bg-69 { + --bg: #5f87ff; +} +.ap-terminal .fg-70 { + --fg: #5faf00; +} +.ap-terminal .bg-70 { + --bg: #5faf00; +} +.ap-terminal .fg-71 { + --fg: #5faf5f; +} +.ap-terminal .bg-71 { + --bg: #5faf5f; +} +.ap-terminal .fg-72 { + --fg: #5faf87; +} +.ap-terminal .bg-72 { + --bg: #5faf87; +} +.ap-terminal .fg-73 { + --fg: #5fafaf; +} +.ap-terminal .bg-73 { + --bg: #5fafaf; +} +.ap-terminal .fg-74 { + --fg: #5fafd7; +} +.ap-terminal .bg-74 { + --bg: #5fafd7; +} +.ap-terminal .fg-75 { + --fg: #5fafff; +} +.ap-terminal .bg-75 { + --bg: #5fafff; +} +.ap-terminal .fg-76 { + --fg: #5fd700; +} +.ap-terminal .bg-76 { + --bg: #5fd700; +} +.ap-terminal .fg-77 { + --fg: #5fd75f; +} +.ap-terminal .bg-77 { + --bg: #5fd75f; +} +.ap-terminal .fg-78 { + --fg: #5fd787; +} +.ap-terminal .bg-78 { + --bg: #5fd787; +} +.ap-terminal .fg-79 { + --fg: #5fd7af; +} +.ap-terminal .bg-79 { + --bg: #5fd7af; +} +.ap-terminal .fg-80 { + --fg: #5fd7d7; +} +.ap-terminal .bg-80 { + --bg: #5fd7d7; +} +.ap-terminal .fg-81 { + --fg: #5fd7ff; +} +.ap-terminal .bg-81 { + --bg: #5fd7ff; +} +.ap-terminal .fg-82 { + --fg: #5fff00; +} +.ap-terminal .bg-82 { + --bg: #5fff00; +} +.ap-terminal .fg-83 { + --fg: #5fff5f; +} +.ap-terminal .bg-83 { + --bg: #5fff5f; +} +.ap-terminal .fg-84 { + --fg: #5fff87; +} +.ap-terminal .bg-84 { + --bg: #5fff87; +} +.ap-terminal .fg-85 { + --fg: #5fffaf; +} +.ap-terminal .bg-85 { + --bg: #5fffaf; +} +.ap-terminal .fg-86 { + --fg: #5fffd7; +} +.ap-terminal .bg-86 { + --bg: #5fffd7; +} +.ap-terminal .fg-87 { + --fg: #5fffff; +} +.ap-terminal .bg-87 { + --bg: #5fffff; +} +.ap-terminal .fg-88 { + --fg: #870000; +} +.ap-terminal .bg-88 { + --bg: #870000; +} +.ap-terminal .fg-89 { + --fg: #87005f; +} +.ap-terminal .bg-89 { + --bg: #87005f; +} +.ap-terminal .fg-90 { + --fg: #870087; +} +.ap-terminal .bg-90 { + --bg: #870087; +} +.ap-terminal .fg-91 { + --fg: #8700af; +} +.ap-terminal .bg-91 { + --bg: #8700af; +} +.ap-terminal .fg-92 { + --fg: #8700d7; +} +.ap-terminal .bg-92 { + --bg: #8700d7; +} +.ap-terminal .fg-93 { + --fg: #8700ff; +} +.ap-terminal .bg-93 { + --bg: #8700ff; +} +.ap-terminal .fg-94 { + --fg: #875f00; +} +.ap-terminal .bg-94 { + --bg: #875f00; +} +.ap-terminal .fg-95 { + --fg: #875f5f; +} +.ap-terminal .bg-95 { + --bg: #875f5f; +} +.ap-terminal .fg-96 { + --fg: #875f87; +} +.ap-terminal .bg-96 { + --bg: #875f87; +} +.ap-terminal .fg-97 { + --fg: #875faf; +} +.ap-terminal .bg-97 { + --bg: #875faf; +} +.ap-terminal .fg-98 { + --fg: #875fd7; +} +.ap-terminal .bg-98 { + --bg: #875fd7; +} +.ap-terminal .fg-99 { + --fg: #875fff; +} +.ap-terminal .bg-99 { + --bg: #875fff; +} +.ap-terminal .fg-100 { + --fg: #878700; +} +.ap-terminal .bg-100 { + --bg: #878700; +} +.ap-terminal .fg-101 { + --fg: #87875f; +} +.ap-terminal .bg-101 { + --bg: #87875f; +} +.ap-terminal .fg-102 { + --fg: #878787; +} +.ap-terminal .bg-102 { + --bg: #878787; +} +.ap-terminal .fg-103 { + --fg: #8787af; +} +.ap-terminal .bg-103 { + --bg: #8787af; +} +.ap-terminal .fg-104 { + --fg: #8787d7; +} +.ap-terminal .bg-104 { + --bg: #8787d7; +} +.ap-terminal .fg-105 { + --fg: #8787ff; +} +.ap-terminal .bg-105 { + --bg: #8787ff; +} +.ap-terminal .fg-106 { + --fg: #87af00; +} +.ap-terminal .bg-106 { + --bg: #87af00; +} +.ap-terminal .fg-107 { + --fg: #87af5f; +} +.ap-terminal .bg-107 { + --bg: #87af5f; +} +.ap-terminal .fg-108 { + --fg: #87af87; +} +.ap-terminal .bg-108 { + --bg: #87af87; +} +.ap-terminal .fg-109 { + --fg: #87afaf; +} +.ap-terminal .bg-109 { + --bg: #87afaf; +} +.ap-terminal .fg-110 { + --fg: #87afd7; +} +.ap-terminal .bg-110 { + --bg: #87afd7; +} +.ap-terminal .fg-111 { + --fg: #87afff; +} +.ap-terminal .bg-111 { + --bg: #87afff; +} +.ap-terminal .fg-112 { + --fg: #87d700; +} +.ap-terminal .bg-112 { + --bg: #87d700; +} +.ap-terminal .fg-113 { + --fg: #87d75f; +} +.ap-terminal .bg-113 { + --bg: #87d75f; +} +.ap-terminal .fg-114 { + --fg: #87d787; +} +.ap-terminal .bg-114 { + --bg: #87d787; +} +.ap-terminal .fg-115 { + --fg: #87d7af; +} +.ap-terminal .bg-115 { + --bg: #87d7af; +} +.ap-terminal .fg-116 { + --fg: #87d7d7; +} +.ap-terminal .bg-116 { + --bg: #87d7d7; +} +.ap-terminal .fg-117 { + --fg: #87d7ff; +} +.ap-terminal .bg-117 { + --bg: #87d7ff; +} +.ap-terminal .fg-118 { + --fg: #87ff00; +} +.ap-terminal .bg-118 { + --bg: #87ff00; +} +.ap-terminal .fg-119 { + --fg: #87ff5f; +} +.ap-terminal .bg-119 { + --bg: #87ff5f; +} +.ap-terminal .fg-120 { + --fg: #87ff87; +} +.ap-terminal .bg-120 { + --bg: #87ff87; +} +.ap-terminal .fg-121 { + --fg: #87ffaf; +} +.ap-terminal .bg-121 { + --bg: #87ffaf; +} +.ap-terminal .fg-122 { + --fg: #87ffd7; +} +.ap-terminal .bg-122 { + --bg: #87ffd7; +} +.ap-terminal .fg-123 { + --fg: #87ffff; +} +.ap-terminal .bg-123 { + --bg: #87ffff; +} +.ap-terminal .fg-124 { + --fg: #af0000; +} +.ap-terminal .bg-124 { + --bg: #af0000; +} +.ap-terminal .fg-125 { + --fg: #af005f; +} +.ap-terminal .bg-125 { + --bg: #af005f; +} +.ap-terminal .fg-126 { + --fg: #af0087; +} +.ap-terminal .bg-126 { + --bg: #af0087; +} +.ap-terminal .fg-127 { + --fg: #af00af; +} +.ap-terminal .bg-127 { + --bg: #af00af; +} +.ap-terminal .fg-128 { + --fg: #af00d7; +} +.ap-terminal .bg-128 { + --bg: #af00d7; +} +.ap-terminal .fg-129 { + --fg: #af00ff; +} +.ap-terminal .bg-129 { + --bg: #af00ff; +} +.ap-terminal .fg-130 { + --fg: #af5f00; +} +.ap-terminal .bg-130 { + --bg: #af5f00; +} +.ap-terminal .fg-131 { + --fg: #af5f5f; +} +.ap-terminal .bg-131 { + --bg: #af5f5f; +} +.ap-terminal .fg-132 { + --fg: #af5f87; +} +.ap-terminal .bg-132 { + --bg: #af5f87; +} +.ap-terminal .fg-133 { + --fg: #af5faf; +} +.ap-terminal .bg-133 { + --bg: #af5faf; +} +.ap-terminal .fg-134 { + --fg: #af5fd7; +} +.ap-terminal .bg-134 { + --bg: #af5fd7; +} +.ap-terminal .fg-135 { + --fg: #af5fff; +} +.ap-terminal .bg-135 { + --bg: #af5fff; +} +.ap-terminal .fg-136 { + --fg: #af8700; +} +.ap-terminal .bg-136 { + --bg: #af8700; +} +.ap-terminal .fg-137 { + --fg: #af875f; +} +.ap-terminal .bg-137 { + --bg: #af875f; +} +.ap-terminal .fg-138 { + --fg: #af8787; +} +.ap-terminal .bg-138 { + --bg: #af8787; +} +.ap-terminal .fg-139 { + --fg: #af87af; +} +.ap-terminal .bg-139 { + --bg: #af87af; +} +.ap-terminal .fg-140 { + --fg: #af87d7; +} +.ap-terminal .bg-140 { + --bg: #af87d7; +} +.ap-terminal .fg-141 { + --fg: #af87ff; +} +.ap-terminal .bg-141 { + --bg: #af87ff; +} +.ap-terminal .fg-142 { + --fg: #afaf00; +} +.ap-terminal .bg-142 { + --bg: #afaf00; +} +.ap-terminal .fg-143 { + --fg: #afaf5f; +} +.ap-terminal .bg-143 { + --bg: #afaf5f; +} +.ap-terminal .fg-144 { + --fg: #afaf87; +} +.ap-terminal .bg-144 { + --bg: #afaf87; +} +.ap-terminal .fg-145 { + --fg: #afafaf; +} +.ap-terminal .bg-145 { + --bg: #afafaf; +} +.ap-terminal .fg-146 { + --fg: #afafd7; +} +.ap-terminal .bg-146 { + --bg: #afafd7; +} +.ap-terminal .fg-147 { + --fg: #afafff; +} +.ap-terminal .bg-147 { + --bg: #afafff; +} +.ap-terminal .fg-148 { + --fg: #afd700; +} +.ap-terminal .bg-148 { + --bg: #afd700; +} +.ap-terminal .fg-149 { + --fg: #afd75f; +} +.ap-terminal .bg-149 { + --bg: #afd75f; +} +.ap-terminal .fg-150 { + --fg: #afd787; +} +.ap-terminal .bg-150 { + --bg: #afd787; +} +.ap-terminal .fg-151 { + --fg: #afd7af; +} +.ap-terminal .bg-151 { + --bg: #afd7af; +} +.ap-terminal .fg-152 { + --fg: #afd7d7; +} +.ap-terminal .bg-152 { + --bg: #afd7d7; +} +.ap-terminal .fg-153 { + --fg: #afd7ff; +} +.ap-terminal .bg-153 { + --bg: #afd7ff; +} +.ap-terminal .fg-154 { + --fg: #afff00; +} +.ap-terminal .bg-154 { + --bg: #afff00; +} +.ap-terminal .fg-155 { + --fg: #afff5f; +} +.ap-terminal .bg-155 { + --bg: #afff5f; +} +.ap-terminal .fg-156 { + --fg: #afff87; +} +.ap-terminal .bg-156 { + --bg: #afff87; +} +.ap-terminal .fg-157 { + --fg: #afffaf; +} +.ap-terminal .bg-157 { + --bg: #afffaf; +} +.ap-terminal .fg-158 { + --fg: #afffd7; +} +.ap-terminal .bg-158 { + --bg: #afffd7; +} +.ap-terminal .fg-159 { + --fg: #afffff; +} +.ap-terminal .bg-159 { + --bg: #afffff; +} +.ap-terminal .fg-160 { + --fg: #d70000; +} +.ap-terminal .bg-160 { + --bg: #d70000; +} +.ap-terminal .fg-161 { + --fg: #d7005f; +} +.ap-terminal .bg-161 { + --bg: #d7005f; +} +.ap-terminal .fg-162 { + --fg: #d70087; +} +.ap-terminal .bg-162 { + --bg: #d70087; +} +.ap-terminal .fg-163 { + --fg: #d700af; +} +.ap-terminal .bg-163 { + --bg: #d700af; +} +.ap-terminal .fg-164 { + --fg: #d700d7; +} +.ap-terminal .bg-164 { + --bg: #d700d7; +} +.ap-terminal .fg-165 { + --fg: #d700ff; +} +.ap-terminal .bg-165 { + --bg: #d700ff; +} +.ap-terminal .fg-166 { + --fg: #d75f00; +} +.ap-terminal .bg-166 { + --bg: #d75f00; +} +.ap-terminal .fg-167 { + --fg: #d75f5f; +} +.ap-terminal .bg-167 { + --bg: #d75f5f; +} +.ap-terminal .fg-168 { + --fg: #d75f87; +} +.ap-terminal .bg-168 { + --bg: #d75f87; +} +.ap-terminal .fg-169 { + --fg: #d75faf; +} +.ap-terminal .bg-169 { + --bg: #d75faf; +} +.ap-terminal .fg-170 { + --fg: #d75fd7; +} +.ap-terminal .bg-170 { + --bg: #d75fd7; +} +.ap-terminal .fg-171 { + --fg: #d75fff; +} +.ap-terminal .bg-171 { + --bg: #d75fff; +} +.ap-terminal .fg-172 { + --fg: #d78700; +} +.ap-terminal .bg-172 { + --bg: #d78700; +} +.ap-terminal .fg-173 { + --fg: #d7875f; +} +.ap-terminal .bg-173 { + --bg: #d7875f; +} +.ap-terminal .fg-174 { + --fg: #d78787; +} +.ap-terminal .bg-174 { + --bg: #d78787; +} +.ap-terminal .fg-175 { + --fg: #d787af; +} +.ap-terminal .bg-175 { + --bg: #d787af; +} +.ap-terminal .fg-176 { + --fg: #d787d7; +} +.ap-terminal .bg-176 { + --bg: #d787d7; +} +.ap-terminal .fg-177 { + --fg: #d787ff; +} +.ap-terminal .bg-177 { + --bg: #d787ff; +} +.ap-terminal .fg-178 { + --fg: #d7af00; +} +.ap-terminal .bg-178 { + --bg: #d7af00; +} +.ap-terminal .fg-179 { + --fg: #d7af5f; +} +.ap-terminal .bg-179 { + --bg: #d7af5f; +} +.ap-terminal .fg-180 { + --fg: #d7af87; +} +.ap-terminal .bg-180 { + --bg: #d7af87; +} +.ap-terminal .fg-181 { + --fg: #d7afaf; +} +.ap-terminal .bg-181 { + --bg: #d7afaf; +} +.ap-terminal .fg-182 { + --fg: #d7afd7; +} +.ap-terminal .bg-182 { + --bg: #d7afd7; +} +.ap-terminal .fg-183 { + --fg: #d7afff; +} +.ap-terminal .bg-183 { + --bg: #d7afff; +} +.ap-terminal .fg-184 { + --fg: #d7d700; +} +.ap-terminal .bg-184 { + --bg: #d7d700; +} +.ap-terminal .fg-185 { + --fg: #d7d75f; +} +.ap-terminal .bg-185 { + --bg: #d7d75f; +} +.ap-terminal .fg-186 { + --fg: #d7d787; +} +.ap-terminal .bg-186 { + --bg: #d7d787; +} +.ap-terminal .fg-187 { + --fg: #d7d7af; +} +.ap-terminal .bg-187 { + --bg: #d7d7af; +} +.ap-terminal .fg-188 { + --fg: #d7d7d7; +} +.ap-terminal .bg-188 { + --bg: #d7d7d7; +} +.ap-terminal .fg-189 { + --fg: #d7d7ff; +} +.ap-terminal .bg-189 { + --bg: #d7d7ff; +} +.ap-terminal .fg-190 { + --fg: #d7ff00; +} +.ap-terminal .bg-190 { + --bg: #d7ff00; +} +.ap-terminal .fg-191 { + --fg: #d7ff5f; +} +.ap-terminal .bg-191 { + --bg: #d7ff5f; +} +.ap-terminal .fg-192 { + --fg: #d7ff87; +} +.ap-terminal .bg-192 { + --bg: #d7ff87; +} +.ap-terminal .fg-193 { + --fg: #d7ffaf; +} +.ap-terminal .bg-193 { + --bg: #d7ffaf; +} +.ap-terminal .fg-194 { + --fg: #d7ffd7; +} +.ap-terminal .bg-194 { + --bg: #d7ffd7; +} +.ap-terminal .fg-195 { + --fg: #d7ffff; +} +.ap-terminal .bg-195 { + --bg: #d7ffff; +} +.ap-terminal .fg-196 { + --fg: #ff0000; +} +.ap-terminal .bg-196 { + --bg: #ff0000; +} +.ap-terminal .fg-197 { + --fg: #ff005f; +} +.ap-terminal .bg-197 { + --bg: #ff005f; +} +.ap-terminal .fg-198 { + --fg: #ff0087; +} +.ap-terminal .bg-198 { + --bg: #ff0087; +} +.ap-terminal .fg-199 { + --fg: #ff00af; +} +.ap-terminal .bg-199 { + --bg: #ff00af; +} +.ap-terminal .fg-200 { + --fg: #ff00d7; +} +.ap-terminal .bg-200 { + --bg: #ff00d7; +} +.ap-terminal .fg-201 { + --fg: #ff00ff; +} +.ap-terminal .bg-201 { + --bg: #ff00ff; +} +.ap-terminal .fg-202 { + --fg: #ff5f00; +} +.ap-terminal .bg-202 { + --bg: #ff5f00; +} +.ap-terminal .fg-203 { + --fg: #ff5f5f; +} +.ap-terminal .bg-203 { + --bg: #ff5f5f; +} +.ap-terminal .fg-204 { + --fg: #ff5f87; +} +.ap-terminal .bg-204 { + --bg: #ff5f87; +} +.ap-terminal .fg-205 { + --fg: #ff5faf; +} +.ap-terminal .bg-205 { + --bg: #ff5faf; +} +.ap-terminal .fg-206 { + --fg: #ff5fd7; +} +.ap-terminal .bg-206 { + --bg: #ff5fd7; +} +.ap-terminal .fg-207 { + --fg: #ff5fff; +} +.ap-terminal .bg-207 { + --bg: #ff5fff; +} +.ap-terminal .fg-208 { + --fg: #ff8700; +} +.ap-terminal .bg-208 { + --bg: #ff8700; +} +.ap-terminal .fg-209 { + --fg: #ff875f; +} +.ap-terminal .bg-209 { + --bg: #ff875f; +} +.ap-terminal .fg-210 { + --fg: #ff8787; +} +.ap-terminal .bg-210 { + --bg: #ff8787; +} +.ap-terminal .fg-211 { + --fg: #ff87af; +} +.ap-terminal .bg-211 { + --bg: #ff87af; +} +.ap-terminal .fg-212 { + --fg: #ff87d7; +} +.ap-terminal .bg-212 { + --bg: #ff87d7; +} +.ap-terminal .fg-213 { + --fg: #ff87ff; +} +.ap-terminal .bg-213 { + --bg: #ff87ff; +} +.ap-terminal .fg-214 { + --fg: #ffaf00; +} +.ap-terminal .bg-214 { + --bg: #ffaf00; +} +.ap-terminal .fg-215 { + --fg: #ffaf5f; +} +.ap-terminal .bg-215 { + --bg: #ffaf5f; +} +.ap-terminal .fg-216 { + --fg: #ffaf87; +} +.ap-terminal .bg-216 { + --bg: #ffaf87; +} +.ap-terminal .fg-217 { + --fg: #ffafaf; +} +.ap-terminal .bg-217 { + --bg: #ffafaf; +} +.ap-terminal .fg-218 { + --fg: #ffafd7; +} +.ap-terminal .bg-218 { + --bg: #ffafd7; +} +.ap-terminal .fg-219 { + --fg: #ffafff; +} +.ap-terminal .bg-219 { + --bg: #ffafff; +} +.ap-terminal .fg-220 { + --fg: #ffd700; +} +.ap-terminal .bg-220 { + --bg: #ffd700; +} +.ap-terminal .fg-221 { + --fg: #ffd75f; +} +.ap-terminal .bg-221 { + --bg: #ffd75f; +} +.ap-terminal .fg-222 { + --fg: #ffd787; +} +.ap-terminal .bg-222 { + --bg: #ffd787; +} +.ap-terminal .fg-223 { + --fg: #ffd7af; +} +.ap-terminal .bg-223 { + --bg: #ffd7af; +} +.ap-terminal .fg-224 { + --fg: #ffd7d7; +} +.ap-terminal .bg-224 { + --bg: #ffd7d7; +} +.ap-terminal .fg-225 { + --fg: #ffd7ff; +} +.ap-terminal .bg-225 { + --bg: #ffd7ff; +} +.ap-terminal .fg-226 { + --fg: #ffff00; +} +.ap-terminal .bg-226 { + --bg: #ffff00; +} +.ap-terminal .fg-227 { + --fg: #ffff5f; +} +.ap-terminal .bg-227 { + --bg: #ffff5f; +} +.ap-terminal .fg-228 { + --fg: #ffff87; +} +.ap-terminal .bg-228 { + --bg: #ffff87; +} +.ap-terminal .fg-229 { + --fg: #ffffaf; +} +.ap-terminal .bg-229 { + --bg: #ffffaf; +} +.ap-terminal .fg-230 { + --fg: #ffffd7; +} +.ap-terminal .bg-230 { + --bg: #ffffd7; +} +.ap-terminal .fg-231 { + --fg: #ffffff; +} +.ap-terminal .bg-231 { + --bg: #ffffff; +} +.ap-terminal .fg-232 { + --fg: #080808; +} +.ap-terminal .bg-232 { + --bg: #080808; +} +.ap-terminal .fg-233 { + --fg: #121212; +} +.ap-terminal .bg-233 { + --bg: #121212; +} +.ap-terminal .fg-234 { + --fg: #1c1c1c; +} +.ap-terminal .bg-234 { + --bg: #1c1c1c; +} +.ap-terminal .fg-235 { + --fg: #262626; +} +.ap-terminal .bg-235 { + --bg: #262626; +} +.ap-terminal .fg-236 { + --fg: #303030; +} +.ap-terminal .bg-236 { + --bg: #303030; +} +.ap-terminal .fg-237 { + --fg: #3a3a3a; +} +.ap-terminal .bg-237 { + --bg: #3a3a3a; +} +.ap-terminal .fg-238 { + --fg: #444444; +} +.ap-terminal .bg-238 { + --bg: #444444; +} +.ap-terminal .fg-239 { + --fg: #4e4e4e; +} +.ap-terminal .bg-239 { + --bg: #4e4e4e; +} +.ap-terminal .fg-240 { + --fg: #585858; +} +.ap-terminal .bg-240 { + --bg: #585858; +} +.ap-terminal .fg-241 { + --fg: #626262; +} +.ap-terminal .bg-241 { + --bg: #626262; +} +.ap-terminal .fg-242 { + --fg: #6c6c6c; +} +.ap-terminal .bg-242 { + --bg: #6c6c6c; +} +.ap-terminal .fg-243 { + --fg: #767676; +} +.ap-terminal .bg-243 { + --bg: #767676; +} +.ap-terminal .fg-244 { + --fg: #808080; +} +.ap-terminal .bg-244 { + --bg: #808080; +} +.ap-terminal .fg-245 { + --fg: #8a8a8a; +} +.ap-terminal .bg-245 { + --bg: #8a8a8a; +} +.ap-terminal .fg-246 { + --fg: #949494; +} +.ap-terminal .bg-246 { + --bg: #949494; +} +.ap-terminal .fg-247 { + --fg: #9e9e9e; +} +.ap-terminal .bg-247 { + --bg: #9e9e9e; +} +.ap-terminal .fg-248 { + --fg: #a8a8a8; +} +.ap-terminal .bg-248 { + --bg: #a8a8a8; +} +.ap-terminal .fg-249 { + --fg: #b2b2b2; +} +.ap-terminal .bg-249 { + --bg: #b2b2b2; +} +.ap-terminal .fg-250 { + --fg: #bcbcbc; +} +.ap-terminal .bg-250 { + --bg: #bcbcbc; +} +.ap-terminal .fg-251 { + --fg: #c6c6c6; +} +.ap-terminal .bg-251 { + --bg: #c6c6c6; +} +.ap-terminal .fg-252 { + --fg: #d0d0d0; +} +.ap-terminal .bg-252 { + --bg: #d0d0d0; +} +.ap-terminal .fg-253 { + --fg: #dadada; +} +.ap-terminal .bg-253 { + --bg: #dadada; +} +.ap-terminal .fg-254 { + --fg: #e4e4e4; +} +.ap-terminal .bg-254 { + --bg: #e4e4e4; +} +.ap-terminal .fg-255 { + --fg: #eeeeee; +} +.ap-terminal .bg-255 { + --bg: #eeeeee; +} +.asciinema-player-theme-asciinema { + --term-color-foreground: #cccccc; + --term-color-background: #121314; + --term-color-0: hsl(0, 0%, 0%); + --term-color-1: hsl(343, 70%, 55%); + --term-color-2: hsl(103, 70%, 44%); + --term-color-3: hsl(43, 70%, 55%); + --term-color-4: hsl(193, 70%, 49.5%); + --term-color-5: hsl(283, 70%, 60.5%); + --term-color-6: hsl(163, 70%, 60.5%); + --term-color-7: hsl(0, 0%, 85%); + --term-color-8: hsl(0, 0%, 30%); + --term-color-9: hsl(343, 70%, 55%); + --term-color-10: hsl(103, 70%, 44%); + --term-color-11: hsl(43, 70%, 55%); + --term-color-12: hsl(193, 70%, 49.5%); + --term-color-13: hsl(283, 70%, 60.5%); + --term-color-14: hsl(163, 70%, 60.5%); + --term-color-15: hsl(0, 0%, 100%); +} +/* + Based on Dracula: https://draculatheme.com + */ +.asciinema-player-theme-dracula { + --term-color-foreground: #f8f8f2; + --term-color-background: #282a36; + --term-color-0: #21222c; + --term-color-1: #ff5555; + --term-color-2: #50fa7b; + --term-color-3: #f1fa8c; + --term-color-4: #bd93f9; + --term-color-5: #ff79c6; + --term-color-6: #8be9fd; + --term-color-7: #f8f8f2; + --term-color-8: #6272a4; + --term-color-9: #ff6e6e; + --term-color-10: #69ff94; + --term-color-11: #ffffa5; + --term-color-12: #d6acff; + --term-color-13: #ff92df; + --term-color-14: #a4ffff; + --term-color-15: #ffffff; +} +/* Based on Monokai from base16 collection - https://github.com/chriskempson/base16 */ +.asciinema-player-theme-monokai { + --term-color-foreground: #f8f8f2; + --term-color-background: #272822; + --term-color-0: #272822; + --term-color-1: #f92672; + --term-color-2: #a6e22e; + --term-color-3: #f4bf75; + --term-color-4: #66d9ef; + --term-color-5: #ae81ff; + --term-color-6: #a1efe4; + --term-color-7: #f8f8f2; + --term-color-8: #75715e; + --term-color-15: #f9f8f5; +} +/* + Based on Nord: https://github.com/arcticicestudio/nord + Via: https://github.com/neilotoole/asciinema-theme-nord + */ +.asciinema-player-theme-nord { + --term-color-foreground: #eceff4; + --term-color-background: #2e3440; + --term-color-0: #3b4252; + --term-color-1: #bf616a; + --term-color-2: #a3be8c; + --term-color-3: #ebcb8b; + --term-color-4: #81a1c1; + --term-color-5: #b48ead; + --term-color-6: #88c0d0; + --term-color-7: #eceff4; +} +.asciinema-player-theme-seti { + --term-color-foreground: #cacecd; + --term-color-background: #111213; + --term-color-0: #323232; + --term-color-1: #c22832; + --term-color-2: #8ec43d; + --term-color-3: #e0c64f; + --term-color-4: #43a5d5; + --term-color-5: #8b57b5; + --term-color-6: #8ec43d; + --term-color-7: #eeeeee; + --term-color-15: #ffffff; +} +/* + Based on Solarized Dark: https://ethanschoonover.com/solarized/ + */ +.asciinema-player-theme-solarized-dark { + --term-color-foreground: #839496; + --term-color-background: #002b36; + --term-color-0: #073642; + --term-color-1: #dc322f; + --term-color-2: #859900; + --term-color-3: #b58900; + --term-color-4: #268bd2; + --term-color-5: #d33682; + --term-color-6: #2aa198; + --term-color-7: #eee8d5; + --term-color-8: #002b36; + --term-color-9: #cb4b16; + --term-color-10: #586e75; + --term-color-11: #657b83; + --term-color-12: #839496; + --term-color-13: #6c71c4; + --term-color-14: #93a1a1; + --term-color-15: #fdf6e3; +} +/* + Based on Solarized Light: https://ethanschoonover.com/solarized/ + */ +.asciinema-player-theme-solarized-light { + --term-color-foreground: #657b83; + --term-color-background: #fdf6e3; + --term-color-0: #073642; + --term-color-1: #dc322f; + --term-color-2: #859900; + --term-color-3: #b58900; + --term-color-4: #268bd2; + --term-color-5: #d33682; + --term-color-6: #2aa198; + --term-color-7: #eee8d5; + --term-color-8: #002b36; + --term-color-9: #cb4b16; + --term-color-10: #586e75; + --term-color-11: #657c83; + --term-color-12: #839496; + --term-color-13: #6c71c4; + --term-color-14: #93a1a1; + --term-color-15: #fdf6e3; +} +.asciinema-player-theme-solarized-light .ap-overlay-start .ap-play-button svg .ap-play-btn-fill { + fill: var(--term-color-1); +} +.asciinema-player-theme-solarized-light .ap-overlay-start .ap-play-button svg .ap-play-btn-stroke { + stroke: var(--term-color-1); +} +/* + Based on Tango: https://en.wikipedia.org/wiki/Tango_Desktop_Project + */ +.asciinema-player-theme-tango { + --term-color-foreground: #cccccc; + --term-color-background: #121314; + --term-color-0: #000000; + --term-color-1: #cc0000; + --term-color-2: #4e9a06; + --term-color-3: #c4a000; + --term-color-4: #3465a4; + --term-color-5: #75507b; + --term-color-6: #06989a; + --term-color-7: #d3d7cf; + --term-color-8: #555753; + --term-color-9: #ef2929; + --term-color-10: #8ae234; + --term-color-11: #fce94f; + --term-color-12: #729fcf; + --term-color-13: #ad7fa8; + --term-color-14: #34e2e2; + --term-color-15: #eeeeec; +} diff --git a/aider/website/assets/asciinema/asciinema-player.min.js b/aider/website/assets/asciinema/asciinema-player.min.js new file mode 100644 index 000000000..d90e64c82 --- /dev/null +++ b/aider/website/assets/asciinema/asciinema-player.min.js @@ -0,0 +1 @@ +var AsciinemaPlayer=function(A){"use strict";function g(A){return"number"==typeof A?A:"string"==typeof A?A.split(":").reverse().map(parseFloat).reduce(((A,g,I)=>A+g*Math.pow(60,I))):void 0}class I{log(){}debug(){}info(){}warn(){}error(){}}class B{constructor(A,g){this.logger=A,this.prefix=g}log(A){for(var g=arguments.length,I=new Array(g>1?g-1:0),B=1;B1?g-1:0),B=1;B1?g-1:0),B=1;B1?g-1:0),B=1;B1?g-1:0),B=1;B{throw Error("TextDecoder not available")}};"undefined"!=typeof TextDecoder&&i.decode();let t=null;function o(){return null!==t&&0!==t.byteLength||(t=new Uint8Array(Q.memory.buffer)),t}function s(A,g){return A>>>=0,i.decode(o().subarray(A,A+g))}function n(A){V===C.length&&C.push(C.length+1);const g=V;return V=C[g],C[g]=A,g}function r(A){const g=typeof A;if("number"==g||"boolean"==g||null==A)return`${A}`;if("string"==g)return`"${A}"`;if("symbol"==g){const g=A.description;return null==g?"Symbol":`Symbol(${g})`}if("function"==g){const g=A.name;return"string"==typeof g&&g.length>0?`Function(${g})`:"Function"}if(Array.isArray(A)){const g=A.length;let I="[";g>0&&(I+=r(A[0]));for(let B=1;B1))return toString.call(A);if(B=I[1],"Object"==B)try{return"Object("+JSON.stringify(A)+")"}catch(A){return"Object"}return A instanceof Error?`${A.name}: ${A.message}\n${A.stack}`:B}let a=0;const c="undefined"!=typeof TextEncoder?new TextEncoder("utf-8"):{encode:()=>{throw Error("TextEncoder not available")}},D="function"==typeof c.encodeInto?function(A,g){return c.encodeInto(A,g)}:function(A,g){const I=c.encode(A);return g.set(I),{read:A.length,written:I.length}};function w(A,g,I){if(void 0===I){const I=c.encode(A),B=g(I.length,1)>>>0;return o().subarray(B,B+I.length).set(I),a=I.length,B}let B=A.length,Q=g(B,1)>>>0;const C=o();let E=0;for(;E127)break;C[Q+E]=g}if(E!==B){0!==E&&(A=A.slice(E)),Q=I(Q,B,B=E+3*A.length,1)>>>0;const g=o().subarray(Q+E,Q+B);E+=D(A,g).written,Q=I(Q,B,E,1)>>>0}return a=E,Q}let h=null;function l(){return null!==h&&0!==h.byteLength||(h=new Int32Array(Q.memory.buffer)),h}let y=null;function k(A,g){return A>>>=0,(null!==y&&0!==y.byteLength||(y=new Uint32Array(Q.memory.buffer)),y).subarray(A/4,A/4+g)}const G="undefined"==typeof FinalizationRegistry?{register:()=>{},unregister:()=>{}}:new FinalizationRegistry((A=>Q.__wbg_vt_free(A>>>0)));class F{static __wrap(A){A>>>=0;const g=Object.create(F.prototype);return g.__wbg_ptr=A,G.register(g,g.__wbg_ptr,g),g}__destroy_into_raw(){const A=this.__wbg_ptr;return this.__wbg_ptr=0,G.unregister(this),A}free(){const A=this.__destroy_into_raw();Q.__wbg_vt_free(A)}feed(A){const g=w(A,Q.__wbindgen_malloc,Q.__wbindgen_realloc),I=a;return e(Q.vt_feed(this.__wbg_ptr,g,I))}resize(A,g){return e(Q.vt_resize(this.__wbg_ptr,A,g))}inspect(){let A,g;try{const C=Q.__wbindgen_add_to_stack_pointer(-16);Q.vt_inspect(C,this.__wbg_ptr);var I=l()[C/4+0],B=l()[C/4+1];return A=I,g=B,s(I,B)}finally{Q.__wbindgen_add_to_stack_pointer(16),Q.__wbindgen_free(A,g,1)}}getSize(){try{const B=Q.__wbindgen_add_to_stack_pointer(-16);Q.vt_getSize(B,this.__wbg_ptr);var A=l()[B/4+0],g=l()[B/4+1],I=k(A,g).slice();return Q.__wbindgen_free(A,4*g,4),I}finally{Q.__wbindgen_add_to_stack_pointer(16)}}getLine(A){return e(Q.vt_getLine(this.__wbg_ptr,A))}getCursor(){return e(Q.vt_getCursor(this.__wbg_ptr))}}function q(){const A={wbg:{}};return A.wbg.__wbindgen_object_drop_ref=function(A){e(A)},A.wbg.__wbindgen_error_new=function(A,g){return n(new Error(s(A,g)))},A.wbg.__wbindgen_object_clone_ref=function(A){return n(E(A))},A.wbg.__wbindgen_number_new=function(A){return n(A)},A.wbg.__wbindgen_bigint_from_u64=function(A){return n(BigInt.asUintN(64,A))},A.wbg.__wbindgen_string_new=function(A,g){return n(s(A,g))},A.wbg.__wbg_set_f975102236d3c502=function(A,g,I){E(A)[e(g)]=e(I)},A.wbg.__wbg_new_b525de17f44a8943=function(){return n(new Array)},A.wbg.__wbg_new_f841cc6f2098f4b5=function(){return n(new Map)},A.wbg.__wbg_new_f9876326328f45ed=function(){return n(new Object)},A.wbg.__wbindgen_is_string=function(A){return"string"==typeof E(A)},A.wbg.__wbg_set_17224bc548dd1d7b=function(A,g,I){E(A)[g>>>0]=e(I)},A.wbg.__wbg_set_388c4c6422704173=function(A,g,I){return n(E(A).set(E(g),E(I)))},A.wbg.__wbindgen_debug_string=function(A,g){const I=w(r(E(g)),Q.__wbindgen_malloc,Q.__wbindgen_realloc),B=a;l()[A/4+1]=B,l()[A/4+0]=I},A.wbg.__wbindgen_throw=function(A,g){throw new Error(s(A,g))},A}function d(A,g){return Q=A.exports,N.__wbindgen_wasm_module=g,h=null,y=null,t=null,Q}async function N(A){if(void 0!==Q)return Q;const g=q();("string"==typeof A||"function"==typeof Request&&A instanceof Request||"function"==typeof URL&&A instanceof URL)&&(A=fetch(A));const{instance:I,module:B}=await async function(A,g){if("function"==typeof Response&&A instanceof Response){if("function"==typeof WebAssembly.instantiateStreaming)try{return await WebAssembly.instantiateStreaming(A,g)}catch(g){if("application/wasm"==A.headers.get("Content-Type"))throw g;console.warn("`WebAssembly.instantiateStreaming` failed because your server does not serve wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n",g)}const I=await A.arrayBuffer();return await WebAssembly.instantiate(I,g)}{const I=await WebAssembly.instantiate(A,g);return I instanceof WebAssembly.Instance?{instance:I,module:A}:I}}(await A,g);return d(I,B)}var M=Object.freeze({__proto__:null,Vt:F,create:function(A,g,I){const B=Q.create(A,g,I);return F.__wrap(B)},default:N,initSync:function(A){if(void 0!==Q)return Q;const g=q();return A instanceof WebAssembly.Module||(A=new WebAssembly.Module(A)),d(new WebAssembly.Instance(A,g),A)}});const u=[62,0,0,0,63,52,53,54,55,56,57,58,59,60,61,0,0,0,0,0,0,0,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,0,0,0,0,0,0,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51];function f(A){return u[A-43]}const R=function(A){let g,I=A.endsWith("==")?2:A.endsWith("=")?1:0,B=A.length,Q=new Uint8Array(B/4*3);for(let I=0,C=0;I>16,Q[C+1]=g>>8&255,Q[C+2]=255&g;return Q.subarray(0,Q.length-I)}("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QQhrIgAgA08NAQwDCyAHQQhrIQALA0AgBkEEaigCACIJQYqUqNAAc0GBgoQIayAJQX9zcSAGKAIAIglBipSo0ABzQYGChAhrIAlBf3NxckGAgYKEeHENAiAGQQhqIQYgACADQQhqIgNPDQALDAELIAIgBEYEQCACIQQMBAtBACEAA0AgACAFai0AAEEKRg0CIAcgAEEBaiIARw0ACyACIQQMAwsgAyAHRgRAIAIhBAwDCwNAIAMgBWotAABBCkYEQCADIQAMAgsgByADQQFqIgNHDQALIAIhBAwCCyAAIARqIgZBAWohBAJAIAIgBk0NACAAIAVqLQAAQQpHDQBBACEFIAQiBiEADAMLIAIgBE8NAAsLQQEhBSACIgAgCCIGRw0AQQAPCwJAIAwtAABFDQAgC0H49MAAQQQgCigCDBEBAEUNAEEBDwsgACAIayEHQQAhAyAAIAhHBEAgACANai0AAEEKRiEDCyABIAhqIQAgDCADOgAAIAYhCCALIAAgByAKKAIMEQEAIgAgBXJFDQALIAAL0gYBBX8jAEHAAWsiAiQAIAAoAgAhAyACQbgBakGojMAANgIAIAJBBGoiAEGsAWpBxJDAADYCACAAQaQBakG0kMAANgIAIABBnAFqQbSQwAA2AgAgAkGYAWpBmI7AADYCACACQZABakGYjsAANgIAIAJBiAFqQaSPwAA2AgAgAkGAAWpBpJDAADYCACAAQfQAakGkj8AANgIAIAJB8ABqQaSPwAA2AgAgAkHoAGpBpI/AADYCACAAQdwAakGkj8AANgIAIAJB2ABqQZSQwAA2AgAgAkHQAGpBmI7AADYCACACQcgAakGEkMAANgIAIAJBQGtBiI/AADYCACACQThqQfSPwAA2AgAgAkEwakHkj8AANgIAIABBJGpB1I/AADYCACACQSBqQcSPwAA2AgAgAkEYakHEj8AANgIAIAJBEGpBmI7AADYCACACIANB3ABqNgKsASACIANBiAFqNgKkASACIANB9ABqNgKcASACIANBrAFqNgKUASACIANBqAFqNgKMASACIANBwgFqNgKEASACIANBwQFqNgJ8IAIgA0HAAWo2AnQgAiADQb8BajYCbCACIANBvgFqNgJkIAIgA0G9AWo2AlwgAiADQdAAajYCVCACIANBpAFqNgJMIAIgA0GwAWo2AkQgAiADQbIB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amohAiADQSBqIQMgCCAHQQRqIgdHDQALCyAGBEAgB0EDdCAFakEEaiEDA0AgAygCACACaiECIANBCGohAyAGQQFrIgYNAAsLIAEoAgwEQCACQQBIDQEgBSgCBEUgAkEQSXENASACQQF0IQILIAINAQtBASEDQQAhAgwBCyACQQBIDQFBqYzBAC0AABogAkEBENcBIgNFDQILIARBADYCCCAEIAM2AgQgBCACNgIAIARBhO/AACABEBhFDQJB5O/AAEEzIARBD2pBmPDAAEHA8MAAEF0ACxCpAQALQQEgAkHkjMEAKAIAIgBB5AAgABsRAgAACyAAIAQpAgA3AgAgAEEIaiAEQQhqKAIANgIAIARBEGokAAvVAgEHf0EBIQkCQAJAIAJFDQAgASACQQF0aiEKIABBgP4DcUEIdiELIABB/wFxIQ0DQCABQQJqIQwgByABLQABIgJqIQggCyABLQAAIgFHBEAgASALSw0CIAghByAKIAwiAUYNAgwBCwJAAkAgByAITQRAIAQgCEkNASADIAdqIQEDQCACRQ0DIAJBAWshAiABLQAAIAFBAWohASANRw0AC0EAIQkMBQsgByAIQbj5wAAQ7AEACyAIIARBuPnAABDqAQALIAghByAKIAwiAUcNAAsLIAZFDQAgBSAGaiEDIABB//8DcSEBA0AgBUEBaiEAAkAgBS0AACICwCIEQQBOBEAgACEFDAELIAAgA0cEQCAFLQABIARB/wBxQQh0ciECIAVBAmohBQwBC0Go+cAAEO4BAAsgASACayIBQQBIDQEgCUEBcyEJIAMgBUcNAAsLIAlBAXEL8wIBBH8gACgCDCECAkACQCABQYACTwRAIAAoAhghAwJAAkAgACACRgRAIABBFEEQIAAoAhQiAhtqKAIAIgENAUEAIQIMAgsgACgCCCIBIAI2AgwgAiABNgIIDAELIABBFGogAEEQaiACGyEEA0AgBCEFIAEiAigCFCEBIAJBFGogAkEQaiABGyEEIAJBFEEQIAEbaigCACIBDQALIAVBADYCAAsgA0UNAiAAIAAoAhxBAnRB+IzBAGoiASgCAEcEQCADQRBBFCADKAIQIABGG2ogAjYCACACRQ0DDAILIAEgAjYCACACDQFBlJDBAEGUkMEAKAIAQX4gACgCHHdxNgIADAILIAIgACgCCCIARwRAIAAgAjYC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IhEoAgAhDyARIAwoAgA2AgAgDCAPNgIAIAVBBGoiBUEkRw0ACyALQQxqIAMoApwBIAMoAqABIgVBAUEAIANBsgFqECsgA0EMahCKASADKAIMIgwEQCADKAIQIAxBBHRBBBDkAQsgAyALQQxqQSQQiAJB3ABqQQAgBRB4CyALQTBqJAAgAxBCCyACQQJqIQIgB0ECayIHDQALCyANBEAgBCANQQF0QQIQ5AELDCALAkAgAi8BAiIEQQEgBEEBSxtBAWsiBCACLwEEIgIgAygCoAEiBSACG0EBayICSSACIAVJcUUEQCADKAKoASEEDAELIAMgAjYCrAEgAyAENgKoAQsgA0EAOgDCASADQQA2AmggAyAEQQAgAy0AvgEbNgJsDB8LIANBAToAcCADQQA7AL0BIANBADsBugEgA0ECOgC2ASADQQI6ALIBIANBADsBsAEgA0IANwKkASADQYCAgAg2AoQBIANBAjoAgAEgA0ECOgB8IANCADcCdCADIAMoAqABQQFrNgKsAQweCyADKAKgASADKAKsASIEQQFqIAQgAygCbCIESRshBSADIAQgBSACLwECIgJBASACQQFLGyADQbIBahAiIANB3ABqIAQgBRB4DB0LIAMgAygCaCADKAJsIgRBACACLwECIgJBASACQQFLGyADQbIBahAoIAQgAygCZCICTw0dIAMoAmAgBGpBAToAAAwcCwJAAkACQAJAIAItAAFBAWsOAwECAwALIAMgAygCaCADKAJsQQEgAyADQbIBahAoIANB3ABqIAMoAmwgAygCoAEQeAwCCyADIAMoAmggAygCbEECIAMgA0GyAWoQKCADQdwAakEAIAMoAmxBAWoQeAwBCyADQQAgAygCHCADQbIBahBLIANB3ABqQQAgAygCoAEQeAsMGwsgAyADKAJoIAMoAmwiBCACLQABQQRqIAMgA0GyAWoQKCAEIAMoAmQiAk8NGyADKAJgIARqQQE6AAAMGgsgAyACLQABOgCxAQwZCyADIAItAAE6ALABDBgLIAMoAlhBAnQhAiADKAJUIQUgAygCaCEHAkACQANAIAJFDQEgAkEEayECIAUoAgAhBCAFQQRqIQUgBCAHTQ0ACyADKAKcASICQQFrIQUMAQsgAygCnAEiAkEBayIFIQQLIANBADoAwgEgAyAEIAUgAiAESxs2AmgMFwsg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+gA2AiggACAAQSRqNgIUIAAgAEEvajYCJCAAQQxqQeCXwAAQpAEACyATQRBqJAAgAAvGAQECfyMAQRBrIgAkACABKAIUQbDswABBCyABKAIYKAIMEQEAIQMgAEEIaiICQQA6AAUgAiADOgAEIAIgATYCACACIgEtAAQhAwJAIAItAAVFBEAgA0EARyEBDAELQQEhAiADRQRAIAEoAgAiAi0AHEEEcUUEQCABIAIoAhRBh/XAAEECIAIoAhgoAgwRAQAiAToABAwCCyACKAIUQYb1wABBASACKAIYKAIMEQEAIQILIAEgAjoABCACIQELIABBEGokACABCzIBAX8gAEEQahAwAkAgACgCACIBQYCAgIB4Rg0AIAFFDQAgACgCBCABQQR0QQQQ5AELCy8BAn8gACAAKAKoASICIAAoAqwBQQFqIgMgASAAQbIBahBZIABB3ABqIAIgAxB4Cy8BAn8gACAAKAKoASICIAAoAqwBQQFqIgMgASAAQbIBahAiIABB3ABqIAIgAxB4CysAIAEgAkkEQEHcosAAQSNBzKPAABCcAQALIAIgACACQQR0aiABIAJrEBILJQAgAEEBNgIEIAAgASgCBCABKAIAa0EEdiIBNgIIIAAgATYCAAslACAARQRAQfCXwABBMhD7AQALIAAgAiADIAQgBSABKAIQEQgACzAAIAEoAhQgAC0AAEECdCIAQYyFwABqKAIAIABB1ITAAGooAgAgASgCGCgCDBEBAAswACABKAIUIAAtAABBAnQiAEGEi8AAaigCACAAQfiKwABqKAIAIAEoAhgoAgwRAQALMAAgASgCFCAALQAAQQJ0IgBB2JTAAGooAgAgAEHMlMAAaigCACABKAIYKAIMEQEACyMAIABFBEBB8JfAAEEyEPsBAAsgACACIAMgBCABKAIQEQUACyMAIABFBEBB8JfAAEEyEPsBAAsgACACIAMgBCABKAIQERgACyMAIABFBEBB8JfAAEEyEPsBAAsgACACIAMgBCABKAIQERoACyMAIABFBEBB8JfAAEEyEPsBAAsgACACIAMgBCABKAIQERwACyMAIABFBEBB8JfAAEEyEPsBAAsgACACIAMgBCABKAIQEQwACygBAX8gACgCACIBQYCAgIB4ckGAgICAeEcEQCAAKAIEIAFBARDkAQsLLgAgASgCFEH8icAAQfeJwAAgACgCAC0AACIAG0EHQQUgABsgASgCGCgCDBEBAAshACAARQRAQfCXwABBMhD7AQALIAAgAiADIAEoAhARAwALHQEBfyAAKAIAIgEEQCAAKAIEIAFBBHRBBBDkAQsLHQEBfyAAKAIAIgEEQCAAKAIEIAFBAnRBBBDkAQsLIgAgAC0AAEUEQCABQaj3wABBBRATDwsgAUGt98AAQQQQEwsrACABKAIUQd+TwABB2JPAACAALQAAIgAbQQlBByAAGyABKAIYKAIMEQEACysAIAEoAhRB6JPAAEHXjsAAIAAtAAAiABtBC0EGIAAbIAEoAhgoAgwRAQALHwAgAEUEQEHwl8AAQTIQ+wEACyAAIAIgASgCEBEAAAsbABAHIQIgAEEANgIIIAAgAjYCBCAAIAE2AgALwQMCAn4Gf0GsjMEAKAIARQRAIwBBMGsiAyQAAn8CQCAABEAgACgCACAAQQA2AgANAQsgA0EQakGwlsAAKQMANwMAIANBqJbAACkDADcDCEEADAELIANBEGogAEEQaikCADcDACADIAApAgg3AwggACgCBAshAEGsjMEAKQIAIQFBsIzBACAANgIAQayMwQBBATYCACADQRhqIgBBEGpBvIzBACkCADcDACAAQQhqIgBBtIzBACkCADcDAEG0jMEAIAMpAwg3AgBBvIzBACADQRBqKQMANwIAIAMgATcDGCABpwRAAkAgACgCBCIGRQ0AIAAoAgwiBwRAIAAoAgAiBEEIaiEFIAQpAwBCf4VCgIGChIiQoMCAf4MhAQNAIAFQBEADQCAEQeAAayEEIAUpAwAgBUEIaiEFQn+FQoCBgoSIkKDAgH+DIgFQDQALCyABQgF9IQIgBCABeqdBA3ZBdGxqQQRrKAIAIghBhAFPBEAgCBAACyABIAKDIQEgB0EBayIHDQALCyAGQQFqrUIMfqdBB2pBeHEiBCAGakEJaiIFRQ0AIAAoAgAgBGsgBUEIEOQBCwsgA0EwaiQAC0GwjMEACxoBAX8gACgCACIBBEAgACgCBCABQQEQ5AELCxQAIAAoAgAiAEGEAU8EQCAAEAALC7YBAQR/IAAoAgAiACgCBCECIAAoAgghAyMAQRBrIgAkACABKAIUQazywABBASABKAIYKAIMEQEAIQUgAEEEaiIEQQA6AAUgBCAFOgAEIAQgATYCACADBEADQCAAIAI2AgwgAEEEaiAAQQxqQaiMwAAQLCACQQFqIQIgA0EBayIDDQALCyAAQQRqIgEtAAQEf0EBBSABKAIAIgEoAhRBjvXAAEEBIAEoAhgoAgwRAQALIABBEGokAAu9AQEEfyAAKAIAIgAoAgQhAiAAKAIIIQMjAEEQayIAJAAgASgCFEGs8sAAQQEgASgCGCgCDBEBACEFIABBBGoiBEEAOgAFIAQgBToABCAEIAE2AgAgAwRAIANBAnQhAQNAIAAgAjYCDCAAQQRqIABBDGpB+IzAABAsIAJBBGohAiABQQRrIgENAAsLIABBBGoiAS0ABAR/QQEFIAEoAgAiASgCFEGO9cAAQQEgASgCGCgCDBEBAAsgAEEQaiQAC+UGAQV/AkACQAJAAkACQCAAQQRrIgUoAgAiB0F4cSIEQQRBCCAHQQNxIgYbIAFqTwRAIAZBAEcgAUEnaiIIIARJcQ0BAkACQCACQQlPBEAgAiADEB0iAg0BQQAhAAwIC0EAIQIgA0HM/3tLDQFBECADQQtqQXhxIANBC0kbIQECQCAGRQRAIAFBgAJJDQEgBCABQQRySQ0BIAQgAWtBgYAITw0BDAkLIABBCGsiBiAEaiEIAkACQAJAAkAgASAESwRAIAhBpJDBACgCAEYNBCAIQaCQwQAoAgBGDQIgCCgCBCIHQQJxDQUgB0F4cSIHIARqIgQgAUkNBSAIIAcQICAEIAFrIgJBEEkNASAFIAEgBSgCAEEBcXJBAnI2AgAgASAGaiIBIAJBA3I2AgQgBCAGaiIDIAMoAgRBAXI2AgQgASACEBsMDQsgBCABayICQQ9LDQIMDAsgBSAEIAUoAgBBAXFyQQJyNgIAIAQgBmoiASABKAIEQQFyNgIEDAsLQZiQwQAoAgAgBGoiBCABSQ0CAkAgBCABayICQQ9NBEAgBSAHQQFxIARyQQJyNgIAIAQgBmoiASABKAIEQQFyNgIEQQAhAkEAIQEMAQsgBSABIAdBAXFyQQJyNgIAIAEgBmoiASACQQFyNgIEIAQgBmoiAyACNgIAIAMgAygCBEF+cTYCBAtBoJDBACABNgIAQZiQwQAgAjYCAAwKCyAFIAEgB0EBcXJBAnI2AgAgASAGaiIBIAJBA3I2AgQgCCAIKAIEQQFyNgIEIAEgAhAbDAkLQZyQwQAoAgAgBGoiBCABSw0HCyADEA8iAUUNASABIAAgBSgCACIBQXhxQXxBeCABQQNxG2oiASADIAEgA0kbEIgCIAAQFiEADAcLIAIgACABIAMgASADSRsQiAIaIAUoAgAiBUF4cSEDIAMgAUEEQQggBUEDcSIFG2pJDQMgBUEARyADIAhLcQ0EIAAQFgsgAiEADAULQbHrwABBLkHg68AAEJwBAAtB8OvAAEEuQaDswAAQnAEAC0Gx68AAQS5B4OvAABCcAQALQfDrwABBLkGg7MAAEJwBAAsgBSABIAdBAXFyQQJyNgIAIAEgBmoiAiAEIAFrIgFBAXI2AgRBnJDBACABNgIAQaSQwQAgAjYCAAsgAAsUACAAIAIgAxAFNgIEIABBADYCAAsQACABBEAgACABIAIQ5AELCxkAIAEoAhRBhPLAAEEOIAEoAhgoAgwRAQALEQAgAEEMaiIAEIoBIAAQwQELEwAgACgCACABKAIAIAIoAgAQDAsQACAAIAEgASACahCOAUEACxQAIAAoAgAgASAAKAIEKAIMEQAAC7gBAQR/IAAoAgQhAiAAKAIIIQMjAEEQayIAJAAgASgCFEGs8sAAQQEgASgCGCgCDBEBACEFIABBBGoiBEEAOgAFIAQgBToABCAEIAE2AgAgAwRAIANBBHQhAQNAIAAgAjYCDCAAQQRqIABBDGpB2IzAABAsIAJBEGohAiABQRBrIgENAAsLIABBBGoiAS0ABAR/QQEFIAEoAgAiASgCFEGO9cAAQQEgASgCGCgCDBEBAAsgAEEQaiQAC7gBAQR/IAAoAgQhAiAAKAIIIQMjAEEQayIAJAAgASgCFEGs8sAAQQEgASgCGCgCDBEBACEFIABBBGoiBEEAOgAFIAQgBToABCAEIAE2AgAgAwRAIANBBHQhAQNAIAAgAjYCDCAAQQRqIABBDGpBmIzAABAsIAJBEGohAiABQRBrIgENAAsLIABBBGoiAS0ABAR/QQEFIAEoAgAiASgCFEGO9cAAQQEgASgCGCgCDBEBAAsgAEEQaiQACxkAAn8gAUEJTwRAIAEgABAdDAELIAAQDwsLFAAgAEEANgIIIABCgICAgBA3AgALEQAgACgCBCAAKAIIIAEQhAILqgIBB38jAEEQayIFJAACQAJAAkAgASgCCCIDIAEoAgBPDQAgBUEIaiEGIwBBIGsiAiQAAkAgASgCACIEIANPBEACf0GBgICAeCAERQ0AGiABKAIEIQcCQCADRQRAQQEhCCAHIARBARDkAQwBC0EBIAcgBEEBIAMQzQEiCEUNARoLIAEgAzYCACABIAg2AgRBgYCAgHgLIQQgBiADNgIEIAYgBDYCACACQSBqJAAMAQsgAkEBNgIMIAJB9OnAADYCCCACQgA3AhQgAkHQ6cAANgIQIAJBCGpByOrAABCkAQALIAUoAggiAkGBgICAeEYNACACRQ0BIAIgBSgCDEHkjMEAKAIAIgBB5AAgABsRAgAACyAFQRBqJAAMAQsQqQEACyAAIAEpAgQ3AwALDgAgACABIAEgAmoQjgELIAAgAEKN04Cn1Nuixjw3AwggAELVnsTj3IPBiXs3AwALIgAgAELiq87AwdHBlKl/NwMIIABCivSnla2v+57uADcDAAsgACAAQsH3+ejMk7LRQTcDCCAAQuTex4WQ0IXefTcDAAsTACAAQdTtwAA2AgQgACABNgIACxAAIAEgACgCACAAKAIEEBMLEAAgASgCFCABKAIYIAAQGAupAQEDfyAAKAIAIQIjAEEQayIAJAAgASgCFEGs8sAAQQEgASgCGCgCDBEBACEEIABBBGoiA0EAOgAFIAMgBDoABCADIAE2AgBBDCEBA0AgACACNgIMIABBBGogAEEMakHojMAAECwgAkECaiECIAFBAmsiAQ0ACyAAQQRqIgEtAAQEf0EBBSABKAIAIgEoAhRBjvXAAEEBIAEoAhgoAgwRAQALIABBEGokAAsNACAAIAEgAhDbAUEAC2QBAX8CQCAAQQRrKAIAIgNBeHEhAgJAIAJBBEEIIANBA3EiAxsgAWpPBEAgA0EARyACIAFBJ2pLcQ0BIAAQFgwCC0Gx68AAQS5B4OvAABCcAQALQfDrwABBLkGg7MAAEJwBAAsLDQAgACgCACABIAIQBgsNACAAKAIAIAEgAhALCwwAIAAoAgAQCkEBRgsOACAAKAIAGgNADAALAAtsAQF/IwBBMGsiAyQAIAMgATYCBCADIAA2AgAgA0EsakHjADYCACADQQI2AgwgA0Ho98AANgIIIANCAjcCFCADQeMANgIkIAMgA0EgajYCECADIANBBGo2AiggAyADNgIgIANBCGogAhCkAQALbAEBfyMAQTBrIgMkACADIAE2AgQgAyAANgIAIANBLGpB4wA2AgAgA0ECNgIMIANBiPjAADYCCCADQgI3AhQgA0HjADYCJCADIANBIGo2AhAgAyADQQRqNgIoIAMgAzYCICADQQhqIAIQpAEACwsAIAA1AgAgARAkC2wBAX8jAEEwayIDJAAgAyABNgIEIAMgADYCACADQSxqQeMANgIAIANBAjYCDCADQbz4wAA2AgggA0ICNwIUIANB4wA2AiQgAyADQSBqNgIQIAMgA0EEajYCKCADIAM2AiAgA0EIaiACEKQBAAsLACAAMQAAIAEQJAsPAEGt8sAAQSsgABCcAQALCwAgACkDACABECQLCwAgACMAaiQAIwALDAAgACgCACABEMMBCwsAIAAoAgAgARAnCwcAIAAQyQELBwAgABDBAQsZACABKAIUQcyHwABBBSABKAIYKAIMEQEAC5cBAQF/IAAoAgAhAiMAQUBqIgAkACAAQgA3AzggAEE4aiACKAIAEA0gACAAKAI8IgI2AjQgACAAKAI4NgIwIAAgAjYCLCAAQd8ANgIoIABBAjYCECAAQcznwAA2AgwgAEIBNwIYIAAgAEEsaiICNgIkIAAgAEEkajYCFCABKAIUIAEoAhggAEEMahAYIAIQyQEgAEFAayQAC6IBAQR/QQIhAyMAQRBrIgIkACABKAIUQazywABBASABKAIYKAIMEQEAIQUgAkEEaiIEQQA6AAUgBCAFOgAEIAQgATYCAANAIAIgADYCDCACQQRqIAJBDGpByIzAABAsIABBAWohACADQQFrIgMNAAsgAkEEaiIALQAEBH9BAQUgACgCACIAKAIUQY71wABBASAAKAIYKAIMEQEACyACQRBqJAALowEBA38jAEEQayICJAAgASgCFEGs8sAAQQEgASgCGCgCDBEBACEEIAJBBGoiA0EAOgAFIAMgBDoABCADIAE2AgBBgAQhAQNAIAIgADYCDCACQQRqIAJBDGpBuIzAABAsIABBEGohACABQRBrIgENAAsgAkEEaiIALQAEBH9BAQUgACgCACIAKAIUQY71wABBASAAKAIYKAIMEQEACyACQRBqJAALBwAgABDCAQsMACAAEIoBIAAQwQELCQAgACABEA4ACw0AQeTowABBGxD7AQALDgBB/+jAAEHPABD7AQALDQAgAEHY6sAAIAEQGAsNACAAQfDqwAAgARAYCw0AIABBhO/AACABEBgLGQAgASgCFEH87sAAQQUgASgCGCgCDBEBAAuGBAEFfyMAQRBrIgMkAAJAAn8CQCABQYABTwRAIANBADYCDCABQYAQSQ0BIAFBgIAESQRAIAMgAUE/cUGAAXI6AA4gAyABQQx2QeABcjoADCADIAFBBnZBP3FBgAFyOgANQQMMAwsgAyABQT9xQYABcjoADyADIAFBBnZBP3FBgAFyOgAOIAMgAUEMdkE/cUGAAXI6AA0gAyABQRJ2QQdxQfABcjoADEEEDAILIAAoAggiAiAAKAIARgRAIwBBIGsiBCQAAkACQCACQQFqIgJFDQAgACgCACIFQQF0IgYgAiACIAZJGyICQQggAkEISxsiAkF/c0EfdiEGIAQgBQR/IAQgBTYCHCAEIAAoAgQ2AhRBAQVBAAs2AhggBEEIaiAGIAIgBEEUahBEIAQoAggEQCAEKAIMIgBFDQEgACAEKAIQQeSMwQAoAgAiAEHkACAAGxECAAALIAQoAgwhBSAAIAI2AgAgACAFNgIEIARBIGokAAwBCxCpAQALIAAoAgghAgsgACACQQFqNgIIIAAoAgQgAmogAToAAAwCCyADIAFBP3FBgAFyOgANIAMgAUEGdkHAAXI6AAxBAgshASABIAAoAgAgACgCCCICa0sEQCAAIAIgARA+IAAoAgghAgsgACgCBCACaiADQQxqIAEQiAIaIAAgASACajYCCAsgA0EQaiQAQQALDQAgAEHg9MAAIAEQGAsKACACIAAgARATC8ECAQN/IAAoAgAhACMAQYABayIEJAACfwJAAkAgASgCHCICQRBxRQRAIAJBIHENASAANQIAIAEQJAwDCyAAKAIAIQJBACEAA0AgACAEakH/AGogAkEPcSIDQTByIANB1wBqIANBCkkbOgAAIABBAWshACACQRBJIAJBBHYhAkUNAAsMAQsgACgCACECQQAhAANAIAAgBGpB/wBqIAJBD3EiA0EwciADQTdqIANBCkkbOgAAIABBAWshACACQRBJIAJBBHYhAkUNAAsgAEGAAWoiAkGBAU8EQCACQYABQaz1wAAQ6QEACyABQbz1wABBAiAAIARqQYABakEAIABrEBUMAQsgAEGAAWoiAkGBAU8EQCACQYABQaz1wAAQ6QEACyABQbz1wABBAiAAIARqQYABakEAIABrEBULIARBgAFqJAALkQUBB38CQAJ/AkAgAiIEIAAgAWtL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AIYIEAAIAAAAjggQAA4AAACcCBAABAAAAKAIEAALAAAAmAcQAAsAAAC0BxAADgAAAKsIEAANAAAAuAgQABAAAADICBAAEAAAANgIEAAKAAAA4ggQAA0AAADvCBAACQAAAPgIEAATAAAACwkQAAsAAAAWCRAACAAAAFByaW1hcnlBbHRlcm5hdGVBcHBsaWNhdGlvbkN1cnNvcmNvbHJvd3Zpc2libGVOb25lU29tZQAAJwAAAAQAAAAEAAAAJgAAACcAAAAEAAAABAAAAEMAAABEaXJ0eUxpbmVzAAAnAAAABAAAAAQAAABEAAAABgAAAAQAAAAFAAAAVwcQAF0HEABhBxAAY2Fubm90IGFjY2VzcyBhIFRocmVhZCBMb2NhbCBTdG9yYWdlIHZhbHVlIGR1cmluZyBvciBhZnRlciBkZXN0cnVjdGlvbgAARgAAAAAAAAABAAAARwAAAC9ydXN0Yy85YjAwOTU2ZTU2MDA5YmFiMmFhMTVkN2JmZjEwOTE2NTk5ZTNkNmQ2L2xpYnJhcnkvc3RkL3NyYy90aHJlYWQvbG9jYWwucnMAvAoQAE8AAAAEAQAAGgAAAAAAAAD//////////yALEABBuJbAAAvZFiBjYW4ndCBiZSByZXByZXNlbnRlZCBhcyBhIEphdmFTY3JpcHQgbnVtYmVyHAsQAAAAAAA4CxAALAAAAEgAAAAvaG9tZS9tYXJjaW4vLmNhcmdvL3JlZ2lzdHJ5L3NyYy9pbmRleC5jcmF0ZXMuaW8tNmYxN2QyMmJiYTE1MDAxZi9zZXJkZS13YXNtLWJpbmRnZW4tMC42LjUvc3JjL2xpYi5ycwAAAHgLEABlAAAANQAAAA4AAABjbG9zdXJlIGludm9rZWQgcmVjdXJzaXZlbHkgb3IgYWZ0ZXIgYmVpbmcgZHJvcHBlZC9ydXN0Yy85YjAwOTU2ZTU2MDA5YmFiMmFhMTVkN2JmZjEwOTE2NTk5ZTNkNmQ2L2xpYnJhcnkvYWxsb2Mvc3JjL3ZlYy9tb2QucnMAACIMEABMAAAAYAgAACQAAAAiDBAATAAAABoGAAAVAAAAL2hvbWUvbWFyY2luLy5jYXJnby9yZWdpc3RyeS9zcmMvaW5kZXguY3JhdGVzLmlvLTZmMTdkMjJiYmExNTAwMWYvYXZ0LTAuMTUuMC9zcmMvcGFyc2Vy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Ai8wMTIzAgICAgICNAICNTY3Ajg5Ojs8PT4/OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5QDk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTlBAgJCQwICREVGR0hJAko5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTlLAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICOTk5OUwCAgICAk1OT1ACAgJRAlJTAgICAgICAgICAgICAlRVAgJWAlcCAlhZWltcXV5fYGECYmMCZGVmZwJoAmlqa2wCAm1ub3ACcXICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnMCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ0dQICAgICAgJ2dzk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5eDk5OTk5OTk5OXl6AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ7OTl8OTl9AgICAgICAgICAgICAgICAgICAn4CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ/AgICgIGCAgICAgICAgICAgICAgICg4QCAgICAgICAgIChYZ1AgKHAgICiAICAgICAgKJigICAgICAgICAgICAgKLjAKNjgKPkJGSk5SVlgKXAgKYmZqbAgICAgICAgICAjk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OZwdHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHQICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAJ0CAgICnp8CBAIFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdAgIeAgICAgICAh8gISIjAiQlJicoKQIqAgICAqChoqOkpaYup6ipqqusrTMCAgICAgKuAgI1NjcCODk6Ozw9Pq85OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTlMAgICAgKwTk+xhYZ1AgKHAgICiAICAgICAgKJigICAgICAgICAgICAgKLjLKzjgKPkJGSk5SVlgKXAgKYmZqbAgICAgICAgICAlVVdVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVRVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVQBBvLvAAAspVVVVVRUAUFVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVQEAQe+7wAALxAEQQRBVVVVVVVdVVVVVVVVVVVVRVVUAAEBU9d1VVVVVVVVVVRUAAAAAAFVVVVX8XVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVBQAUABQEUFVVVVVVVVUVUVVVVVVVVVUAAAAAAABAVVVVVVVVVVVV1VdVVVVVVVVVVVVVVQUAAFRVVVVVVVVVVVVVVVVVFQAAVVVRVVVVVVUFEAAAAQFQVVVVVVVVVVVVVQFVVVVVVf////9/VVVVUFUAAFVVVVVVVVVVVVUFAEHAvcAAC5gEQFVVVVVVVVVVVVVVVVVFVAEAVFEBAFVVBVVVVVVVVVVRVVVVVVVVVVVVVVVVVVVEAVRVUVUVVVUFVVVVVVVVRUFVVVVVVVVVVVVVVVVVVVRBFRRQUVVVVVVVVVVQUVVVQVVVVVVVVVVVVVVVVVVVVAEQVFFVVVVVBVVVVVVVBQBRVVVVVVVVVVVVVVVVVVUEAVRVUVUBVVUFVVVVVVVVVUVVVVVVVVVVVVVVVVVVVUVUVVVRVRVVVVVVVVVVVVVVVFRVVVVVVVVVVVVVVVVVBFQFBFBVQVVVBVVVVVVVVVVRVVVVVVVVVVVVVVVVVVUURAUEUFVBVVUFVVVVVVVVVVBVVVVVVVVVVVVVVVVVFUQBVFVBVRVVVQVVVVVVVVVVUVVVVVVVVVVVVVVVVVVVVVVVRRUFRFUVVVVVVVVVVVVVVVVVVVVVVVVVVVVRAEBVVRUAQFVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVEAAFRVVQBAVVVVVVVVVVVVVVVVVVVVVVVVUFVVVVVVVRFRVVVVVVVVVVVVVVVVVQEAAEAABFUBAAABAAAAAAAAAABUVUVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVAQQAQUFVVVVVVVVQBVRVVVUBVFVVRUFVUVVVVVFVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVWqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqoAQYDCwAALkANVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVQFVVVVVVVVVVVVVVVUFVFVVVVVVVQVVVVVVVVVVBVVVVVVVVVUFVVVVf//99//911931tXXVRAAUFVFAQAAVVdRVVVVVVVVVVVVVRUAVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVBVVVVVVVVVVVRVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVAFVRVRVUBVVVVVVVVVVVVVVVVVVVVVVVVVVVVVxUUVVVVVVVVVVVVVVVVVVVFAEBEAQBUFQAAFFVVVVVVVVVVVVVVVQAAAAAAAABAVVVVVVVVVVVVVVVVAFVVVVVVVVVVVVVVVQAAUAVVVVVVVVVVVVUVAABVVVVQVVVVVVVVVQVQEFBVVVVVVVVVVVVVVVVVRVARUFVVVVVVVVVVVVVVVVVVAAAFVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVAAAAABABUUVVUUFVVVVVVVVVVVVVVVVVVVVVVAEGgxcAAC5MIVVUVAFVVVVVVVQVAVVVVVVVVVVVVVVVVAAAAAFVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVQAAAAAAAAAAVFVVVVVVVVVVVfVVVVVpVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVX9V9dVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVfVVVVVVVX1VVVVVVVVVVVVVVVf///1VVVVVVVVVVVVXVVVVVVdVVVVVdVfVVVVVVfVVfVXVVV1VVVVV1VfVddV1VXfVVVVVVVVVVV1VVVVVVVVVVd9XfVVVVVVVVVVVVVVVVVVVV/VVVVVVVVVdVVdVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV1VdVVVVVVVVVVVVVVVVXXVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUVUFVVVVVVVVVVVVVVVVVVVf3///////////////9fVdVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVAAAAAAAAAACqqqqqqqqaqqqqqqqqqqqqqqqqqqqqqqqqqqqqqlVVVaqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqWlVVVVVVVaqqqqqqqqqqqqqqqqqqCgCqqqpqqaqqqqqqqqqqqqqqqqqqqqqqqqqqaoGqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqVamqqqqqqqqqqqqqqaqqqqqqqqqqqqqqqqiqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqVVWVqqqqqqqqqqqqqqpqqqqqqqqqqqqqqlVVqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqlVVVVVVVVVVVVVVVVVVVVWqqqpWqqqqqqqqqqqqqqqqqmpVVVVVVVVVVVVVVVVVX1VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVRVAAABQVVVVVVVVVQVVVVVVVVVVVVVVVVVVVVVVVVVVVVBVVVVFRRVVVVVVVVVBVVRVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUFVVVVVVVQAAAABQVUUVVVVVVVVVVVVVBQBQVVVVVVUVAABQVVVVqqqqqqqqqlZAVVVVVVVVVVVVVVUVBVBQVVVVVVVVVVVVUVVVVVVVVVVVVVVVVVVVVVUBQEFBVVUVVVVUVVVVVVVVVVVVVVVUVVVVVVVVVVVVVVVVBBRUBVFVVVVVVVVVVVVVUFVFVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUVRRVVVVVaqqqqqqqqqqqlVVVQAAAAAAQBUAQb/NwAAL4QxVVVVVVVVVVUVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUAAADwqqpaVQAAAACqqqqqqqqqqmqqqqqqaqpVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUVqaqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqVlVVVVVVVVVVVVVVVVVVBVRVVVVVVVVVVVVVVVVVVVWqalVVAABUVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVRVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVFVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVQVAVQFBVQBVVVVVVVVVVVVVQBVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUFVVVVVVVXVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVQBVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVRVUVVVVVVVVVVVVVVVVVVVVVVVVVQFVVVVVVVVVVVVVVVVVVVVVVQUAAFRVVVVVVVVVVVVVVQVQVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUVVVVVVVVVVVVVVVVVUAAABAVVVVVVVVVVVVVRRUVRVQVVVVVVVVVVVVVVUVQEFVRVVVVVVVVVVVVVVVVVVVVUBVVVVVVVVVVRUAAQBUVVVVVVVVVVVVVVVVVVUVVVVVUFVVVVVVVVVVVVVVVQUAQAVVARRVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVRVQBFVFUVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVFRUAQFVVVVVVUFVVVVVVVVVVVVVVVVUVRFRVVVVVFVVVVQUAVABUVVVVVVVVVVVVVVVVVVVVVQAABURVVVVVVUVVVVVVVVVVVVVVVVVVVVVVVVVVVRQARBEEVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUVBVBVEFRVVVVVVVVQVVVVVVVVVVVVVVVVVVVVVVVVVVUVAEARVFVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUVUQAQVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVQEFEABVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVRUAAEFVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVRVFQQRVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVAAVVVFVVVVVVVVUBAEBVVVVVVVVVVVUVAARAVRVVVQFAAVVVVVVVVVVVVVUAAAAAQFBVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVAEAAEFVVVVVVVVVVVVVVVVVVVVVVVVVVBQAAAAAABQAEQVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVQFARRAAAFVVVVVVVVVVVVVVVVVVVVVVVVARVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVFVRVVUBVVVVVVVVVVVVVVVUFQFVEVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVQVAAAAUFVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVAFRVVVVVVVVVVVVVVVVVVQBAVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVRVVVVVVVVVVVVVVVVVVVVUVQFVVVVVVVVVVVVVVVVVVVVVVVVWqVFVVWlVVVaqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqlVVqqqqqqqqqqqqqqqqqqqqqqqqqqqqWlVVVVVVVVVVVVWqqlZVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVWqqappqqqqqqqqqqpqVVVVZVVVVVVVVVVqWVVVVapVVaqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqVVVVVVVVVVVBAFVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVAEGr2sAAC3VQAAAAAABAVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVEVAFAAAAAEABAFVVVVVVVVUFUFVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVQVUVVVVVVVVVVVVVVVVVVUAQa3bwAALAkAVAEG728AAC8UGVFVRVVVVVFVVVVUVAAEAAABVVVVVVVVVVVVVVVVVVVVVVVVVVQBAAAAAABQAEARAVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVFVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUVVVVVVVVVVVVVVVVVVVVUAVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVQBVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUAQFVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVQBAVVVVVVVVVVVVVVVVVVVXVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVdVVVVVVVVVVVVVVVVVVVVV1/f9/VVVVVVVVVVVVVVVVVVVVVVVV9f///////25VVVWqqrqqqqqq6vq/v1WqqlZVX1VVVapaVVVVVVVV//////////9XVVX9/9////////////////////////f//////1VVVf////////////9/1f9VVVX/////V1f//////////////////////3/3/////////////////////////////////////////////////////////////9f///////////////////9fVVXVf////////1VVVVV1VVVVVVVVfVVVVVdVVVVVVVVVVVVVVVVVVVVVVVVVVdX///////////////////////////9VVVVVVVVVVVVVVVX//////////////////////19VV3/9Vf9VVdVXVf//V1VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVf///1VXVVVVVVVV//////////////9////f/////////////////////////////////////////////////////////////1VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVX///9X//9XVf//////////////3/9fVfX///9V//9XVf//V1WqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqWlVVVVVVVVVVWZZVYaqlWapVVVVVVZVVVVVVVVVVlVVVAEGO4sAACwEDAEGc4sAAC4oqVVVVVVWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUVAJZqWlpqqgVAplmVZVVVVVVVVVVVAAAAAFVWVVWpVlVVVVVVVVVVVVZVVVVVVVVVVQAAAAAAAAAAVFVVVZVZWVVVZVVVaVVVVVVVVVVVVVVVlVaVaqqqqlWqqlpVVVVZVaqqqlVVVVVlVVVaVVVVVaVlVlVVVZVVVVVVVVWmlpqWWVllqZaqqmZVqlVaWVVaVmVVVVVqqqWlWlVVVaWqWlVVWVlVVVlVVVVVVZVVVVVVVVVVVVVVVVVVVVVVVVVVVWVV9VVVVWlVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVWqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqmqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqlWqqqqqqqqqqqpVVVWqqqqqpVpVVZqqWlWlpVVaWqWWpVpVVVWlWlWVVVVVfVVpWaVVX1VmVVVVVVVVVVVmVf///1VVVZqaappVVVXVVVVVVdVVVaVdVfVVVVVVvVWvqrqqq6qqmlW6qvquuq5VXfVVVVVVVVVVV1VVVVVZVVVVd9XfVVVVVVVVVaWqqlVVVVVVVdVXVVVVVVVVVVVVVVVVV61aVVVVVVVVVVVVqqqqqqqqqmqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqoAAADAqqpaVQAAAACqqqqqqqqqqmqqqqqqaqpVVVVVVVVVVVVVVVUFVFVVVVVVVVVVVVVVVVVVVapqVVUAAFRZqqpqVaqqqqqqqqpaqqqqqqqqqqqqqqqqqqpaVaqqqqqqqqq6/v+/qqqqqlZVVVVVVVVVVVVVVVVV9f///////0pzVmFsdWUoKQAAAMAzEAAIAAAAyDMQAAEAAABUcmllZCB0byBzaHJpbmsgdG8gYSBsYXJnZXIgY2FwYWNpdHncMxAAJAAAAC9ydXN0Yy85YjAwOTU2ZTU2MDA5YmFiMmFhMTVkN2JmZjEwOTE2NTk5ZTNkNmQ2L2xpYnJhcnkvYWxsb2Mvc3JjL3Jhd192ZWMucnMINBAATAAAAOcBAAAJAAAAbnVsbCBwb2ludGVyIHBhc3NlZCB0byBydXN0cmVjdXJzaXZlIHVzZSBvZiBhbiBvYmplY3QgZGV0ZWN0ZWQgd2hpY2ggd291bGQgbGVhZCB0byB1bnNhZmUgYWxpYXNpbmcgaW4gcnVzdAAAVHJpZWQgdG8gc2hyaW5rIHRvIGEgbGFyZ2VyIGNhcGFjaXR50DQQACQAAAAvcnVzdGMvOWIwMDk1NmU1NjAwOWJhYjJhYTE1ZDdiZmYxMDkxNjU5OWUzZDZkNi9saWJyYXJ5L2FsbG9jL3NyYy9yYXdfdmVjLnJz/DQQAEwAAADnAQAACQAAAGAAAAAMAAAABAAAAGEAAABiAAAAEQAAAGUAAAAMAAAABAAAAGYAAABnAAAAaAAAAC9ydXN0L2RlcHMvZGxtYWxsb2MtMC4yLjYvc3JjL2RsbWFsbG9jLnJzYXNzZXJ0aW9uIGZhaWxlZDogcHNpemUgPj0gc2l6ZSArIG1pbl9vdmVyaGVhZACINRAAKQAAAKgEAAAJAAAAYXNzZXJ0aW9uIGZhaWxlZDogcHNpemUgPD0gc2l6ZSArIG1heF9vdmVyaGVhZAAAiDUQACkAAACuBAAADQAAAEFjY2Vzc0Vycm9ybWVtb3J5IGFsbG9jYXRpb24gb2YgIGJ5dGVzIGZhaWxlZAAAADs2EAAVAAAAUDYQAA0AAABsaWJyYXJ5L3N0ZC9zcmMvYWxsb2MucnNwNhAAGAAAAGIBAAAJAAAAbGlicmFyeS9zdGQvc3JjL3Bhbmlja2luZy5yc5g2EAAcAAAAhAIAAB4AAABlAAAADAAAAAQAAABpAAAAagAAAAgAAAAEAAAAawAAAGoAAAAIAAAABAAAAGwAAABtAAAAbgAAABAAAAAEAAAAbwAAAHAAAABxAAAAAAAAAAEAAAByAAAASGFzaCB0YWJsZSBjYXBhY2l0eSBvdmVyZmxvdxw3EAAcAAAAL3J1c3QvZGVwcy9oYXNoYnJvd24tMC4xNC4zL3NyYy9yYXcvbW9kLnJzAABANxAAKgAAAFYAAAAoAAAARXJyb3IAAABzAAAADAAAAAQAAAB0AAAAdQAAAHYAAABjYXBhY2l0eSBvdmVyZmxvdwAAAJw3EAARAAAAbGlicmFyeS9hbGxvYy9zcmMvcmF3X3ZlYy5yc7g3EAAcAAAAGQAAAAUAAABhIGZvcm1hdHRpbmcgdHJhaXQgaW1wbGVtZW50YXRpb24gcmV0dXJuZWQgYW4gZXJyb3IAdwAAAAAAAAABAAAAeAAAAGxpYnJhcnkvYWxsb2Mvc3JjL2ZtdC5ycyg4EAAY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");class J{constructor(){let A=arguments.length>0&&void 0!==arguments[0]?arguments[0]:1;this.speed=A,this.startTime=performance.now()}getTime(){return this.speed*(performance.now()-this.startTime)/1e3}setTime(A){this.startTime=performance.now()-A/this.speed*1e3}}class S{constructor(){}getTime(A){}setTime(A){}}class Y{constructor(A,g){this.input="function"==typeof A.next?A:A[Symbol.iterator](),this.xfs=g??[]}map(A){return this.transform(function(A){return g=>I=>{g(A(I))}}(A))}flatMap(A){return this.transform(function(A){return g=>I=>{A(I).forEach(g)}}(A))}filter(A){return this.transform(function(A){return g=>I=>{A(I)&&g(I)}}(A))}take(A){return this.transform(function(A){let g=0;return I=>B=>{gB=>{g+=1,g>A&&I(B)}}(A))}transform(A){return new Y(this.input,this.xfs.concat([A]))}multiplex(A,g){return new Y(new p(this[Symbol.iterator](),A[Symbol.iterator](),g))}toArray(){return Array.from(this)}[Symbol.iterator](){let A=0,g=[],I=!1;const B=(Q=this.xfs,C=A=>g.push(A),Q.reverse().reduce(((A,g)=>{const I=U(g(A.step));return{step:I.step,flush:()=>{I.flush(),A.flush()}}}),U(C)));var Q,C;return{next:()=>{for(A===g.length&&(g=[],A=0);0===g.length;){const A=this.input.next();if(A.done)break;B.step(A.value)}return 0!==g.length||I||(B.flush(),I=!0),g.length>0?{done:!1,value:g[A++]}:{done:!0}}}}}function U(A){return"function"==typeof A?{step:A,flush:()=>{}}:A}class p{constructor(A,g,I){this.left=A,this.right=g,this.comparator=I}[Symbol.iterator](){let A,g;return{next:()=>{if(void 0===A&&void 0!==this.left){const g=this.left.next();g.done?this.left=void 0:A=g.value}if(void 0===g&&void 0!==this.right){const A=this.right.next();A.done?this.right=void 0:g=A.value}if(void 0===A&&void 0===g)return{done:!0};if(void 0===A){const A=g;return g=void 0,{done:!1,value:A}}if(void 0===g){const g=A;return A=void 0,{done:!1,value:g}}if(this.comparator(A,g)){const g=A;return A=void 0,{done:!1,value:g}}{const A=g;return g=void 0,{done:!1,value:A}}}}}}async function L(A){let g,I;if(A instanceof Response){const B=await A.text(),Q=function(A){const g=A.split("\n");let I;try{I=JSON.parse(g[0])}catch(A){return}const B=new Y(g).drop(1).filter((A=>"["===A[0])).map(JSON.parse).toArray();return{header:I,events:B}}(B);void 0!==Q?(g=Q.header,I=Q.events):g=JSON.parse(B)}else if("object"==typeof A&&"number"==typeof A.version)g=A;else{if(!Array.isArray(A))throw"invalid data";g=A[0],I=A.slice(1,A.length)}if(1===g.version)return function(A){let g=0;const I=new Y(A.stdout).map((A=>(g+=A[0],[g,"o",A[1]])));return{cols:A.width,rows:A.height,events:I}}(g);if(2===g.version)return function(A,g){return{cols:A.width,rows:A.height,theme:m(A.theme),events:g,idleTimeLimit:A.idle_time_limit}}(g,I);throw`asciicast v${g.version} format not supported`}function m(A){const g=/^#[0-9A-Fa-f]{6}$/,I=A?.fg,B=A?.bg,Q=A?.palette;if(g.test(I)&&g.test(B)&&/^(#[0-9A-Fa-f]{6}:){7,}#[0-9A-Fa-f]{6}$/.test(Q))return{foreground:I,background:B,palette:Q.split(":")}}function K(A){return"number"==typeof A?[A,"m",""]:[A[0],"m",A[1]]}function b(){let A=0;return function(g){return"m"===g[1]?[g[0],g[1],{index:A++,time:g[0],label:g[2]}]:g}}class H{constructor(){this.items=[],this.onPush=void 0}push(A){this.items.push(A),void 0!==this.onPush&&(this.onPush(this.popAll()),this.onPush=void 0)}popAll(){if(this.items.length>0){const A=this.items;return this.items=[],A}{const A=this;return new Promise((g=>{A.onPush=g}))}}}function v(A,g,I,B,Q,C,E,V,e){const i=function(A,g,I,B){return function(Q,C){"o"===Q?A(C):"i"===Q?I(C):"r"===Q?g(C.cols,C.rows):"m"===Q&&B(C)}}(g,I,B,Q);if(0===A)return e.debug("using no buffer"),function(A){return{pushEvent(g){A(g[1],g[2])},pushText(g){A("o",g)},stop(){}}}(i);{let g;return"number"==typeof(A=A??{})?(e.debug(`using fixed time buffer (${A} ms)`),g=g=>A):"function"==typeof A?(e.debug("using custom dynamic buffer"),g=A({logger:e})):(e.debug("using adaptive buffer",A),g=function(A,g){let{logger:I}=A,{minTime:B=25,maxLevel:Q=100,interval:C=50,windowSize:E=20,smoothingFactor:V=.2,minImprovementDuration:e=1e3}=g,i=0,t=a(i),o=[],s=0,n=0,r=null;function a(A){return 0===A?B:C*A}return A=>{if(o.push(A),o.lengthgg>A?g:A))}(o);s=B*V+s*(1-V),n=(B-g)*V+n*(1-V);const C=s+n;if(A>t&&I.debug("buffer underrun",{latency:A,maxJitter:s,jitterRange:n,bufferTime:t}),it)t=a(i+=1),I.debug("jitter increased, raising bufferTime",{latency:A,maxJitter:s,jitterRange:n,bufferTime:t});else if(i>1&&Ce&&(r=performance.now(),t=a(i-=1),I.debug("jitter decreased, lowering bufferTime",{latency:A,maxJitter:s,jitterRange:n,bufferTime:t})),t;return r=null,t}}({logger:e},A)),function(A,g,I,B,Q){let C=arguments.length>5&&void 0!==arguments[5]?arguments[5]:1/60,E=performance.now()-1e3*Q,V=A(0);const e=new H;C*=1e3;let i=-C,t=!1;function o(){return performance.now()-E}return setTimeout((async()=>{for(;!t;){const A=await e.popAll();if(t)return;for(const B of A){const A=1e3*B[0]+V;if(A-i0&&(await T(Q),t))return;I(B[0]),g(B[1],B[2]),i=A}}}),0),{pushEvent(g){let I=o()-1e3*g[0];I<0&&(B.debug(`correcting epoch by ${I} ms`),E+=I,I=0),V=A(I),e.push(g)},pushText(A){e.push([o()/1e3,"o",A])},stop(){t=!0,e.push(void 0)}}}(g,i,C,e,E??0,V)}}function T(A){return new Promise((g=>{setTimeout(g,A)}))}const O=1e6;function x(A){const g=new TextDecoder,I=new TextDecoder;let B,Q=function(A){const g=(new TextDecoder).decode(A);if("ALiS"!==g)throw"not an ALiS v1 live stream";Q=E},C=0;function E(A){const g=new X(new DataView(A)),I=g.getUint8();if(1!==I)throw`expected reset (0x01) frame, got ${I}`;return V(g,A)}function V(A,I){A.decodeVarUint();let E=A.decodeVarUint();B=E,E/=O,C=0;const V=A.decodeVarUint(),i=A.decodeVarUint(),t=A.getUint8();let o;if(8===t){const g=30;o=j(new Uint8Array(I,A.offset,g)),A.forward(g)}else if(16===t){const g=54;o=j(new Uint8Array(I,A.offset,g)),A.forward(g)}else if(0!==t)throw`alis: invalid theme format (${t})`;const s=A.decodeVarUint();let n;return s>0&&(n=g.decode(new Uint8Array(I,A.offset,s))),Q=e,{time:E,term:{size:{cols:V,rows:i},theme:o,init:n}}}function e(e){const i=new X(new DataView(e)),t=i.getUint8();return 1===t?V(i,e):111===t?function(A,I){A.decodeVarUint();const Q=A.decodeVarUint();B+=Q;const C=A.decodeVarUint(),E=g.decode(new Uint8Array(I,A.offset,C));return[B/O,"o",E]}(i,e):105===t?function(A,g){A.decodeVarUint();const Q=A.decodeVarUint();B+=Q;const C=A.decodeVarUint(),E=I.decode(new Uint8Array(g,A.offset,C));return[B/O,"i",E]}(i,e):114===t?function(A){A.decodeVarUint();const g=A.decodeVarUint();B+=g;const I=A.decodeVarUint(),Q=A.decodeVarUint();return[B/O,"r",{cols:I,rows:Q}]}(i):109===t?function(A,g){A.decodeVarUint();const I=A.decodeVarUint();B+=I;const Q=A.decodeVarUint(),E=new TextDecoder,V=C++,e=B/O,i=E.decode(new Uint8Array(g,A.offset,Q));return[e,"m",{index:V,time:e,label:i}]}(i,e):4===t?(Q=E,!1):void A.debug(`alis: unknown frame type: ${t}`)}return function(A){return Q(A)}}function j(A){const g=A.length/3,I=Z(A[0],A[1],A[2]),B=Z(A[3],A[4],A[5]),Q=[];for(let I=2;I1&&void 0!==arguments[1]?arguments[1]:0;this.inner=A,this.offset=g}forward(A){this.offset+=A}getUint8(){const A=this.inner.getUint8(this.offset);return this.offset+=1,A}decodeVarUint(){let A=BigInt(0),g=BigInt(0),I=this.getUint8();for(;I>127;)I&=127,A+=BigInt(I)<(await N(R),M))();class gA{constructor(A){this.core=A,this.driver=A.driver}onEnter(A){}init(){}play(){}pause(){}togglePlay(){}seek(A){return!1}step(A){}stop(){this.driver.stop()}}class IA extends gA{async init(){try{return await this.core._initializeDriver(),this.core._setState("idle")}catch(A){throw this.core._setState("errored"),A}}async play(){this.core._dispatchEvent("play");const A=await this.init();await A.doPlay()}async togglePlay(){await this.play()}async seek(A){const g=await this.init();return await g.seek(A)}async step(A){const g=await this.init();await g.step(A)}stop(){}}class BA extends gA{onEnter(A){let{reason:g,message:I}=A;this.core._dispatchEvent("idle",{message:I}),"paused"===g&&this.core._dispatchEvent("pause")}async play(){this.core._dispatchEvent("play"),await this.doPlay()}async doPlay(){const A=await this.driver.play();!0===A?this.core._setState("playing"):"function"==typeof A&&(this.core._setState("playing"),this.driver.stop=A)}async togglePlay(){await this.play()}seek(A){return this.driver.seek(A)}step(A){this.driver.step(A)}}class QA extends gA{onEnter(){this.core._dispatchEvent("playing")}pause(){!0===this.driver.pause()&&this.core._setState("idle",{reason:"paused"})}togglePlay(){this.pause()}seek(A){return this.driver.seek(A)}}class CA extends gA{onEnter(){this.core._dispatchEvent("loading")}}class EA extends gA{onEnter(A){let{message:g}=A;this.core._dispatchEvent("offline",{message:g})}}class VA extends gA{onEnter(A){let{message:g}=A;this.core._dispatchEvent("ended",{message:g})}async play(){this.core._dispatchEvent("play"),await this.driver.restart()&&this.core._setState("playing")}async togglePlay(){await this.play()}seek(A){return!0===this.driver.seek(A)&&(this.core._setState("idle"),!0)}}class eA extends gA{onEnter(){this.core._dispatchEvent("errored")}}class iA{constructor(A,I){this.logger=I.logger,this.state=new IA(this),this.stateName="uninitialized",this.driver=function(A){if("function"==typeof A)return A;"string"==typeof A&&(A="ws://"==A.substring(0,5)||"wss://"==A.substring(0,6)?{driver:"websocket",url:A}:"clock:"==A.substring(0,6)?{driver:"clock"}:"random:"==A.substring(0,7)?{driver:"random"}:"benchmark:"==A.substring(0,10)?{driver:"benchmark",url:A.substring(10)}:{driver:"recording",url:A});void 0===A.driver&&(A.driver="recording");if("recording"==A.driver&&(void 0===A.parser&&(A.parser="asciicast"),"string"==typeof A.parser)){if(!oA.has(A.parser))throw`unknown parser: ${A.parser}`;A.parser=oA.get(A.parser)}if(tA.has(A.driver)){const g=tA.get(A.driver);return(I,B)=>g(A,I,B)}throw`unsupported driver: ${JSON.stringify(A)}`}(A),this.changedLines=new Set,this.cursor=void 0,this.duration=void 0,this.cols=I.cols,this.rows=I.rows,this.speed=I.speed,this.loop=I.loop,this.autoPlay=I.autoPlay,this.idleTimeLimit=I.idleTimeLimit,this.preload=I.preload,this.startAt=g(I.startAt),this.poster=this._parsePoster(I.poster),this.markers=this._normalizeMarkers(I.markers),this.pauseOnMarkers=I.pauseOnMarkers,this.commandQueue=Promise.resolve(),this.eventHandlers=new Map([["ended",[]],["errored",[]],["idle",[]],["input",[]],["loading",[]],["marker",[]],["metadata",[]],["offline",[]],["pause",[]],["play",[]],["playing",[]],["ready",[]],["reset",[]],["resize",[]],["seeked",[]],["terminalUpdate",[]]])}async init(){this.wasm=await AA;const A=this._feed.bind(this),g=this._now.bind(this),I=this._resetVt.bind(this),B=this._resizeVt.bind(this),Q=this._setState.bind(this),C="npt"===this.poster.type?this.poster.value:void 0;this.driver=this.driver({feed:A,onInput:A=>{this._dispatchEvent("input",{data:A})},onMarker:A=>{let{index:g,time:I,label:B}=A;this._dispatchEvent("marker",{index:g,time:I,label:B})},reset:I,resize:B,now:g,setTimeout:(A,g)=>setTimeout(A,g/this.speed),setInterval:(A,g)=>setInterval(A,g/this.speed),setState:Q,logger:this.logger},{cols:this.cols,rows:this.rows,idleTimeLimit:this.idleTimeLimit,startAt:this.startAt,loop:this.loop,posterTime:C,markers:this.markers,pauseOnMarkers:this.pauseOnMarkers}),"function"==typeof this.driver&&(this.driver={play:this.driver}),(this.preload||void 0!==C)&&this._withState((A=>A.init()));const E="text"===this.poster.type?this._renderPoster(this.poster.value):null,V={isPausable:!!this.driver.pause,isSeekable:!!this.driver.seek,poster:E};if(void 0===this.driver.init&&(this.driver.init=()=>({})),void 0===this.driver.pause&&(this.driver.pause=()=>{}),void 0===this.driver.seek&&(this.driver.seek=A=>!1),void 0===this.driver.step&&(this.driver.step=A=>{}),void 0===this.driver.stop&&(this.driver.stop=()=>{}),void 0===this.driver.restart&&(this.driver.restart=()=>{}),void 0===this.driver.getCurrentTime){const A=this.driver.play;let g=new S;this.driver.play=()=>(g=new J(this.speed),A()),this.driver.getCurrentTime=()=>g.getTime()}this._dispatchEvent("ready",V),this.autoPlay&&this.play()}play(){return this._withState((A=>A.play()))}pause(){return this._withState((A=>A.pause()))}togglePlay(){return this._withState((A=>A.togglePlay()))}seek(A){return this._withState((async g=>{await g.seek(A)&&this._dispatchEvent("seeked")}))}step(A){return this._withState((g=>g.step(A)))}stop(){return this._withState((A=>A.stop()))}getChanges(){const A={};if(this.changedLines.size>0){const g=new Map,I=this.vt.rows;for(const A of this.changedLines)A1&&void 0!==arguments[1]?arguments[1]:{};for(const I of this.eventHandlers.get(A))I(g)}_withState(A){return this._enqueueCommand((()=>A(this.state)))}_enqueueCommand(A){return this.commandQueue=this.commandQueue.then(A),this.commandQueue}_setState(A){let g=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};if(this.stateName===A)return this.state;if(this.stateName=A,"playing"===A)this.state=new QA(this);else if("idle"===A)this.state=new BA(this);else if("loading"===A)this.state=new CA(this);else if("ended"===A)this.state=new VA(this);else if("offline"===A)this.state=new EA(this);else{if("errored"!==A)throw`invalid state: ${A}`;this.state=new eA(this)}return this.state.onEnter(g),this.state}_feed(A){this._doFeed(A),this._dispatchEvent("terminalUpdate")}_doFeed(A){this.vt.feed(A).forEach((A=>this.changedLines.add(A))),this.cursor=void 0}_now(){return performance.now()*this.speed}async _initializeDriver(){const A=await this.driver.init();this.cols=this.cols??A.cols??80,this.rows=this.rows??A.rows??24,this.duration=this.duration??A.duration,this.markers=this._normalizeMarkers(A.markers)??this.markers??[],0===this.cols&&(this.cols=80),0===this.rows&&(this.rows=24),this._initializeVt(this.cols,this.rows);const g=void 0!==A.poster?this._renderPoster(A.poster):null;this._dispatchEvent("metadata",{cols:this.cols,rows:this.rows,duration:this.duration,markers:this.markers,theme:A.theme,poster:g})}_resetVt(A,g){let I=arguments.length>2&&void 0!==arguments[2]?arguments[2]:void 0,B=arguments.length>3&&void 0!==arguments[3]?arguments[3]:void 0;this.logger.debug(`core: vt reset (${A}x${g})`),this.cols=A,this.rows=g,this.cursor=void 0,this._initializeVt(A,g),void 0!==I&&""!==I&&this._doFeed(I),this._dispatchEvent("reset",{cols:A,rows:g,theme:B})}_resizeVt(A,g){if(A===this.vt.cols&&g===this.vt.rows)return;this.vt.resize(A,g).forEach((A=>this.changedLines.add(A))),this.cursor=void 0,this.vt.cols=A,this.vt.rows=g,this.logger.debug(`core: vt resize (${A}x${g})`),this._dispatchEvent("resize",{cols:A,rows:g})}_initializeVt(A,g){this.vt=this.wasm.create(A,g,!0,100),this.vt.cols=A,this.vt.rows=g,this.changedLines.clear();for(let A=0;AB.feed(A)));const Q=B.getCursor()??!1,C=[];for(let A=0;A"number"==typeof A?[A,""]:A))}}const tA=new Map([["benchmark",function(A,g){let I,{url:B,iterations:Q=10}=A,{feed:C,setState:E,now:V}=g,e=0;return{async init(){const A=await L(await fetch(B)),{cols:g,rows:Q,events:C}=A;I=Array.from(C).filter((A=>{let[g,I,B]=A;return"o"===I})).map((A=>{let[g,I,B]=A;return[g,B]}));const E=I[I.length-1][0];for(const[A,g]of I)e+=new Blob([g]).size;return{cols:g,rows:Q,duration:E}},play(){const A=V();for(let A=0;A{E("stopped",{reason:"ended"})}),0),!0}}}],["clock",function(A,g,I){let{hourColor:B=3,minuteColor:Q=4,separatorColor:C=9}=A,{feed:E}=g,{cols:V=5,rows:e=1}=I;const i=Math.floor(e/2),t=Math.floor(V/2)-2,o=`[?25l[${i}B`;let s;const n=()=>{const A=new Date,g=A.getHours(),I=A.getMinutes(),E=[];E.push("\r");for(let A=0;A{n().forEach(E)};return{init:()=>{const A=[o].concat(n());return{cols:V,rows:e,duration:1440,poster:A}},play:()=>(E(o),r(),s=setInterval(r,1e3),!0),stop:()=>{clearInterval(s)},getCurrentTime:()=>{const A=new Date;return 60*A.getHours()+A.getMinutes()}}}],["eventsource",function(A,g){let I,Q,{url:C,bufferTime:E,minFrameTime:V}=A,{feed:e,reset:i,resize:t,onInput:o,onMarker:s,setState:n,logger:r}=g;r=new B(r,"eventsource: ");let a=new S;function c(A){void 0!==Q&&Q.stop(),Q=v(E,e,t,o,s,(A=>a.setTime(A)),A,V,r)}return{play:()=>{I=new EventSource(C),I.addEventListener("open",(()=>{r.info("opened"),c()})),I.addEventListener("error",(A=>{r.info("errored"),r.debug({e:A}),n("loading")})),I.addEventListener("message",(A=>{const g=JSON.parse(A.data);if(Array.isArray(g))Q.pushEvent(g);else if(void 0!==g.cols||void 0!==g.width){const A=g.cols??g.width,I=g.rows??g.height;r.debug(`vt reset (${A}x${I})`),n("playing"),c(g.time),i(A,I,g.init??void 0),a=new J,"number"==typeof g.time&&a.setTime(g.time)}else"offline"===g.state&&(r.info("stream offline"),n("offline",{message:"Stream offline"}),a=new S)})),I.addEventListener("done",(()=>{r.info("closed"),I.close(),n("ended",{message:"Stream ended"})}))},stop:()=>{void 0!==Q&&Q.stop(),void 0!==I&&I.close()},getCurrentTime:()=>a.getTime()}}],["random",function(A,g){let{feed:I,setTimeout:B}=g;const Q=" ".charCodeAt(0),C="~".charCodeAt(0)-Q;let E;const V=()=>{const A=Math.pow(5,4*Math.random());E=B(e,A)},e=()=>{V();const A=String.fromCharCode(Q+Math.floor(Math.random()*C));I(A)};return()=>(V(),()=>clearInterval(E))}],["recording",function(A,g,I){let B,Q,C,E,V,e,i,t,o,{feed:s,resize:n,onInput:r,onMarker:a,now:c,setTimeout:D,setState:w,logger:h}=g,{idleTimeLimit:l,startAt:y,loop:k,posterTime:G,markers:F,pauseOnMarkers:q,cols:d,rows:N}=I,M=0,u=0,f=0;async function R(A,g){const I=await fetch(A,g);if(!I.ok)throw`failed fetching recording from ${A}: ${I.status} ${I.statusText}`;return I}function J(){const A=C[M];A?i=D(S,function(A){let g=1e3*A-(c()-t);return g<0&&(g=0),g}(A[0])):L()}function S(){let A,g=C[M];do{u=g[0],M++;if(p(g))return;g=C[M],A=c()-t}while(g&&A>1e3*g[0]);J()}function U(){clearTimeout(i),i=null}function p(A){const[g,I,B]=A;if("o"===I)s(B);else if("i"===I)r(B);else if("r"===I){const[A,g]=B.split("x");n(A,g)}else if("m"===I&&(a(B),q))return m(),o=1e3*g,w("idle",{reason:"paused"}),!0;return!1}function L(){U(),f++,!0===k||"number"==typeof k&&f>"===A?A=I+5:"<<<"===A?A=I-.1*V:">>>"===A?A=I+.1*V:"%"===A[A.length-1]&&(A=parseFloat(A.substring(0,A.length-1))/100*V);else if("object"==typeof A)if("prev"===A.marker)A=T(I)??0,g&&I-A<1&&(A=T(A)??0);else if("next"===A.marker)A=function(A){if(0==E.length)return;let g,I=E.length-1,B=E[I];for(;B&&B[0]>A;)g=B[0],B=E[--I];return g}(I)??V;else if("number"==typeof A.marker){const g=E[A.marker];if(void 0===g)throw`invalid marker index: ${A.marker}`;A=g[0]}const B=Math.min(Math.max(A,0),V);B1&&void 0!==arguments[1]?arguments[1]:1/60;return B=>{let Q=0,C=0;return{step:A=>{Q++,void 0!==g?"o"===A[1]&&"o"===g[1]&&A[0]-g[0]{void 0!==g&&(B(g),C++),A.debug(`batched ${Q} frames to ${C} frames`)}}}}(g,C)).map(function(A,g,I){let B=0,Q=0;return function(C){const E=C[0]-B-A;return B=C[0],E>0&&(Q+=E,C[0]"m"!==A[1])).multiplex(V,((A,g)=>A[0]"i"===A[1]?[A[0]+E,A[1],A[2]]:A)),e.sort(((A,g)=>A[0]-g[0])));const t=e[e.length-1][0],o=B-i.offset;return{...A,events:e,duration:t,effectiveStartAt:o}}(await g(await function(A){let{url:g,data:I,fetchOpts:B={}}=A;if("string"==typeof g)return R(g,B);if(Array.isArray(g))return Promise.all(g.map((A=>R(A,B))));if(void 0!==I)return"function"==typeof I&&(I=I()),I instanceof Promise||(I=Promise.resolve(I)),I.then((A=>"string"==typeof A||A instanceof ArrayBuffer?new Response(A):A));throw"failed fetching recording file: url/data missing in src"}(A),{encoding:o}),h,{idleTimeLimit:l,startAt:y,minFrameTime:I,inputOffset:i,markers_:F});if(({cols:B,rows:Q,events:C,duration:V,effectiveStartAt:e}=s),d=d??B,N=N??Q,0===C.length)throw"recording is missing events";void 0!==t&&function(A,g){const I=document.createElement("a"),B=A.events.map((A=>"m"===A[1]?[A[0],A[1],A[2].label]:A)),Q=function(A){return`${JSON.stringify({version:2,width:A.cols,height:A.rows})}\n${A.events.map(JSON.stringify).join("\n")}\n`}({...A,events:B});I.href=URL.createObjectURL(new Blob([Q],{type:"text/plain"})),I.download=g,I.click()}(s,t);const n=void 0!==G?(r=G,C.filter((A=>A[0]A[2]))):void 0;var r;return E=C.filter((A=>"m"===A[1])).map((A=>[A[0],A[2].label])),{cols:B,rows:Q,duration:V,theme:s.theme,poster:n,markers:E}},play:function(){if(i)throw"already playing";if(void 0===C[M])throw"already ended";return null!==e&&v(e),H(),!0},pause:m,seek:v,step:function(A){let g,I;if(void 0===A&&(A=1),A>0){let B=M;g=C[B];for(let Q=0;Q{const A=I.protocol||"raw";a.info("opened"),a.info(`activating ${A} protocol handler`),"v1.alis"===A?I.onmessage=G(x(a)):"v2.asciicast"===A?I.onmessage=G(function(){let A=function(I){const B=JSON.parse(I);if(2!==B.version)throw"not an asciicast v2 stream";return A=g,{time:0,term:{size:{cols:B.width,rows:B.height}}}};function g(A){const g=JSON.parse(A);if("r"===g[1]){const[A,I]=g[2].split("x");return[g[0],"r",{cols:A,rows:I}]}return g}return function(g){return A(g)}}()):"raw"===A&&(I.onmessage=G(z())),c=setTimeout((()=>{h=0}),1e3)},I.onclose=A=>{if(clearTimeout(D),d(),l||1e3===A.code||1005===A.code)a.info("closed"),r("ended",{message:"Stream ended"});else if(1002===A.code)a.debug(`close reason: ${A.reason}`),r("ended",{message:"Err: Player not compatible with the server"});else{clearTimeout(c);const A=V(h++);a.info(`unclean close, reconnecting in ${A}...`),r("loading"),setTimeout(k,A)}},y=!1}function G(A){return D=setTimeout(q,5e3),function(g){try{const I=A(g.data);if(Q)if(Array.isArray(I))Q.pushEvent(I);else if("string"==typeof I)Q.pushText(I);else if("object"!=typeof I||Array.isArray(I)){if(!1===I)q();else if(void 0!==I)throw`unexpected value from protocol handler: ${I}`}else F(I);else if("object"!=typeof I||Array.isArray(I)){if(void 0!==I)throw clearTimeout(D),`unexpected value from protocol handler: ${I}`;clearTimeout(D),D=setTimeout(q,1e3)}else F(I),clearTimeout(D)}catch(A){throw I.close(),A}}}function F(A){let{time:g,term:I}=A;const{size:B,init:C,theme:V}=I,{cols:c,rows:D}=B;a.info(`stream reset (${c}x${D} @${g})`),r("playing"),d(),Q=v(E,i,o,s,n,(A=>w.setTime(A)),g,e,a),t(c,D,C,V),w=new J,y=!0,"number"==typeof g&&w.setTime(g)}function q(){d(),y?(a.info("stream ended"),r("offline",{message:"Stream ended"})):(a.info("stream offline"),r("offline",{message:"Stream offline"})),w=new S}function d(){Q&&Q.stop(),Q=null}return{play:()=>{k()},stop:()=>{l=!0,d(),void 0!==I&&I.close()},getCurrentTime:()=>w.getTime()}}]]),oA=new Map([["asciicast",L],["typescript",async function(A,g){let{encoding:I}=g;const B=new TextDecoder(I);let Q,C,E=(await A[0].text()).split("\n").filter((A=>A.length>0)).map((A=>A.split(" ")));E[0].length<3&&(E=E.map((A=>["O",A[0],A[1]])));const V=await A[1].arrayBuffer(),e=new Uint8Array(V),i=e.findIndex((A=>10==A))+1,t=B.decode(e.subarray(0,i)).match(/COLUMNS="(\d+)" LINES="(\d+)"/);null!==t&&(Q=parseInt(t[1],10),C=parseInt(t[2],10));const o={array:e,cursor:i};let s=o;if(void 0!==A[2]){const g=await A[2].arrayBuffer();s={array:new Uint8Array(g),cursor:i}}const n=[];let r=0;for(const A of E)if(r+=parseFloat(A[1]),"O"===A[0]){const g=parseInt(A[2],10),I=o.array.subarray(o.cursor,o.cursor+g),Q=B.decode(I);n.push([r,"o",Q]),o.cursor+=g}else if("I"===A[0]){const g=parseInt(A[2],10),I=s.array.subarray(s.cursor,s.cursor+g),Q=B.decode(I);n.push([r,"i",Q]),s.cursor+=g}else if("S"===A[0]&&"SIGWINCH"===A[2]){const g=parseInt(A[4].slice(5),10),I=parseInt(A[3].slice(5),10);n.push([r,"r",`${g}x${I}`])}else"H"===A[0]&&"COLUMNS"===A[2]?Q=parseInt(A[3],10):"H"===A[0]&&"LINES"===A[2]&&(C=parseInt(A[3],10));return Q=Q??80,C=C??24,{cols:Q,rows:C,events:n}}],["ttyrec",async function(A,g){let{encoding:I}=g;const B=new TextDecoder(I),Q=await A.arrayBuffer(),C=new Uint8Array(Q),E=_(C),V=E.time,e=B.decode(E.data).match(/\x1b\[8;(\d+);(\d+)t/),i=[];let t=80,o=24;null!==e&&(t=parseInt(e[2],10),o=parseInt(e[1],10));let s=0,n=_(C);for(;void 0!==n;){const A=n.time-V,g=B.decode(n.data);i.push([A,"o",g]),s+=n.len,n=_(C.subarray(s))}return{cols:t,rows:o,events:i}}]]);const sA={};const nA=Symbol("solid-proxy"),rA=Symbol("solid-track"),aA={equals:(A,g)=>A===g};let cA=vA;const DA=1,wA=2,hA={owned:null,cleanups:null,context:null,owner:null};var lA=null;let yA=null,kA=null,GA=null,FA=null,qA=0;function dA(A,g){const I=kA,B=lA,Q=0===A.length,C=Q?hA:{owned:null,cleanups:null,context:null,owner:void 0===g?B:g},E=Q?A:()=>A((()=>RA((()=>jA(C)))));lA=C,kA=null;try{return HA(E,!0)}finally{kA=I,lA=B}}function NA(A,g){const I={value:A,observers:null,observerSlots:null,comparator:(g=g?Object.assign({},aA,g):aA).equals||void 0};return[pA.bind(I),A=>("function"==typeof A&&(A=A(I.value)),LA(I,A))]}function MA(A,g,I){mA(KA(A,g,!1,DA))}function uA(A,g,I){I=I?Object.assign({},aA,I):aA;const B=KA(A,g,!0,0);return B.observers=null,B.observerSlots=null,B.comparator=I.equals||void 0,mA(B),pA.bind(B)}function fA(A){return HA(A,!1)}function RA(A){if(null===kA)return A();const g=kA;kA=null;try{return A()}finally{kA=g}}function JA(A){!function(A,g,I){cA=TA;const B=KA(A,g,!1,DA);B.user=!0,FA?FA.push(B):mA(B)}((()=>RA(A)))}function SA(A){return null===lA||(null===lA.cleanups?lA.cleanups=[A]:lA.cleanups.push(A)),A}function YA(){return kA}function UA(A){const g=uA(A),I=uA((()=>WA(g())));return I.toArray=()=>{const A=I();return Array.isArray(A)?A:null!=A?[A]:[]},I}function pA(){const A=yA;if(this.sources&&(this.state||A))if(this.state===DA||A)mA(this);else{const A=GA;GA=null,HA((()=>OA(this)),!1),GA=A}if(kA){const A=this.observers?this.observers.length:0;kA.sources?(kA.sources.push(this),kA.sourceSlots.push(A)):(kA.sources=[this],kA.sourceSlots=[A]),this.observers?(this.observers.push(kA),this.observerSlots.push(kA.sources.length-1)):(this.observers=[kA],this.observerSlots=[kA.sources.length-1])}return this.value}function LA(A,g,I){let B=A.value;return A.comparator&&A.comparator(B,g)||(A.value=g,A.observers&&A.observers.length&&HA((()=>{for(let g=0;g1e6)throw GA=[],new Error}),!1)),g}function mA(A){if(!A.fn)return;jA(A);const g=lA,I=kA,B=qA;kA=lA=A,function(A,g,I){let B;try{B=A.fn(g)}catch(g){A.pure&&(A.state=DA,A.owned&&A.owned.forEach(jA),A.owned=null),ZA(g)}(!A.updatedAt||A.updatedAt<=I)&&(null!=A.updatedAt&&"observers"in A?LA(A,B):A.value=B,A.updatedAt=I)}(A,A.value,B),kA=I,lA=g}function KA(A,g,I,B=DA,Q){const C={fn:A,state:B,updatedAt:null,owned:null,sources:null,sourceSlots:null,cleanups:null,value:g,owner:lA,context:null,pure:I};return null===lA||lA!==hA&&(lA.owned?lA.owned.push(C):lA.owned=[C]),C}function bA(A){const g=yA;if(0===A.state||g)return;if(A.state===wA||g)return OA(A);if(A.suspense&&RA(A.suspense.inFallback))return A.suspense.effects.push(A);const I=[A];for(;(A=A.owner)&&(!A.updatedAt||A.updatedAt=0;B--)if((A=I[B]).state===DA||g)mA(A);else if(A.state===wA||g){const g=GA;GA=null,HA((()=>OA(A,I[0])),!1),GA=g}}function HA(A,g){if(GA)return A();let I=!1;g||(GA=[]),FA?I=!0:FA=[],qA++;try{const g=A();return function(A){GA&&(vA(GA),GA=null);if(A)return;const g=FA;FA=null,g.length&&HA((()=>cA(g)),!1)}(I),g}catch(A){I||(FA=null),GA=null,ZA(A)}}function vA(A){for(let g=0;gA(g||{})))}function _A(){return!0}const $A={get:(A,g,I)=>g===nA?I:A.get(g),has:(A,g)=>g===nA||A.has(g),set:_A,deleteProperty:_A,getOwnPropertyDescriptor:(A,g)=>({configurable:!0,enumerable:!0,get:()=>A.get(g),set:_A,deleteProperty:_A}),ownKeys:A=>A.keys()};function Ag(A){return(A="function"==typeof A?A():A)?A:{}}function gg(A){const g="fallback"in A&&{fallback:()=>A.fallback};return uA(function(A,g,I={}){let B=[],Q=[],C=[],E=0,V=g.length>1?[]:null;return SA((()=>zA(C))),()=>{let e,i,t=A()||[];return t[rA],RA((()=>{let A,g,s,n,r,a,c,D,w,h=t.length;if(0===h)0!==E&&(zA(C),C=[],B=[],Q=[],E=0,V&&(V=[])),I.fallback&&(B=[XA],Q[0]=dA((A=>(C[0]=A,I.fallback()))),E=1);else if(0===E){for(Q=new Array(h),i=0;i=a&&D>=a&&B[c]===t[D];c--,D--)s[D]=Q[c],n[D]=C[c],V&&(r[D]=V[c]);for(A=new Map,g=new Array(D+1),i=D;i>=a;i--)w=t[i],e=A.get(w),g[i]=void 0===e?-1:e,A.set(w,i);for(e=a;e<=c;e++)w=B[e],i=A.get(w),void 0!==i&&-1!==i?(s[i]=Q[e],n[i]=C[e],V&&(r[i]=V[e]),i=g[i],A.set(w,i)):C[e]();for(i=a;iA.each),A.children,g||void 0))}function Ig(A){const g="fallback"in A&&{fallback:()=>A.fallback};return uA(function(A,g,I={}){let B,Q=[],C=[],E=[],V=[],e=0;return SA((()=>zA(E))),()=>{const i=A()||[];return i[rA],RA((()=>{if(0===i.length)return 0!==e&&(zA(E),E=[],Q=[],C=[],e=0,V=[]),I.fallback&&(Q=[XA],C[0]=dA((A=>(E[0]=A,I.fallback()))),e=1),C;for(Q[0]===XA&&(E[0](),E=[],Q=[],C=[],e=0),B=0;Bi[B])):B>=Q.length&&(C[B]=dA(t));for(;BA.each),A.children,g||void 0))}function Bg(A){let g=!1;const I=A.keyed,B=uA((()=>A.when),void 0,{equals:(A,I)=>g?A===I:!A==!I});return uA((()=>{const Q=B();if(Q){const B=A.children,C="function"==typeof B&&B.length>0;return g=I||C,C?RA((()=>B(Q))):B}return A.fallback}),void 0,void 0)}function Qg(A){let g=!1,I=!1;const B=UA((()=>A.children)),Q=uA((()=>{let A=B();Array.isArray(A)||(A=[A]);for(let g=0;gA[0]===I[0]&&(g?A[1]===I[1]:!A[1]==!I[1])&&A[2]===I[2]});return uA((()=>{const[B,C,E]=Q();if(B<0)return A.fallback;const V=E.children,e="function"==typeof V&&V.length>0;return g=I||e,e?RA((()=>V(C))):V}),void 0,void 0)}function Cg(A){return A}const Eg="_$DX_DELEGATE";function Vg(A,g,I,B={}){let Q;return dA((B=>{Q=B,g===document?A():rg(g,A(),g.firstChild?null:void 0,I)}),B.owner),()=>{Q(),g.textContent=""}}function eg(A,g,I){const B=document.createElement("template");B.innerHTML=A;let Q=B.content.firstChild;return I&&(Q=Q.firstChild),Q}function ig(A,g=window.document){const I=g[Eg]||(g[Eg]=new Set);for(let B=0,Q=A.length;BB.call(A,I[1],g))}else A.addEventListener(g,I)}function sg(A,g,I){if(!g)return I?function(A,g,I){null==I?A.removeAttribute(g):A.setAttribute(g,I)}(A,"style"):g;const B=A.style;if("string"==typeof g)return B.cssText=g;let Q,C;for(C in"string"==typeof I&&(B.cssText=I=void 0),I||(I={}),g||(g={}),I)null==g[C]&&B.removeProperty(C),delete I[C];for(C in g)Q=g[C],Q!==I[C]&&(B.setProperty(C,Q),I[C]=Q);return I}function ng(A,g,I){return RA((()=>A(g,I)))}function rg(A,g,I,B){if(void 0===I||B||(B=[]),"function"!=typeof g)return cg(A,g,B,I);MA((B=>cg(A,g(),B,I)),B)}function ag(A){const g=`$$${A.type}`;let I=A.composedPath&&A.composedPath()[0]||A.target;for(A.target!==I&&Object.defineProperty(A,"target",{configurable:!0,value:I}),Object.defineProperty(A,"currentTarget",{configurable:!0,get:()=>I||document}),sA.registry&&!sA.done&&(sA.done=!0,document.querySelectorAll("[id^=pl-]").forEach((g=>{for(;g&&8!==g.nodeType&&g.nodeValue!=="pl-"+A;){let A=g.nextSibling;g.remove(),g=A}g&&g.remove()})));I;){const B=I[g];if(B&&!I.disabled){const Q=I[`${g}Data`];if(void 0!==Q?B.call(I,Q,A):B.call(I,A),A.cancelBubble)return}I=I._$host||I.parentNode||I.host}}function cg(A,g,I,B,Q){for(sA.context&&!I&&(I=[...A.childNodes]);"function"==typeof I;)I=I();if(g===I)return I;const C=typeof g,E=void 0!==B;if(A=E&&I[0]&&I[0].parentNode||A,"string"===C||"number"===C){if(sA.context)return I;if("number"===C&&(g=g.toString()),E){let Q=I[0];Q&&3===Q.nodeType?Q.data=g:Q=document.createTextNode(g),I=hg(A,I,B,Q)}else I=""!==I&&"string"==typeof I?A.firstChild.data=g:A.textContent=g}else if(null==g||"boolean"===C){if(sA.context)return I;I=hg(A,I,B)}else{if("function"===C)return MA((()=>{let Q=g();for(;"function"==typeof Q;)Q=Q();I=cg(A,Q,I,B)})),()=>I;if(Array.isArray(g)){const C=[],V=I&&Array.isArray(I);if(Dg(C,g,I,Q))return MA((()=>I=cg(A,C,I,B,!0))),()=>I;if(sA.context){if(!C.length)return I;for(let A=0;AB-V){const Q=g[E];for(;V=0;C--){const E=g[C];if(Q!==E){const g=E.parentNode===A;B||C?g&&E.remove():g?A.replaceChild(Q,E):A.insertBefore(Q,I)}else B=!0}}else A.insertBefore(Q,I);return[Q]}const lg=Symbol("store-raw"),yg=Symbol("store-node"),kg=Symbol("store-name");function Gg(A,g){let I=A[nA];if(!I&&(Object.defineProperty(A,nA,{value:I=new Proxy(A,fg)}),!Array.isArray(A))){const g=Object.keys(A),B=Object.getOwnPropertyDescriptors(A);for(let Q=0,C=g.length;Q!0,deleteProperty:()=>!0,ownKeys:function(A){return Mg(A),Reflect.ownKeys(A)},getOwnPropertyDescriptor:function(A,g){const I=Reflect.getOwnPropertyDescriptor(A,g);return I&&!I.get&&I.configurable&&g!==nA&&g!==yg&&g!==kg?(delete I.value,delete I.writable,I.get=()=>A[nA][g],I):I}};function Rg(A,g,I,B=!1){if(!B&&A[g]===I)return;const Q=A[g],C=A.length;void 0===I?delete A[g]:A[g]=I;let E,V=dg(A);(E=Ng(V,g,Q))&&E.$((()=>I)),Array.isArray(A)&&A.length!==C&&(E=Ng(V,"length",C))&&E.$(A.length),(E=V._)&&E.$()}function Jg(A,g){const I=Object.keys(g);for(let B=0;B1){B=g.shift();const C=typeof B,E=Array.isArray(A);if(Array.isArray(B)){for(let Q=0;Q1)return void Sg(A[B],g,[B].concat(I));Q=A[B],I=[B].concat(I)}let C=g[0];"function"==typeof C&&(C=C(Q,I),C===Q)||void 0===B&&null==C||(C=qg(C),void 0===B||Fg(Q)&&Fg(C)&&!Array.isArray(C)?Jg(Q,C):Rg(A,B,C))}function Yg(...[A,g]){const I=qg(A||{}),B=Array.isArray(I);return[Gg(I),function(...A){fA((()=>{B&&1===A.length?function(A,g){if("function"==typeof g&&(g=g(A)),g=qg(g),Array.isArray(g)){if(A===g)return;let I=0,B=g.length;for(;I=E&&e>=E&&(C[V]===A[e]||Q&&C[E]&&A[E]&&C[V][Q]===A[e][Q]);V--,e--)s[e]=C[V];if(E>e||E>V){for(I=E;I<=e;I++)Rg(C,I,A[I]);for(;IA.length&&Rg(C,"length",A.length))}for(t=new Array(e+1),I=e;I>=E;I--)i=A[I],o=Q&&i?i[Q]:i,g=n.get(o),t[I]=void 0===g?-1:g,n.set(o,I);for(g=E;g<=V;g++)i=C[g],o=Q&&i?i[Q]:i,I=n.get(o),void 0!==I&&-1!==I&&(s[I]=C[g],I=t[I],n.set(o,I));for(I=E;IA.length&&Rg(C,"length",A.length))}const E=Object.keys(A);for(let g=0,I=E.length;g{if(!Fg(A)||!Fg(Q))return Q;const g=pg(Q,{[Ug]:A},Ug,I,B);return void 0===g?A:g}}const mg=eg("");var Kg=A=>{const g=uA((()=>{if(1==A.text.length){const g=A.text.codePointAt(0);if(g>=9600&&g<=9631||57520==g||57522==g)return g}})),I=uA((()=>g()?" ":A.text)),B=uA((()=>function(A,g,I){const B=A.get("fg"),Q=A.get("bg");let C={"--offset":g,width:`${I+.01}ch`};"string"==typeof B&&(C["--fg"]=B);"string"==typeof Q&&(C["--bg"]=Q);return C}(A.pen,A.offset,A.width))),Q=uA((()=>function(A,g,I){const B=bg(A.get("fg"),A.get("bold"),"fg-"),Q=bg(A.get("bg"),!1,"bg-");let C=I??"";void 0!==g&&(C+=` cp-${g.toString(16)}`);B&&(C+=" "+B);Q&&(C+=" "+Q);A.has("bold")&&(C+=" ap-bright");A.has("faint")&&(C+=" ap-faint");A.has("italic")&&(C+=" ap-italic");A.has("underline")&&(C+=" ap-underline");A.has("blink")&&(C+=" ap-blink");A.get("inverse")&&(C+=" ap-inverse");return C}(A.pen,g(),A.extraClass)));return(()=>{const A=mg.cloneNode(!0);return rg(A,I),MA((g=>{const I=Q(),C=B();return I!==g._v$&&tg(A,g._v$=I),g._v$2=sg(A,C,g._v$2),g}),{_v$:void 0,_v$2:void 0}),A})()};function bg(A,g,I){if("number"==typeof A)return g&&A<8&&(A+=8),`${I}${A}`}const Hg=eg('');var vg=A=>(()=>{const g=Hg.cloneNode(!0);return rg(g,PA(Ig,{get each(){return(()=>{if("number"==typeof A.cursor){const g=[];let I=0,B=0;for(;B0&&g.push({...Q,text:Q.text.substring(0,C)}),g.push({...Q,text:Q.text[C],offset:Q.offset+C,extraClass:"ap-cursor"}),CPA(Kg,function(...A){let g=!1;for(let I=0;I=0;I--){const B=Ag(A[I])[g];if(void 0!==B)return B}},has(g){for(let I=A.length-1;I>=0;I--)if(g in Ag(A[I]))return!0;return!1},keys(){const g=[];for(let I=0;I=0;g--)if(A[g]){const B=Object.getOwnPropertyDescriptors(A[g]);for(const g in B)g in I||Object.defineProperty(I,g,{enumerable:!0,get(){for(let I=A.length-1;I>=0;I--){const B=(A[I]||{})[g];if(void 0!==B)return B}}})}return I}(A))})),g})();const Tg=eg('
');var Og=A=>{const g=()=>A.lineHeight??1.3333333333,I=uA((()=>({width:`${A.cols}ch`,height:g()*A.rows+"em","font-size":100*(A.scale||1)+"%","font-family":A.fontFamily,"--term-line-height":`${g()}em`,"--term-cols":A.cols}))),B=uA((()=>A.cursor?.[0])),Q=uA((()=>A.cursor?.[1]));return(()=>{const g=Tg.cloneNode(!0),C=A.ref;return"function"==typeof C?ng(C,g):A.ref=g,rg(g,PA(gg,{get each(){return A.lines},children:(A,g)=>PA(vg,{get segments(){return A.segments},get cursor(){return uA((()=>g()===Q()))()?B():null}})})),MA((B=>{const Q=!(!A.blink&&!A.cursorHold),C=!!A.blink,E=I();return Q!==B._v$&&g.classList.toggle("ap-cursor-on",B._v$=Q),C!==B._v$2&&g.classList.toggle("ap-blink",B._v$2=C),B._v$3=sg(g,E,B._v$3),B}),{_v$:void 0,_v$2:void 0,_v$3:void 0}),g})()};const xg=eg(''),jg=eg(''),Zg=eg(''),Wg=eg(''),Xg=eg('
Keyboard shortcuts (?)Fullscreen (f)
'),zg=eg('');function Pg(A){let g=Math.floor(A);const I=Math.floor(g/86400);g%=86400;const B=Math.floor(g/3600);g%=3600;const Q=Math.floor(g/60);return g%=60,I>0?`${_g(I)}:${_g(B)}:${_g(Q)}:${_g(g)}`:B>0?`${_g(B)}:${_g(Q)}:${_g(g)}`:`${_g(Q)}:${_g(g)}`}function _g(A){return A<10?`0${A}`:A.toString()}var $g=A=>{const g=A=>g=>{g.preventDefault(),A(g)},I=()=>"number"==typeof A.currentTime?Pg(A.currentTime):"--:--",B=()=>"number"==typeof A.remainingTime?"-"+Pg(A.remainingTime):I(),Q=uA((()=>"number"==typeof A.duration?A.markers.filter((g=>g[0]{const g=A.currentTarget.offsetWidth,I=A.currentTarget.getBoundingClientRect(),B=A.clientX-I.left;return 100*Math.max(0,B/g)+"%"},[E,V]=NA(!1),e=function(A,g){let I=!0;return function(){if(I){I=!1;for(var B=arguments.length,Q=new Array(B),C=0;CI=!0),g)}}}(A.onSeekClick,50),i=g=>{g._marker||g.altKey||g.shiftKey||g.metaKey||g.ctrlKey||0!==g.button||(V(!0),A.onSeekClick(C(g)))},t=A=>{A.altKey||A.shiftKey||A.metaKey||A.ctrlKey||E()&&e(C(A))},o=()=>{V(!1)};return document.addEventListener("mouseup",o),SA((()=>{document.removeEventListener("mouseup",o)})),(()=>{const C=Xg.cloneNode(!0),E=C.firstChild,V=E.firstChild,e=V.nextSibling,o=E.nextSibling,s=o.nextSibling,n=s.nextSibling,r=A.ref;return"function"==typeof r?ng(r,C):A.ref=C,rg(C,PA(Bg,{get when(){return A.isPausable},get children(){const I=Zg.cloneNode(!0);return og(I,"click",g(A.onPlayClick),!0),rg(I,PA(Qg,{get children(){return[PA(Cg,{get when(){return A.isPlaying},get children(){return xg.cloneNode(!0)}}),PA(Cg,{get when(){return!A.isPlaying},get children(){return jg.cloneNode(!0)}})]}})),I}}),E),rg(V,I),rg(e,B),rg(o,PA(Bg,{get when(){return"number"==typeof A.progress||A.isSeekable},get children(){const I=Wg.cloneNode(!0),B=I.firstChild.nextSibling;return I.$$mousemove=t,I.$$mousedown=i,rg(I,PA(gg,{get each(){return Q()},children:(I,B)=>(()=>{const Q=zg.cloneNode(!0),C=Q.firstChild,E=C.nextSibling;var V;return Q.$$mousedown=A=>{A._marker=!0},og(Q,"click",(V=B(),g((()=>{A.onSeekClick({marker:V})}))),!0),rg(E,(()=>(A=>""===A[1]?Pg(A[0]):`${Pg(A[0])} - ${A[1]}`)(I))),MA((g=>{const B=(g=>g[0]/A.duration*100+"%")(I),E=!!(g=>"number"==typeof A.currentTime&&g[0]<=A.currentTime)(I);return B!==g._v$&&Q.style.setProperty("left",g._v$=B),E!==g._v$2&&C.classList.toggle("ap-marker-past",g._v$2=E),g}),{_v$:void 0,_v$2:void 0}),Q})()}),null),MA((g=>sg(B,{transform:`scaleX(${A.progress||0}`},g))),I}})),og(s,"click",g(A.onHelpClick),!0),og(n,"click",g(A.onFullscreenClick),!0),MA((()=>C.classList.toggle("ap-seekable",!!A.isSeekable))),C})()};ig(["click","mousedown","mousemove"]);const AI=eg('
💥
');var gI=A=>AI.cloneNode(!0);const II=eg('
');var BI=A=>II.cloneNode(!0);const QI=eg('
');var CI=A=>(()=>{const g=QI.cloneNode(!0),I=g.firstChild;return rg(I,(()=>A.message)),MA((g=>sg(I,{"font-family":A.fontFamily},g))),g})();const EI=eg('
');var VI=A=>(()=>{const g=EI.cloneNode(!0);var I;return og(g,"click",(I=A.onClick,A=>{A.preventDefault(),I(A)}),!0),g})();ig(["click"]);const eI=eg("
  • space - pause / resume
  • "),iI=eg("
  • / - rewind / fast-forward by 5 seconds
  • "),tI=eg("
  • Shift + / - rewind / fast-forward by 10%
  • "),oI=eg("
  • [ / ] - jump to the previous / next marker
  • "),sI=eg("
  • 0, 1, 2 ... 9 - jump to 0%, 10%, 20% ... 90%
  • "),nI=eg("
  • , / . - step back / forward, a frame at a time (when paused)
  • "),rI=eg('

    Keyboard shortcuts

    • f - toggle fullscreen mode
    • ? - toggle this help popup
    ');var aI=A=>(()=>{const g=rI.cloneNode(!0),I=g.firstChild,B=I.firstChild.firstChild.nextSibling,Q=B.firstChild;var C;return og(g,"click",(C=A.onClose,A=>{A.preventDefault(),C(A)}),!0),I.$$click=A=>{A.stopPropagation()},rg(B,PA(Bg,{get when(){return A.isPausable},get children(){return eI.cloneNode(!0)}}),Q),rg(B,PA(Bg,{get when(){return A.isSeekable},get children(){return[iI.cloneNode(!0),tI.cloneNode(!0),oI.cloneNode(!0),sI.cloneNode(!0),nI.cloneNode(!0)]}}),Q),MA((I=>sg(g,{"font-family":A.fontFamily},I))),g})();ig(["click"]);const cI=eg('
    ');var DI=A=>{const g=A.logger,I=A.core,B=A.autoPlay,[Q,C]=Yg({lines:[],cursor:void 0,charW:A.charW,charH:A.charH,bordersW:A.bordersW,bordersH:A.bordersH,containerW:0,containerH:0,isPausable:!0,isSeekable:!0,isFullscreen:!1,currentTime:null,remainingTime:null,progress:null,blink:!0,cursorHold:!1}),[E,V]=NA(!1),[e,i]=NA(B?null:"start"),[t,o]=NA(null),[s,n]=NA({cols:A.cols,rows:A.rows},{equals:(A,g)=>A.cols===g.cols&&A.rows===g.rows}),[r,a]=NA(void 0),[c,D]=Yg([]),[w,h]=NA(!1),[l,y]=NA(!1),[k,G]=NA(void 0),F=uA((()=>s().cols||80)),q=uA((()=>s().rows||24)),d=()=>!1===A.controls?0:32;let N,M,u,f,R,J,S,Y,U,p;function L(){gA(),_(),$()}function m(A){fA((()=>{A.rows{p=A}));I.addEventListener("ready",(A=>{let{isPausable:g,isSeekable:I,poster:B}=A;C({isPausable:g,isSeekable:I}),K(B),p()})),I.addEventListener("metadata",(A=>{let{cols:g,rows:I,duration:B,theme:Q,poster:C,markers:E}=A;fA((()=>{m({cols:g,rows:I}),a(B),G(Q),D(E),K(C)}))})),I.addEventListener("play",(()=>{i(null)})),I.addEventListener("playing",(()=>{fA((()=>{V(!0),i(null),T(),AA(),P()}))})),I.addEventListener("idle",(()=>{fA((()=>{V(!1),L()}))})),I.addEventListener("loading",(()=>{fA((()=>{V(!1),L(),i("loader")}))})),I.addEventListener("offline",(A=>{let{message:g}=A;fA((()=>{V(!1),L(),void 0!==g&&(o(g),i("info"))}))}));let H=0;I.addEventListener("ended",(A=>{let{message:I}=A;fA((()=>{V(!1),L(),void 0!==I&&(o(I),i("info"))})),g.debug(`view: render count: ${H}`)})),I.addEventListener("errored",(()=>{i("error")})),I.addEventListener("resize",m),I.addEventListener("reset",(A=>{let{cols:g,rows:I,theme:B}=A;fA((()=>{m({cols:g,rows:I}),G(B),T()}))})),I.addEventListener("seeked",(()=>{$()})),I.addEventListener("terminalUpdate",(()=>{void 0===N&&(N=requestAnimationFrame(T))}));const v=()=>{U=new ResizeObserver(function(A,g){let I;return function(){for(var B=arguments.length,Q=new Array(B),C=0;CA.apply(this,Q)),g)}}((A=>{C({containerW:R.offsetWidth,containerH:R.offsetHeight}),R.dispatchEvent(new CustomEvent("resize",{detail:{el:J}}))}),10)),U.observe(R)};JA((async()=>{g.info("view: mounted"),g.debug("view: font measurements",{charW:Q.charW,charH:Q.charH}),v(),C({containerW:R.offsetWidth,containerH:R.offsetHeight})})),SA((()=>{I.stop(),gA(),_(),U.disconnect()}));const T=async()=>{const A=await I.getChanges();fA((()=>{void 0!==A.lines&&A.lines.forEach(((A,g)=>{C("lines",g,Lg(A))})),void 0!==A.cursor&&C("cursor",Lg(A.cursor)),C("cursorHold",!0)})),N=void 0,H+=1},O=uA((()=>{const g=Q.charW*F()+Q.bordersW,I=Q.charH*q()+Q.bordersH;let B=A.fit??"width";if("both"===B||Q.isFullscreen){B=Q.containerW/(Q.containerH-d())>g/I?"height":"width"}if(!1===B||"none"===B)return{};if("width"===B){const A=Q.containerW/g;return{scale:A,width:Q.containerW,height:I*A+d()}}if("height"===B){const A=(Q.containerH-d())/I;return{scale:A,width:g*A,height:Q.containerH}}throw`unsupported fit mode: ${B}`})),x=()=>{C("isFullscreen",document.fullscreenElement??document.webkitFullscreenElement)},j=()=>{Q.isFullscreen?(document.exitFullscreen??document.webkitExitFullscreen??(()=>{})).apply(document):(R.requestFullscreen??R.webkitRequestFullscreen??(()=>{})).apply(R)},Z=()=>{l()?y(!1):(I.pause(),y(!0))},W=A=>{if(!(A.altKey||A.metaKey||A.ctrlKey)){if(" "==A.key)I.togglePlay();else if(","==A.key)I.step(-1),$();else if("."==A.key)I.step(),$();else if("f"==A.key)j();else if("["==A.key)I.seek({marker:"prev"});else if("]"==A.key)I.seek({marker:"next"});else if(A.key.charCodeAt(0)>=48&&A.key.charCodeAt(0)<=57){const g=(A.key.charCodeAt(0)-48)/10;I.seek(100*g+"%")}else if("?"==A.key)Z();else if("ArrowLeft"==A.key)A.shiftKey?I.seek("<<<"):I.seek("<<");else if("ArrowRight"==A.key)A.shiftKey?I.seek(">>>"):I.seek(">>");else{if("Escape"!=A.key)return;y(!1)}A.stopPropagation(),A.preventDefault()}},X=()=>{Q.isFullscreen&&IA(!0)},z=()=>{Q.isFullscreen||IA(!1)},P=()=>{u=setInterval($,100)},_=()=>{clearInterval(u)},$=async()=>{const A=await I.getCurrentTime(),g=await I.getRemainingTime(),B=await I.getProgress();C({currentTime:A,remainingTime:g,progress:B})},AA=()=>{f=setInterval((()=>{C((A=>{const g={blink:!A.blink};return g.blink&&(g.cursorHold=!1),g}))}),500)},gA=()=>{clearInterval(f),C("blink",!0)},IA=A=>{clearTimeout(M),A&&(M=setTimeout((()=>IA(!1)),2e3)),h(A)},BA=uA((()=>{const g=A.theme||"auto/asciinema";return"auto/"===g.slice(0,5)?{name:g.slice(5),colors:k()}:{name:g}})),QA=()=>{b.then((()=>I.play()))},CA=()=>{b.then((()=>I.togglePlay()))},EA=A=>{b.then((()=>I.seek(A)))},VA=(()=>{const g=cI.cloneNode(!0),I=g.firstChild;"function"==typeof R?ng(R,g):R=g,g.addEventListener("webkitfullscreenchange",x),g.addEventListener("fullscreenchange",x),g.$$mousemove=X,g.$$keydown=W;return"function"==typeof J?ng(J,I):J=I,I.$$mousemove=()=>IA(!0),I.addEventListener("mouseleave",z),rg(I,PA(Og,{get cols(){return F()},get rows(){return q()},get scale(){return O()?.scale},get blink(){return Q.blink},get lines(){return Q.lines},get cursor(){return Q.cursor},get cursorHold(){return Q.cursorHold},get fontFamily(){return A.terminalFontFamily},get lineHeight(){return A.terminalLineHeight},ref(A){"function"==typeof S?S(A):S=A}}),null),rg(I,PA(Bg,{get when(){return!1!==A.controls},get children(){return PA($g,{get duration(){return r()},get currentTime(){return Q.currentTime},get remainingTime(){return Q.remainingTime},get progress(){return Q.progress},markers:c,get isPlaying(){return E()},get isPausable(){return Q.isPausable},get isSeekable(){return Q.isSeekable},onPlayClick:CA,onFullscreenClick:j,onHelpClick:Z,onSeekClick:EA,ref(A){"function"==typeof Y?Y(A):Y=A}})}}),null),rg(I,PA(Qg,{get children(){return[PA(Cg,{get when(){return"start"==e()},get children(){return PA(VI,{onClick:QA})}}),PA(Cg,{get when(){return"loader"==e()},get children(){return PA(BI,{})}}),PA(Cg,{get when(){return"info"==e()},get children(){return PA(CI,{get message(){return t()},get fontFamily(){return A.terminalFontFamily}})}}),PA(Cg,{get when(){return"error"==e()},get children(){return PA(gI,{})}})]}}),null),rg(I,PA(Bg,{get when(){return l()},get children(){return PA(aI,{get fontFamily(){return A.terminalFontFamily},onClose:()=>y(!1),get isPausable(){return Q.isPausable},get isSeekable(){return Q.isSeekable}})}}),null),MA((B=>{const Q=!!(!0===A.controls||"auto"===A.controls&&w()),C=`ap-player asciinema-player-theme-${BA().name}`,E=(()=>{const g={};!1!==A.fit&&"none"!==A.fit||void 0===A.terminalFontSize||("small"===A.terminalFontSize?g["font-size"]="12px":"medium"===A.terminalFontSize?g["font-size"]="18px":"big"===A.terminalFontSize?g["font-size"]="24px":g["font-size"]=A.terminalFontSize);const I=O();void 0!==I.width&&(g.width=`${I.width}px`,g.height=`${I.height}px`);const B=BA().colors;return B&&(g["--term-color-foreground"]=B.foreground,g["--term-color-background"]=B.background,B.palette.forEach(((A,I)=>{g[`--term-color-${I}`]=A}))),g})();return Q!==B._v$&&g.classList.toggle("ap-hud",B._v$=Q),C!==B._v$2&&tg(I,B._v$2=C),B._v$3=sg(I,E,B._v$3),B}),{_v$:void 0,_v$2:void 0,_v$3:void 0}),g})();return VA};function wI(A,g){let I=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};const B=function(A,g){const I=80,B=24,Q=document.createElement("div");let C;Q.style.height="0px",Q.style.overflow="hidden",Q.style.fontSize="15px",document.body.appendChild(Q);const E=Vg((()=>(C=PA(Og,{cols:I,rows:B,lineHeight:g,fontFamily:A,lines:[]}),C)),Q),V={charW:C.clientWidth/I,charH:C.clientHeight/B,bordersW:C.offsetWidth-C.clientWidth,bordersH:C.offsetHeight-C.clientHeight};return E(),document.body.removeChild(Q),V}(I.terminalFontFamily,I.terminalLineHeight),Q={core:A,logger:I.logger,cols:I.cols,rows:I.rows,fit:I.fit,controls:I.controls,autoPlay:I.autoPlay,terminalFontSize:I.terminalFontSize,terminalFontFamily:I.terminalFontFamily,terminalLineHeight:I.terminalLineHeight,theme:I.theme,...B};let C;const E=Vg((()=>(C=PA(DI,Q),C)),g);return{el:C,dispose:E}}ig(["keydown","mousemove"]);const hI=["autoPlay","autoplay","cols","idleTimeLimit","loop","markers","pauseOnMarkers","poster","preload","rows","speed","startAt"],lI=["autoPlay","autoplay","cols","controls","fit","rows","terminalFontFamily","terminalFontSize","terminalLineHeight","theme"];return A.create=function(A,g){let B=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};const Q=B.logger??new I,C=new iA(A,function(A){let g=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};const I=Object.fromEntries(Object.entries(A).filter((A=>{let[g]=A;return hI.includes(g)})));return I.autoPlay??=I.autoplay,I.speed??=1,{...I,...g}}(B,{logger:Q})),{el:E,dispose:V}=wI(C,g,function(A){let g=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};const I=Object.fromEntries(Object.entries(A).filter((A=>{let[g]=A;return lI.includes(g)})));return I.autoPlay??=I.autoplay,I.controls??="auto",{...I,...g}}(B,{logger:Q})),e=C.init(),i={el:E,dispose:V,getCurrentTime:()=>e.then(C.getCurrentTime.bind(C)),getDuration:()=>e.then(C.getDuration.bind(C)),play:()=>e.then(C.play.bind(C)),pause:()=>e.then(C.pause.bind(C)),seek:A=>e.then((()=>C.seek(A))),addEventListener:(A,g)=>C.addEventListener(A,g.bind(i))};return i},A}({}); diff --git a/aider/website/assets/audio/auto-accept-architect/00-01.mp3 b/aider/website/assets/audio/auto-accept-architect/00-01.mp3 new file mode 100644 index 000000000..a3f2cb496 Binary files /dev/null and b/aider/website/assets/audio/auto-accept-architect/00-01.mp3 differ diff --git a/aider/website/assets/audio/auto-accept-architect/00-11.mp3 b/aider/website/assets/audio/auto-accept-architect/00-11.mp3 new file mode 100644 index 000000000..75b3724f0 Binary files /dev/null and b/aider/website/assets/audio/auto-accept-architect/00-11.mp3 differ diff --git a/aider/website/assets/audio/auto-accept-architect/00-40.mp3 b/aider/website/assets/audio/auto-accept-architect/00-40.mp3 new file mode 100644 index 000000000..3c2f3e7d6 Binary files /dev/null and b/aider/website/assets/audio/auto-accept-architect/00-40.mp3 differ diff --git a/aider/website/assets/audio/auto-accept-architect/00-48.mp3 b/aider/website/assets/audio/auto-accept-architect/00-48.mp3 new file mode 100644 index 000000000..80ba920a9 Binary files /dev/null and b/aider/website/assets/audio/auto-accept-architect/00-48.mp3 differ diff --git a/aider/website/assets/audio/auto-accept-architect/01-00.mp3 b/aider/website/assets/audio/auto-accept-architect/01-00.mp3 new file mode 100644 index 000000000..719ade695 Binary files /dev/null and b/aider/website/assets/audio/auto-accept-architect/01-00.mp3 differ diff --git a/aider/website/assets/audio/auto-accept-architect/01-28.mp3 b/aider/website/assets/audio/auto-accept-architect/01-28.mp3 new file mode 100644 index 000000000..e23d70786 Binary files /dev/null and b/aider/website/assets/audio/auto-accept-architect/01-28.mp3 differ diff --git a/aider/website/assets/audio/auto-accept-architect/01-42.mp3 b/aider/website/assets/audio/auto-accept-architect/01-42.mp3 new file mode 100644 index 000000000..19804c26b Binary files /dev/null and b/aider/website/assets/audio/auto-accept-architect/01-42.mp3 differ diff --git a/aider/website/assets/audio/auto-accept-architect/02-00.mp3 b/aider/website/assets/audio/auto-accept-architect/02-00.mp3 new file mode 100644 index 000000000..a2bab171c Binary files /dev/null and b/aider/website/assets/audio/auto-accept-architect/02-00.mp3 differ diff --git a/aider/website/assets/audio/auto-accept-architect/02-05.mp3 b/aider/website/assets/audio/auto-accept-architect/02-05.mp3 new file mode 100644 index 000000000..752b3f86c Binary files /dev/null and b/aider/website/assets/audio/auto-accept-architect/02-05.mp3 differ diff --git a/aider/website/assets/audio/auto-accept-architect/metadata.json b/aider/website/assets/audio/auto-accept-architect/metadata.json new file mode 100644 index 000000000..d3185544b --- /dev/null +++ b/aider/website/assets/audio/auto-accept-architect/metadata.json @@ -0,0 +1,11 @@ +{ + "00-01": "We're going to add a new feature to automatically accept edits proposed by the architect model.", + "00-11": "First, let's add the new switch.", + "00-40": "Aider figured out that it should be passed to the Coder class.", + "00-48": "Now we need to implement the functionality.", + "01-00": "Let's do some manual testing.", + "01-28": "That worked. Let's make sure we can turn it off too.", + "02-00": "Let's quickly tidy up the changes to HISTORY.", + "02-05": "All done!", + "01-42": "That worked too. Let's have aider update the HISTORY file to document the new feature." +} \ No newline at end of file diff --git a/aider/website/assets/audio/dont-drop-original-read-files/00-01.mp3 b/aider/website/assets/audio/dont-drop-original-read-files/00-01.mp3 new file mode 100644 index 000000000..30a9138cf Binary files /dev/null and b/aider/website/assets/audio/dont-drop-original-read-files/00-01.mp3 differ diff --git a/aider/website/assets/audio/dont-drop-original-read-files/00-10.mp3 b/aider/website/assets/audio/dont-drop-original-read-files/00-10.mp3 new file mode 100644 index 000000000..61fd0d612 Binary files /dev/null and b/aider/website/assets/audio/dont-drop-original-read-files/00-10.mp3 differ diff --git a/aider/website/assets/audio/dont-drop-original-read-files/00-20.mp3 b/aider/website/assets/audio/dont-drop-original-read-files/00-20.mp3 new file mode 100644 index 000000000..0d08c1664 Binary files /dev/null and b/aider/website/assets/audio/dont-drop-original-read-files/00-20.mp3 differ diff --git a/aider/website/assets/audio/dont-drop-original-read-files/01-20.mp3 b/aider/website/assets/audio/dont-drop-original-read-files/01-20.mp3 new file mode 100644 index 000000000..f60c9cf01 Binary files /dev/null and b/aider/website/assets/audio/dont-drop-original-read-files/01-20.mp3 differ diff --git a/aider/website/assets/audio/dont-drop-original-read-files/01-30.mp3 b/aider/website/assets/audio/dont-drop-original-read-files/01-30.mp3 new file mode 100644 index 000000000..62e123446 Binary files /dev/null and b/aider/website/assets/audio/dont-drop-original-read-files/01-30.mp3 differ diff --git a/aider/website/assets/audio/dont-drop-original-read-files/01-45.mp3 b/aider/website/assets/audio/dont-drop-original-read-files/01-45.mp3 new file mode 100644 index 000000000..ea4c98253 Binary files /dev/null and b/aider/website/assets/audio/dont-drop-original-read-files/01-45.mp3 differ diff --git a/aider/website/assets/audio/dont-drop-original-read-files/02-10.mp3 b/aider/website/assets/audio/dont-drop-original-read-files/02-10.mp3 new file mode 100644 index 000000000..63527e6e8 Binary files /dev/null and b/aider/website/assets/audio/dont-drop-original-read-files/02-10.mp3 differ diff --git a/aider/website/assets/audio/dont-drop-original-read-files/02-19.mp3 b/aider/website/assets/audio/dont-drop-original-read-files/02-19.mp3 new file mode 100644 index 000000000..3c26a9589 Binary files /dev/null and b/aider/website/assets/audio/dont-drop-original-read-files/02-19.mp3 differ diff --git a/aider/website/assets/audio/dont-drop-original-read-files/02-50.mp3 b/aider/website/assets/audio/dont-drop-original-read-files/02-50.mp3 new file mode 100644 index 000000000..b168823ce Binary files /dev/null and b/aider/website/assets/audio/dont-drop-original-read-files/02-50.mp3 differ diff --git a/aider/website/assets/audio/dont-drop-original-read-files/metadata.json b/aider/website/assets/audio/dont-drop-original-read-files/metadata.json new file mode 100644 index 000000000..9c7b354f7 --- /dev/null +++ b/aider/website/assets/audio/dont-drop-original-read-files/metadata.json @@ -0,0 +1,11 @@ +{ + "00-10": "We've added files that handle the main CLI and in-chat slash commands like /drop.", + "00-20": "Let's explain the needed change to aider.", + "01-20": "Ok, let's look at the code.", + "01-30": "I'd prefer not to use \"hasattr()\", let's ask for improvements.", + "01-45": "Let's try some manual testing.", + "02-10": "Looks good. Let's check the existing test suite to ensure we didn't break anything.", + "02-19": "Let's ask aider to add tests for this.", + "02-50": "Tests look reasonable, we're done!", + "00-01": "We're going to update the /drop command to keep any read only files that were originally specified at launch." +} \ No newline at end of file diff --git a/aider/website/assets/audio/model-accepts-settings/00-01.mp3 b/aider/website/assets/audio/model-accepts-settings/00-01.mp3 new file mode 100644 index 000000000..ae766a784 Binary files /dev/null and b/aider/website/assets/audio/model-accepts-settings/00-01.mp3 differ diff --git a/aider/website/assets/audio/model-accepts-settings/00-25.mp3 b/aider/website/assets/audio/model-accepts-settings/00-25.mp3 new file mode 100644 index 000000000..03288fa39 Binary files /dev/null and b/aider/website/assets/audio/model-accepts-settings/00-25.mp3 differ diff --git a/aider/website/assets/audio/model-accepts-settings/01-30.mp3 b/aider/website/assets/audio/model-accepts-settings/01-30.mp3 new file mode 100644 index 000000000..9c7d66f3e Binary files /dev/null and b/aider/website/assets/audio/model-accepts-settings/01-30.mp3 differ diff --git a/aider/website/assets/audio/model-accepts-settings/01-45.mp3 b/aider/website/assets/audio/model-accepts-settings/01-45.mp3 new file mode 100644 index 000000000..f2837b7ec Binary files /dev/null and b/aider/website/assets/audio/model-accepts-settings/01-45.mp3 differ diff --git a/aider/website/assets/audio/model-accepts-settings/02-00.mp3 b/aider/website/assets/audio/model-accepts-settings/02-00.mp3 new file mode 100644 index 000000000..fcbfb04e9 Binary files /dev/null and b/aider/website/assets/audio/model-accepts-settings/02-00.mp3 differ diff --git a/aider/website/assets/audio/model-accepts-settings/03-00.mp3 b/aider/website/assets/audio/model-accepts-settings/03-00.mp3 new file mode 100644 index 000000000..49a28b7e2 Binary files /dev/null and b/aider/website/assets/audio/model-accepts-settings/03-00.mp3 differ diff --git a/aider/website/assets/audio/model-accepts-settings/03-45.mp3 b/aider/website/assets/audio/model-accepts-settings/03-45.mp3 new file mode 100644 index 000000000..4e3cf5953 Binary files /dev/null and b/aider/website/assets/audio/model-accepts-settings/03-45.mp3 differ diff --git a/aider/website/assets/audio/model-accepts-settings/04-45.mp3 b/aider/website/assets/audio/model-accepts-settings/04-45.mp3 new file mode 100644 index 000000000..a0a149b8d Binary files /dev/null and b/aider/website/assets/audio/model-accepts-settings/04-45.mp3 differ diff --git a/aider/website/assets/audio/model-accepts-settings/05-00.mp3 b/aider/website/assets/audio/model-accepts-settings/05-00.mp3 new file mode 100644 index 000000000..712c73656 Binary files /dev/null and b/aider/website/assets/audio/model-accepts-settings/05-00.mp3 differ diff --git a/aider/website/assets/audio/model-accepts-settings/05-10.mp3 b/aider/website/assets/audio/model-accepts-settings/05-10.mp3 new file mode 100644 index 000000000..c7720262c Binary files /dev/null and b/aider/website/assets/audio/model-accepts-settings/05-10.mp3 differ diff --git a/aider/website/assets/audio/model-accepts-settings/06-00.mp3 b/aider/website/assets/audio/model-accepts-settings/06-00.mp3 new file mode 100644 index 000000000..d90f4993e Binary files /dev/null and b/aider/website/assets/audio/model-accepts-settings/06-00.mp3 differ diff --git a/aider/website/assets/audio/model-accepts-settings/07-43.mp3 b/aider/website/assets/audio/model-accepts-settings/07-43.mp3 new file mode 100644 index 000000000..12e56dc28 Binary files /dev/null and b/aider/website/assets/audio/model-accepts-settings/07-43.mp3 differ diff --git a/aider/website/assets/audio/model-accepts-settings/09-20.mp3 b/aider/website/assets/audio/model-accepts-settings/09-20.mp3 new file mode 100644 index 000000000..cc3bb03da Binary files /dev/null and b/aider/website/assets/audio/model-accepts-settings/09-20.mp3 differ diff --git a/aider/website/assets/audio/model-accepts-settings/10-20.mp3 b/aider/website/assets/audio/model-accepts-settings/10-20.mp3 new file mode 100644 index 000000000..16a60047b Binary files /dev/null and b/aider/website/assets/audio/model-accepts-settings/10-20.mp3 differ diff --git a/aider/website/assets/audio/model-accepts-settings/10-41.mp3 b/aider/website/assets/audio/model-accepts-settings/10-41.mp3 new file mode 100644 index 000000000..131650279 Binary files /dev/null and b/aider/website/assets/audio/model-accepts-settings/10-41.mp3 differ diff --git a/aider/website/assets/audio/model-accepts-settings/10-55.mp3 b/aider/website/assets/audio/model-accepts-settings/10-55.mp3 new file mode 100644 index 000000000..9a95cda50 Binary files /dev/null and b/aider/website/assets/audio/model-accepts-settings/10-55.mp3 differ diff --git a/aider/website/assets/audio/model-accepts-settings/11-28.mp3 b/aider/website/assets/audio/model-accepts-settings/11-28.mp3 new file mode 100644 index 000000000..57335a1ea Binary files /dev/null and b/aider/website/assets/audio/model-accepts-settings/11-28.mp3 differ diff --git a/aider/website/assets/audio/model-accepts-settings/12-00.mp3 b/aider/website/assets/audio/model-accepts-settings/12-00.mp3 new file mode 100644 index 000000000..a87d8c9dc Binary files /dev/null and b/aider/website/assets/audio/model-accepts-settings/12-00.mp3 differ diff --git a/aider/website/assets/audio/model-accepts-settings/12-32.mp3 b/aider/website/assets/audio/model-accepts-settings/12-32.mp3 new file mode 100644 index 000000000..ba1b34b91 Binary files /dev/null and b/aider/website/assets/audio/model-accepts-settings/12-32.mp3 differ diff --git a/aider/website/assets/audio/model-accepts-settings/12-48.mp3 b/aider/website/assets/audio/model-accepts-settings/12-48.mp3 new file mode 100644 index 000000000..2515c6499 Binary files /dev/null and b/aider/website/assets/audio/model-accepts-settings/12-48.mp3 differ diff --git a/aider/website/assets/audio/model-accepts-settings/13-00.mp3 b/aider/website/assets/audio/model-accepts-settings/13-00.mp3 new file mode 100644 index 000000000..db16d1e11 Binary files /dev/null and b/aider/website/assets/audio/model-accepts-settings/13-00.mp3 differ diff --git a/aider/website/assets/audio/model-accepts-settings/14-30.mp3 b/aider/website/assets/audio/model-accepts-settings/14-30.mp3 new file mode 100644 index 000000000..563231f17 Binary files /dev/null and b/aider/website/assets/audio/model-accepts-settings/14-30.mp3 differ diff --git a/aider/website/assets/audio/model-accepts-settings/14-45.mp3 b/aider/website/assets/audio/model-accepts-settings/14-45.mp3 new file mode 100644 index 000000000..0f870f836 Binary files /dev/null and b/aider/website/assets/audio/model-accepts-settings/14-45.mp3 differ diff --git a/aider/website/assets/audio/model-accepts-settings/14-59.mp3 b/aider/website/assets/audio/model-accepts-settings/14-59.mp3 new file mode 100644 index 000000000..65f2247d2 Binary files /dev/null and b/aider/website/assets/audio/model-accepts-settings/14-59.mp3 differ diff --git a/aider/website/assets/audio/model-accepts-settings/15-09.mp3 b/aider/website/assets/audio/model-accepts-settings/15-09.mp3 new file mode 100644 index 000000000..fd831dd86 Binary files /dev/null and b/aider/website/assets/audio/model-accepts-settings/15-09.mp3 differ diff --git a/aider/website/assets/audio/model-accepts-settings/15-34.mp3 b/aider/website/assets/audio/model-accepts-settings/15-34.mp3 new file mode 100644 index 000000000..fb6bffe6b Binary files /dev/null and b/aider/website/assets/audio/model-accepts-settings/15-34.mp3 differ diff --git a/aider/website/assets/audio/model-accepts-settings/15-44.mp3 b/aider/website/assets/audio/model-accepts-settings/15-44.mp3 new file mode 100644 index 000000000..0822147c9 Binary files /dev/null and b/aider/website/assets/audio/model-accepts-settings/15-44.mp3 differ diff --git a/aider/website/assets/audio/model-accepts-settings/16-04.mp3 b/aider/website/assets/audio/model-accepts-settings/16-04.mp3 new file mode 100644 index 000000000..26231d85c Binary files /dev/null and b/aider/website/assets/audio/model-accepts-settings/16-04.mp3 differ diff --git a/aider/website/assets/audio/model-accepts-settings/16-14.mp3 b/aider/website/assets/audio/model-accepts-settings/16-14.mp3 new file mode 100644 index 000000000..d431c03d4 Binary files /dev/null and b/aider/website/assets/audio/model-accepts-settings/16-14.mp3 differ diff --git a/aider/website/assets/audio/model-accepts-settings/16-29.mp3 b/aider/website/assets/audio/model-accepts-settings/16-29.mp3 new file mode 100644 index 000000000..f3f4ad358 Binary files /dev/null and b/aider/website/assets/audio/model-accepts-settings/16-29.mp3 differ diff --git a/aider/website/assets/audio/model-accepts-settings/16-47.mp3 b/aider/website/assets/audio/model-accepts-settings/16-47.mp3 new file mode 100644 index 000000000..7e770950c Binary files /dev/null and b/aider/website/assets/audio/model-accepts-settings/16-47.mp3 differ diff --git a/aider/website/assets/audio/model-accepts-settings/16-55.mp3 b/aider/website/assets/audio/model-accepts-settings/16-55.mp3 new file mode 100644 index 000000000..62b20bf9f Binary files /dev/null and b/aider/website/assets/audio/model-accepts-settings/16-55.mp3 differ diff --git a/aider/website/assets/audio/model-accepts-settings/17-59.mp3 b/aider/website/assets/audio/model-accepts-settings/17-59.mp3 new file mode 100644 index 000000000..cd1fbd8f6 Binary files /dev/null and b/aider/website/assets/audio/model-accepts-settings/17-59.mp3 differ diff --git a/aider/website/assets/audio/model-accepts-settings/18-35.mp3 b/aider/website/assets/audio/model-accepts-settings/18-35.mp3 new file mode 100644 index 000000000..deb37d2ad Binary files /dev/null and b/aider/website/assets/audio/model-accepts-settings/18-35.mp3 differ diff --git a/aider/website/assets/audio/model-accepts-settings/19-44.mp3 b/aider/website/assets/audio/model-accepts-settings/19-44.mp3 new file mode 100644 index 000000000..9b70789f5 Binary files /dev/null and b/aider/website/assets/audio/model-accepts-settings/19-44.mp3 differ diff --git a/aider/website/assets/audio/model-accepts-settings/19-54.mp3 b/aider/website/assets/audio/model-accepts-settings/19-54.mp3 new file mode 100644 index 000000000..195a77385 Binary files /dev/null and b/aider/website/assets/audio/model-accepts-settings/19-54.mp3 differ diff --git a/aider/website/assets/audio/model-accepts-settings/20-25.mp3 b/aider/website/assets/audio/model-accepts-settings/20-25.mp3 new file mode 100644 index 000000000..f0e80535a Binary files /dev/null and b/aider/website/assets/audio/model-accepts-settings/20-25.mp3 differ diff --git a/aider/website/assets/audio/model-accepts-settings/20-55.mp3 b/aider/website/assets/audio/model-accepts-settings/20-55.mp3 new file mode 100644 index 000000000..22d5e0b61 Binary files /dev/null and b/aider/website/assets/audio/model-accepts-settings/20-55.mp3 differ diff --git a/aider/website/assets/audio/model-accepts-settings/21-10.mp3 b/aider/website/assets/audio/model-accepts-settings/21-10.mp3 new file mode 100644 index 000000000..5bc319d9c Binary files /dev/null and b/aider/website/assets/audio/model-accepts-settings/21-10.mp3 differ diff --git a/aider/website/assets/audio/model-accepts-settings/22-32.mp3 b/aider/website/assets/audio/model-accepts-settings/22-32.mp3 new file mode 100644 index 000000000..79861e041 Binary files /dev/null and b/aider/website/assets/audio/model-accepts-settings/22-32.mp3 differ diff --git a/aider/website/assets/audio/model-accepts-settings/24-25.mp3 b/aider/website/assets/audio/model-accepts-settings/24-25.mp3 new file mode 100644 index 000000000..c46021a0c Binary files /dev/null and b/aider/website/assets/audio/model-accepts-settings/24-25.mp3 differ diff --git a/aider/website/assets/audio/model-accepts-settings/24-56.mp3 b/aider/website/assets/audio/model-accepts-settings/24-56.mp3 new file mode 100644 index 000000000..95436c938 Binary files /dev/null and b/aider/website/assets/audio/model-accepts-settings/24-56.mp3 differ diff --git a/aider/website/assets/audio/model-accepts-settings/25-35.mp3 b/aider/website/assets/audio/model-accepts-settings/25-35.mp3 new file mode 100644 index 000000000..942e2db22 Binary files /dev/null and b/aider/website/assets/audio/model-accepts-settings/25-35.mp3 differ diff --git a/aider/website/assets/audio/model-accepts-settings/26-20.mp3 b/aider/website/assets/audio/model-accepts-settings/26-20.mp3 new file mode 100644 index 000000000..3f4f8ff83 Binary files /dev/null and b/aider/website/assets/audio/model-accepts-settings/26-20.mp3 differ diff --git a/aider/website/assets/audio/model-accepts-settings/metadata.json b/aider/website/assets/audio/model-accepts-settings/metadata.json new file mode 100644 index 000000000..b9ce5c335 --- /dev/null +++ b/aider/website/assets/audio/model-accepts-settings/metadata.json @@ -0,0 +1,46 @@ +{ + "00-01": "Users sometimes run aider with \"reasoning\" settings that aren't supported by the model they're using. This can cause LLM API calls to completely fail, with non-specific error messages from the API provider. We're going to warn users up front to prevent this.", + "01-30": "Looks like it's including some extra changes we don't want.", + "01-45": "Let's have a look at the models code and clean up some stray lines.", + "02-00": "It also made the warning logic too conservative. We want to warn unless the setting is explicitly known to be supported.", + "03-00": "Ok, good. Now lets add a setting to silence these warnings for power users who are doing something intentional.", + "03-45": "Now we need to update the database of model settings to annotate which models support which reasoning settings. We'll start with the code that handles \"fallback\" settings for known models on unknown providers.", + "04-45": "Oh, we forgot to give aider the actual file with that code! Aider asks to see it.", + "05-00": "Ok, we've confused aider by asking it to change code it couldn't see.", + "05-10": "Let's clear the chat and refine the prompt and try again.", + "06-00": "Ok, looks good. Let's move on and update the full model settings database YAML file. Each main model like \"o1\" appears here from many providers, like OpenAI, OpenRouter, etc. We want to update them all.", + "09-20": "Looks good. Let's review the YAML file and eyeball all the relevant models.", + "10-20": "Now let's do some manual testing.", + "10-55": "Let's see if aider can spot the problem?", + "11-28": "That doesn't sound like a promising solution. Let's add more of the relevant code, clear history and try again.", + "12-00": "Ok, let's try aider's proposed solution.", + "12-48": "Time for some manual print debugging.", + "13-00": "It seems like the \"accept_settings\" value is not being set?", + "14-30": "Aha! I have a local model settings file for Sonnet which overrides aider's built in settings. And we did not update it. Let's add \"accepts_settings\" there.", + "14-45": "That was the problem, it wasn't a bug.", + "14-59": "Ok, let's add test coverage for all this stuff.", + "15-09": "And while aider writes tests, let's use \"git diff\" to review all the changes we've made.", + "15-34": "Aider is done writing tests, let's try them.", + "15-44": "One passed, one failed. Let's eyeball the passing test first.", + "16-04": "And let's see if aider can fix the failing test.", + "16-14": "Aider needs to see another file, which makes sense.", + "16-29": "It's found the problem, but is trying to \"fix\" the code. We want it to fix the test.", + "16-47": "Ok, tests are passing.", + "16-55": "We should stop and ask the user \"are you sure?\", not just flash a warning if they're about to break their API calls.", + "17-59": "Ok, that confirmation dialog looks good.", + "18-35": "This code is a little bit repetitive. Let's do a bit of refactoring.", + "20-25": "Are tests still passing after the refactor?", + "20-55": "Tests passed, good. Let's tweak the warning text.", + "21-10": "And now let's have aider update the docs to explain these changes.", + "22-32": "Let's proofread and edit the updated docs.", + "24-25": "And a \"git diff\" of all the docs changes to do a final check.", + "24-56": "Let's have aider update the project's HISTORY file.", + "26-20": "All done!", + "00-25": "Ok, let's ask aider to add a new model setting where we can note which reasoning settings it supports. And then print a warning if the user tries to apply an unsupported setting.", + "07-43": "Let's interrupt and refine the prompt to be more clear about which models to update.", + "10-41": "Ok, it should not be warning us about using \"thinking tokens\" with Sonnet 3.7.", + "12-32": "And see if it worked... Nope! Still getting the unneeded warning. Undo that change!", + "19-44": "Sonnet is messing up the code editing instructions, so aider is retrying.", + "19-54": "Let's clear the chat history and try again.", + "25-35": "We can refine the HISTORY entries a bit." +} \ No newline at end of file diff --git a/aider/website/assets/audio/tree-sitter-language-pack/00-01.mp3 b/aider/website/assets/audio/tree-sitter-language-pack/00-01.mp3 new file mode 100644 index 000000000..8fb957b00 Binary files /dev/null and b/aider/website/assets/audio/tree-sitter-language-pack/00-01.mp3 differ diff --git a/aider/website/assets/audio/tree-sitter-language-pack/00-10.mp3 b/aider/website/assets/audio/tree-sitter-language-pack/00-10.mp3 new file mode 100644 index 000000000..1cfa695a3 Binary files /dev/null and b/aider/website/assets/audio/tree-sitter-language-pack/00-10.mp3 differ diff --git a/aider/website/assets/audio/tree-sitter-language-pack/01-00.mp3 b/aider/website/assets/audio/tree-sitter-language-pack/01-00.mp3 new file mode 100644 index 000000000..b101c22e9 Binary files /dev/null and b/aider/website/assets/audio/tree-sitter-language-pack/01-00.mp3 differ diff --git a/aider/website/assets/audio/tree-sitter-language-pack/01-10.mp3 b/aider/website/assets/audio/tree-sitter-language-pack/01-10.mp3 new file mode 100644 index 000000000..69d768aa9 Binary files /dev/null and b/aider/website/assets/audio/tree-sitter-language-pack/01-10.mp3 differ diff --git a/aider/website/assets/audio/tree-sitter-language-pack/01-29.mp3 b/aider/website/assets/audio/tree-sitter-language-pack/01-29.mp3 new file mode 100644 index 000000000..057ddde63 Binary files /dev/null and b/aider/website/assets/audio/tree-sitter-language-pack/01-29.mp3 differ diff --git a/aider/website/assets/audio/tree-sitter-language-pack/01-45.mp3 b/aider/website/assets/audio/tree-sitter-language-pack/01-45.mp3 new file mode 100644 index 000000000..12171999d Binary files /dev/null and b/aider/website/assets/audio/tree-sitter-language-pack/01-45.mp3 differ diff --git a/aider/website/assets/audio/tree-sitter-language-pack/02-05.mp3 b/aider/website/assets/audio/tree-sitter-language-pack/02-05.mp3 new file mode 100644 index 000000000..073e5253a Binary files /dev/null and b/aider/website/assets/audio/tree-sitter-language-pack/02-05.mp3 differ diff --git a/aider/website/assets/audio/tree-sitter-language-pack/03-37.mp3 b/aider/website/assets/audio/tree-sitter-language-pack/03-37.mp3 new file mode 100644 index 000000000..5e00df9ed Binary files /dev/null and b/aider/website/assets/audio/tree-sitter-language-pack/03-37.mp3 differ diff --git a/aider/website/assets/audio/tree-sitter-language-pack/04-19.mp3 b/aider/website/assets/audio/tree-sitter-language-pack/04-19.mp3 new file mode 100644 index 000000000..225844442 Binary files /dev/null and b/aider/website/assets/audio/tree-sitter-language-pack/04-19.mp3 differ diff --git a/aider/website/assets/audio/tree-sitter-language-pack/05-02.mp3 b/aider/website/assets/audio/tree-sitter-language-pack/05-02.mp3 new file mode 100644 index 000000000..674b9f1d2 Binary files /dev/null and b/aider/website/assets/audio/tree-sitter-language-pack/05-02.mp3 differ diff --git a/aider/website/assets/audio/tree-sitter-language-pack/05-55.mp3 b/aider/website/assets/audio/tree-sitter-language-pack/05-55.mp3 new file mode 100644 index 000000000..d304a6aba Binary files /dev/null and b/aider/website/assets/audio/tree-sitter-language-pack/05-55.mp3 differ diff --git a/aider/website/assets/audio/tree-sitter-language-pack/06-12.mp3 b/aider/website/assets/audio/tree-sitter-language-pack/06-12.mp3 new file mode 100644 index 000000000..977347c9b Binary files /dev/null and b/aider/website/assets/audio/tree-sitter-language-pack/06-12.mp3 differ diff --git a/aider/website/assets/audio/tree-sitter-language-pack/06-30.mp3 b/aider/website/assets/audio/tree-sitter-language-pack/06-30.mp3 new file mode 100644 index 000000000..5b698f78d Binary files /dev/null and b/aider/website/assets/audio/tree-sitter-language-pack/06-30.mp3 differ diff --git a/aider/website/assets/audio/tree-sitter-language-pack/09-02.mp3 b/aider/website/assets/audio/tree-sitter-language-pack/09-02.mp3 new file mode 100644 index 000000000..882ad92ed Binary files /dev/null and b/aider/website/assets/audio/tree-sitter-language-pack/09-02.mp3 differ diff --git a/aider/website/assets/audio/tree-sitter-language-pack/09-45.mp3 b/aider/website/assets/audio/tree-sitter-language-pack/09-45.mp3 new file mode 100644 index 000000000..a932d475f Binary files /dev/null and b/aider/website/assets/audio/tree-sitter-language-pack/09-45.mp3 differ diff --git a/aider/website/assets/audio/tree-sitter-language-pack/10-15.mp3 b/aider/website/assets/audio/tree-sitter-language-pack/10-15.mp3 new file mode 100644 index 000000000..1b09a4c65 Binary files /dev/null and b/aider/website/assets/audio/tree-sitter-language-pack/10-15.mp3 differ diff --git a/aider/website/assets/audio/tree-sitter-language-pack/11-15.mp3 b/aider/website/assets/audio/tree-sitter-language-pack/11-15.mp3 new file mode 100644 index 000000000..70a82b8f2 Binary files /dev/null and b/aider/website/assets/audio/tree-sitter-language-pack/11-15.mp3 differ diff --git a/aider/website/assets/audio/tree-sitter-language-pack/12-00.mp3 b/aider/website/assets/audio/tree-sitter-language-pack/12-00.mp3 new file mode 100644 index 000000000..056d7837a Binary files /dev/null and b/aider/website/assets/audio/tree-sitter-language-pack/12-00.mp3 differ diff --git a/aider/website/assets/audio/tree-sitter-language-pack/13-00.mp3 b/aider/website/assets/audio/tree-sitter-language-pack/13-00.mp3 new file mode 100644 index 000000000..10ab3895d Binary files /dev/null and b/aider/website/assets/audio/tree-sitter-language-pack/13-00.mp3 differ diff --git a/aider/website/assets/audio/tree-sitter-language-pack/14-00.mp3 b/aider/website/assets/audio/tree-sitter-language-pack/14-00.mp3 new file mode 100644 index 000000000..af62e280d Binary files /dev/null and b/aider/website/assets/audio/tree-sitter-language-pack/14-00.mp3 differ diff --git a/aider/website/assets/audio/tree-sitter-language-pack/16-07.mp3 b/aider/website/assets/audio/tree-sitter-language-pack/16-07.mp3 new file mode 100644 index 000000000..e66fcffcf Binary files /dev/null and b/aider/website/assets/audio/tree-sitter-language-pack/16-07.mp3 differ diff --git a/aider/website/assets/audio/tree-sitter-language-pack/16-16.mp3 b/aider/website/assets/audio/tree-sitter-language-pack/16-16.mp3 new file mode 100644 index 000000000..430adead8 Binary files /dev/null and b/aider/website/assets/audio/tree-sitter-language-pack/16-16.mp3 differ diff --git a/aider/website/assets/audio/tree-sitter-language-pack/16-33.mp3 b/aider/website/assets/audio/tree-sitter-language-pack/16-33.mp3 new file mode 100644 index 000000000..0a3abb2aa Binary files /dev/null and b/aider/website/assets/audio/tree-sitter-language-pack/16-33.mp3 differ diff --git a/aider/website/assets/audio/tree-sitter-language-pack/17-01.mp3 b/aider/website/assets/audio/tree-sitter-language-pack/17-01.mp3 new file mode 100644 index 000000000..88aec8d6b Binary files /dev/null and b/aider/website/assets/audio/tree-sitter-language-pack/17-01.mp3 differ diff --git a/aider/website/assets/audio/tree-sitter-language-pack/17-12.mp3 b/aider/website/assets/audio/tree-sitter-language-pack/17-12.mp3 new file mode 100644 index 000000000..a45e86f93 Binary files /dev/null and b/aider/website/assets/audio/tree-sitter-language-pack/17-12.mp3 differ diff --git a/aider/website/assets/audio/tree-sitter-language-pack/19-04.mp3 b/aider/website/assets/audio/tree-sitter-language-pack/19-04.mp3 new file mode 100644 index 000000000..68573f57e Binary files /dev/null and b/aider/website/assets/audio/tree-sitter-language-pack/19-04.mp3 differ diff --git a/aider/website/assets/audio/tree-sitter-language-pack/19-28.mp3 b/aider/website/assets/audio/tree-sitter-language-pack/19-28.mp3 new file mode 100644 index 000000000..ac4368128 Binary files /dev/null and b/aider/website/assets/audio/tree-sitter-language-pack/19-28.mp3 differ diff --git a/aider/website/assets/audio/tree-sitter-language-pack/20-20.mp3 b/aider/website/assets/audio/tree-sitter-language-pack/20-20.mp3 new file mode 100644 index 000000000..9fcdfbf76 Binary files /dev/null and b/aider/website/assets/audio/tree-sitter-language-pack/20-20.mp3 differ diff --git a/aider/website/assets/audio/tree-sitter-language-pack/20-50.mp3 b/aider/website/assets/audio/tree-sitter-language-pack/20-50.mp3 new file mode 100644 index 000000000..e8442e10a Binary files /dev/null and b/aider/website/assets/audio/tree-sitter-language-pack/20-50.mp3 differ diff --git a/aider/website/assets/audio/tree-sitter-language-pack/21-30.mp3 b/aider/website/assets/audio/tree-sitter-language-pack/21-30.mp3 new file mode 100644 index 000000000..58793d46e Binary files /dev/null and b/aider/website/assets/audio/tree-sitter-language-pack/21-30.mp3 differ diff --git a/aider/website/assets/audio/tree-sitter-language-pack/24-39.mp3 b/aider/website/assets/audio/tree-sitter-language-pack/24-39.mp3 new file mode 100644 index 000000000..4bd9b922c Binary files /dev/null and b/aider/website/assets/audio/tree-sitter-language-pack/24-39.mp3 differ diff --git a/aider/website/assets/audio/tree-sitter-language-pack/26-55.mp3 b/aider/website/assets/audio/tree-sitter-language-pack/26-55.mp3 new file mode 100644 index 000000000..004daa8eb Binary files /dev/null and b/aider/website/assets/audio/tree-sitter-language-pack/26-55.mp3 differ diff --git a/aider/website/assets/audio/tree-sitter-language-pack/27-10.mp3 b/aider/website/assets/audio/tree-sitter-language-pack/27-10.mp3 new file mode 100644 index 000000000..8d30466f0 Binary files /dev/null and b/aider/website/assets/audio/tree-sitter-language-pack/27-10.mp3 differ diff --git a/aider/website/assets/audio/tree-sitter-language-pack/27-19.mp3 b/aider/website/assets/audio/tree-sitter-language-pack/27-19.mp3 new file mode 100644 index 000000000..1d0acac46 Binary files /dev/null and b/aider/website/assets/audio/tree-sitter-language-pack/27-19.mp3 differ diff --git a/aider/website/assets/audio/tree-sitter-language-pack/27-50.mp3 b/aider/website/assets/audio/tree-sitter-language-pack/27-50.mp3 new file mode 100644 index 000000000..a4e353f22 Binary files /dev/null and b/aider/website/assets/audio/tree-sitter-language-pack/27-50.mp3 differ diff --git a/aider/website/assets/audio/tree-sitter-language-pack/28-12.mp3 b/aider/website/assets/audio/tree-sitter-language-pack/28-12.mp3 new file mode 100644 index 000000000..ef3fa8435 Binary files /dev/null and b/aider/website/assets/audio/tree-sitter-language-pack/28-12.mp3 differ diff --git a/aider/website/assets/audio/tree-sitter-language-pack/28-52.mp3 b/aider/website/assets/audio/tree-sitter-language-pack/28-52.mp3 new file mode 100644 index 000000000..d5c3749e7 Binary files /dev/null and b/aider/website/assets/audio/tree-sitter-language-pack/28-52.mp3 differ diff --git a/aider/website/assets/audio/tree-sitter-language-pack/29-27.mp3 b/aider/website/assets/audio/tree-sitter-language-pack/29-27.mp3 new file mode 100644 index 000000000..f19f776a9 Binary files /dev/null and b/aider/website/assets/audio/tree-sitter-language-pack/29-27.mp3 differ diff --git a/aider/website/assets/audio/tree-sitter-language-pack/30-25.mp3 b/aider/website/assets/audio/tree-sitter-language-pack/30-25.mp3 new file mode 100644 index 000000000..63805e87d Binary files /dev/null and b/aider/website/assets/audio/tree-sitter-language-pack/30-25.mp3 differ diff --git a/aider/website/assets/audio/tree-sitter-language-pack/30-37.mp3 b/aider/website/assets/audio/tree-sitter-language-pack/30-37.mp3 new file mode 100644 index 000000000..67ca0999a Binary files /dev/null and b/aider/website/assets/audio/tree-sitter-language-pack/30-37.mp3 differ diff --git a/aider/website/assets/audio/tree-sitter-language-pack/31-52.mp3 b/aider/website/assets/audio/tree-sitter-language-pack/31-52.mp3 new file mode 100644 index 000000000..9aa50b655 Binary files /dev/null and b/aider/website/assets/audio/tree-sitter-language-pack/31-52.mp3 differ diff --git a/aider/website/assets/audio/tree-sitter-language-pack/32-27.mp3 b/aider/website/assets/audio/tree-sitter-language-pack/32-27.mp3 new file mode 100644 index 000000000..593369440 Binary files /dev/null and b/aider/website/assets/audio/tree-sitter-language-pack/32-27.mp3 differ diff --git a/aider/website/assets/audio/tree-sitter-language-pack/32-36.mp3 b/aider/website/assets/audio/tree-sitter-language-pack/32-36.mp3 new file mode 100644 index 000000000..2311eae15 Binary files /dev/null and b/aider/website/assets/audio/tree-sitter-language-pack/32-36.mp3 differ diff --git a/aider/website/assets/audio/tree-sitter-language-pack/32-42.mp3 b/aider/website/assets/audio/tree-sitter-language-pack/32-42.mp3 new file mode 100644 index 000000000..74b78ca83 Binary files /dev/null and b/aider/website/assets/audio/tree-sitter-language-pack/32-42.mp3 differ diff --git a/aider/website/assets/audio/tree-sitter-language-pack/32-54.mp3 b/aider/website/assets/audio/tree-sitter-language-pack/32-54.mp3 new file mode 100644 index 000000000..2e364c58e Binary files /dev/null and b/aider/website/assets/audio/tree-sitter-language-pack/32-54.mp3 differ diff --git a/aider/website/assets/audio/tree-sitter-language-pack/33-05.mp3 b/aider/website/assets/audio/tree-sitter-language-pack/33-05.mp3 new file mode 100644 index 000000000..87ef361e3 Binary files /dev/null and b/aider/website/assets/audio/tree-sitter-language-pack/33-05.mp3 differ diff --git a/aider/website/assets/audio/tree-sitter-language-pack/33-20.mp3 b/aider/website/assets/audio/tree-sitter-language-pack/33-20.mp3 new file mode 100644 index 000000000..8156566d3 Binary files /dev/null and b/aider/website/assets/audio/tree-sitter-language-pack/33-20.mp3 differ diff --git a/aider/website/assets/audio/tree-sitter-language-pack/34-10.mp3 b/aider/website/assets/audio/tree-sitter-language-pack/34-10.mp3 new file mode 100644 index 000000000..dc21d3064 Binary files /dev/null and b/aider/website/assets/audio/tree-sitter-language-pack/34-10.mp3 differ diff --git a/aider/website/assets/audio/tree-sitter-language-pack/35-00.mp3 b/aider/website/assets/audio/tree-sitter-language-pack/35-00.mp3 new file mode 100644 index 000000000..aba4436e6 Binary files /dev/null and b/aider/website/assets/audio/tree-sitter-language-pack/35-00.mp3 differ diff --git a/aider/website/assets/audio/tree-sitter-language-pack/metadata.json b/aider/website/assets/audio/tree-sitter-language-pack/metadata.json new file mode 100644 index 000000000..87eb83df8 --- /dev/null +++ b/aider/website/assets/audio/tree-sitter-language-pack/metadata.json @@ -0,0 +1,51 @@ +{ + "00-01": "We're going to add a ton of new languages to aider via tree-sitter-language-pack.", + "00-10": "First, lets try and find which languages it supports.", + "01-00": "Ok, there's a language definitions json file", + "01-10": "Does it have the github repos for each language?", + "01-29": "Ok, this is what we need.", + "01-45": "We need to get all the tags files from each repository for aider's repo-map. Let's have aider write a script to fetch them all.", + "02-05": "We'll show aider the language definitions json file.", + "03-37": "Looks like it can't find most of the tags.scm files.", + "04-19": "Maybe we should have it try other branches besides master?", + "05-02": "Ok, it seems to be downloading them now.", + "05-55": "Let's make it so we can re-run the script and only download files we haven't fetched yet.", + "06-12": "I see lots of tags files, so it's working.", + "06-30": "Ok, restart to run with latest code. This will take awhile to fetch them all.", + "09-02": "The Grep-AST module needs to know about all the new languages.", + "09-45": "Let's have aider add them all, and register each using their commonly used file extensions.", + "10-15": "Some of the languages need to be recognized by their base name, not by their extension.", + "11-15": "Let's sanity check if Grep-AST can handle PowerShell, one of the new languages.", + "12-00": "Looks like it's parsing PowerShell fine.", + "13-00": "Ok, let's download all the tags files into the right spot in the aider repo.", + "14-00": "This will take a minute...", + "16-07": "Delete some no-op or empty tags files.", + "16-16": "Let's commit all the unmodified tags files.", + "16-33": "We need to update each tag file, so that aider can identify names of functions, classes, etc in all these languages.", + "17-01": "Let's use a bash loop to script aider to modify each tags file.", + "17-12": "I'm giving aider a read-only example of an already modified tags file, as an example to follow.", + "19-04": "Looks like it correctly updated the first couple of tags files.", + "19-28": "Let's grep to watch aider's progress working through the list of files.", + "21-30": "This is going to take a little while...", + "24-39": "Let's add a README file with attribution for these tags files.", + "27-10": "Let's add test coverage to be sure these languages work with the repo-map.", + "27-19": "Each language needs a \"fixture\" with some sample code to parse during the test. Let's show aider the layout of the fixtures directory.", + "27-50": "We can use a bash loop to ask aider to add test coverage for each new tags file.", + "28-12": "We'll pass the fixtures directory listing to aider.", + "28-52": "Just need to fix the bash to correctly iterate through the list of tags files.", + "29-27": "I forgot to ask aider to actually generate a sample code fixture for each language.", + "30-25": "Lets run the repo-map tests to see if the first new test works.", + "30-37": "Tests for the Arduino language failed, with an empty repo-map? That's not good.", + "31-52": "Can aider figure out what's wrong?", + "32-42": "Oh! I'm not using the updated Grep-AST that knows about all the new languages.", + "32-54": "Ok, now we're parsing Arduino code properly. Undo aider's bogus test fix.", + "33-05": "Ok, arduino passes now but there seems to be a regression with tsx?", + "33-20": "Can aider figure out why?", + "34-10": "Let's check the parsers map.", + "35-00": "Well, that's all for this recording. The tsx problem was due to a bad mapping from \".tsx\" to \"typescript\" in the map that aider generated earlier.", + "20-20": "It's working on the Dart language now...", + "20-50": "E-lisp is up next...", + "26-55": "Ok, all the files are updated with tags for definitions and references to named code objects.", + "32-36": "Let me see if I can use Grep-AST on the new Arduino fixture code.", + "32-27": "Well, aider made the test pass by basically skipping Arduino." +} \ No newline at end of file diff --git a/aider/website/assets/azure-deployment.png b/aider/website/assets/azure-deployment.png new file mode 100644 index 000000000..0594cc08e Binary files /dev/null and b/aider/website/assets/azure-deployment.png differ diff --git a/aider/website/assets/home.css b/aider/website/assets/home.css new file mode 100644 index 000000000..9d64fd2e1 --- /dev/null +++ b/aider/website/assets/home.css @@ -0,0 +1,937 @@ +@font-face { + font-family: GlassTTYVT220; + src: local("Glass TTY VT220"), local("Glass TTY VT220 Medium"), url(/assets/Glass_TTY_VT220.ttf) format("truetype"); +} + +:root { + --primary: #4C6EF5; + --primary-dark: #3b5bdb; + --secondary: #12B886; + --dark: #212529; + --light: #F8F9FA; + --gray: #ADB5BD; + --code-bg: #282a36; + --terminal-green: #14b014; + + /* Typography variables */ + --heading-line-height: 1.2; + --body-line-height: 1.7; + --paragraph-spacing: 1.6rem; + --heading-spacing: 1.8rem; + + /* Typographic scale */ + --text-xs: 0.75rem; /* 12px */ + --text-sm: 0.875rem; /* 14px */ + --text-base: 1rem; /* 16px */ + --text-md: 1.125rem; /* 18px */ + --text-lg: 1.25rem; /* 20px */ + --text-xl: 1.5rem; /* 24px */ + --text-2xl: 1.875rem; /* 30px */ + --text-3xl: 2.25rem; /* 36px */ + --text-4xl: 3rem; /* 48px */ + + /* Spacing rhythm values */ + --space-1: 0.25rem; + --space-2: 0.5rem; + --space-3: 0.75rem; + --space-4: 1rem; + --space-6: 1.5rem; + --space-8: 2rem; + --space-12: 3rem; + --space-16: 4rem; +} + +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +html { + scroll-behavior: smooth; +} + +h1, h2, h3, h4, h5, h6 { + letter-spacing: -0.025em; + font-weight: 700; +} + +h1 { + font-size: var(--text-4xl); + line-height: 1.1; + font-weight: 800; +} + +h2 { + font-size: var(--text-2xl); + line-height: 1.2; +} + +h3 { + font-size: var(--text-xl); + line-height: 1.3; +} + +body { + font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; + font-size: 16px; + line-height: var(--body-line-height); + color: var(--dark); + background-color: var(--light); + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +.container { + width: 100%; + max-width: 1200px; + margin: 0 auto; + padding: 0 20px; +} + +header { + background-color: white; + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); + position: sticky; + top: 0; + z-index: 100; +} + +nav { + display: flex; + justify-content: space-between; + align-items: center; + padding: 20px 0; +} + +.logo { + font-size: 1.8rem; + font-weight: 600; + font-family: 'GlassTTYVT220', monospace; + color: var(--terminal-green); + text-decoration: none; + letter-spacing: 0.5px; +} + +.nav-links { + display: flex; + gap: 30px; +} + +.nav-links a { + color: var(--dark); + text-decoration: none; + font-weight: 500; + transition: color 0.3s; + font-size: var(--text-sm); + letter-spacing: 0.01em; + text-transform: uppercase; +} + +.nav-links a:hover { + color: var(--primary); +} + +.hero { + padding: 80px 0; + background: linear-gradient(135deg, rgba(20, 176, 20, 0.15) 0%, rgba(20, 176, 20, 0.1) 25%, rgba(201, 214, 255, 0.7) 50%, rgba(179, 198, 255, 0.8) 75%, rgba(163, 189, 255, 0.9) 100%); + position: relative; + overflow: hidden; +} + +.hero::before { + content: ""; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-image: url("data:image/svg+xml,%3Csvg width='60' height='60' viewBox='0 0 60 60' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cg fill='%234c6ef5' fill-opacity='0.07'%3E%3Cpath d='M36 34v-4h-2v4h-4v2h4v4h2v-4h4v-2h-4zm0-30V0h-2v4h-4v2h4v4h2V6h4V4h-4zM6 34v-4H4v4H0v2h4v4h2v-4h4v-2H6zM6 4V0H4v4H0v2h4v4h2V6h4V4H6z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E"); + opacity: 0.6; + z-index: 0; +} + +.hero::after { + content: ""; + position: absolute; + top: -50%; + left: -50%; + right: -50%; + bottom: -50%; + background: radial-gradient(circle, rgba(76, 110, 245, 0.2) 0%, rgba(20, 176, 20, 0.05) 50%, rgba(76, 110, 245, 0) 80%); + opacity: 0.7; + z-index: 0; + transform: rotate(30deg); + pointer-events: none; +} + +.hero .container { + position: relative; + z-index: 1; +} + +.hero-grid { + display: grid; + grid-template-columns: 40% 60%; + gap: 40px; + align-items: center; +} + +.hero-content { + text-align: left; + max-width: 90%; + padding-left: 0; +} + +.hero-video { + display: flex; + justify-content: center; + align-items: center; + margin-right: 40px; +} + +.hero h1 { + font-size: 2.5rem; + margin-bottom: var(--heading-spacing); + color: var(--dark); + text-align: left; + line-height: var(--heading-line-height); + font-weight: 800; + letter-spacing: -0.75px; +} + +.hero p { + font-size: 1.25rem; + max-width: 90%; + margin: 0 0 var(--paragraph-spacing); + color: #495057; + line-height: var(--body-line-height); + font-weight: 400; +} + +.buttons { + display: flex; + gap: 20px; + justify-content: flex-start; + margin-top: 10px; + margin-bottom: 0; +} + +.btn-primary { + padding: 14px 28px; + font-size: 1.1rem; +} + +.btn { + display: inline-block; + padding: 12px 24px; + border-radius: 6px; + font-weight: 600; + text-decoration: none; + transition: all 0.3s; +} + +.btn-primary { + background-color: var(--primary); + color: white; +} + +.btn-primary:hover { + background-color: var(--primary-dark); + transform: translateY(-2px); +} + +.btn-secondary { + background-color: white; + color: var(--primary); + border: 1px solid var(--primary); +} + +.btn-secondary:hover { + background-color: #f8f9fa; + transform: translateY(-2px); +} + +.video-container { + max-width: 800px; + width: 100%; + margin: 0 auto; + box-shadow: 0 10px 30px rgba(0, 0, 0, 0.15); + border-radius: 8px; + overflow: hidden; + position: relative; + height: 0; + /* Calculate exact padding-bottom based on aspect ratio: (2160/2656) × 100% */ + padding-bottom: calc(2160 / 2656 * 100%); + background-color: rgba(180, 200, 255, 0.3); /* Semi-transparent blue that matches the gradient */ +} + +.video-container video { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + display: block; +} + +.features { + padding: 80px 0; +} + +.section-title { + text-align: center; + margin-bottom: var(--heading-spacing); + font-size: 2.5rem; + color: var(--dark); + font-weight: 700; + line-height: var(--heading-line-height); + letter-spacing: -0.5px; +} + +.feature-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); + gap: 40px; +} + +.feature-card { + background-color: white; + border-radius: 8px; + padding: 30px; + box-shadow: 0 4px 10px rgba(0, 0, 0, 0.05); + transition: transform 0.3s, box-shadow 0.3s, background-color 0.3s; + border-left: 3px solid var(--primary); + display: block; + color: inherit; + text-decoration: none; + cursor: pointer; +} + +.feature-card:hover { + transform: translateY(-5px); + box-shadow: 0 8px 20px rgba(0, 0, 0, 0.12); + background-color: rgba(76, 110, 245, 0.03); +} + +.feature-card p { + font-size: var(--text-base); + line-height: 1.6; + color: rgba(33, 37, 41, 0.9); +} + +.feature-card-header { + display: flex; + align-items: center; + margin-bottom: 20px; + padding: 0 0 12px 0; +} + +.feature-icon { + font-size: 28px; + width: 48px; + height: 48px; + display: flex; + align-items: center; + justify-content: center; + border-radius: 10px; + background: rgba(18, 184, 134, 0.1); + color: var(--secondary); + margin-right: 14px; + flex-shrink: 0; + transition: transform 0.3s, background 0.3s; + transform: scale(1.1); +} + +.feature-card:hover .feature-icon { + transform: scale(1.25); + background: rgba(18, 184, 134, 0.2); + color: var(--secondary); +} + +.feature-card:hover .feature-icon { + transform: scale(1.15); + background: rgba(18, 184, 134, 0.2); + color: var(--secondary); +} + +.feature-title { + font-size: var(--text-lg); + color: var(--dark); + margin: 0; + position: relative; + padding-bottom: var(--space-3); + font-weight: 600; + letter-spacing: -0.01em; + line-height: 1.3; +} + +.feature-title::after { + content: ""; + position: absolute; + bottom: 0; + left: 0; + width: 60px; + height: 3px; + background-color: var(--primary); +} + +.models { + padding: 80px 0; + background-color: #f8f9fb; +} + +code, pre, .code-block { + font-family: 'Fira Code', 'JetBrains Mono', 'SF Mono', Consolas, Monaco, 'Andale Mono', monospace; + font-feature-settings: "liga" 1, "calt" 1; /* Enable ligatures */ + letter-spacing: -0.025em; + font-size: 0.95em; +} + +.code-block { + background-color: var(--code-bg); + border-radius: 8px; + padding: 1.5rem; + color: white; + font-size: 1.1rem; + line-height: 1.5; + margin: 1.5rem 0; + overflow-x: auto; + box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1); + tab-size: 2; +} + +.testimonials { + padding: 80px 0; +} + +.testimonial-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); + gap: 30px; +} + +.testimonial-card { + background: linear-gradient(135deg, rgba(20, 176, 20, 0.05) 0%, rgba(76, 110, 245, 0.15) 100%); + border-radius: 8px; + padding: 30px; + box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1); + transition: transform 0.6s, box-shadow 0.3s; + transform-style: preserve-3d; + perspective: 1000px; + backface-visibility: hidden; + border-left: 3px solid var(--primary); +} + +.testimonial-text { + margin-bottom: 1.5rem; + font-style: italic; + color: #495057; + position: relative; + z-index: 1; + padding: 0 10px; + font-size: 1.1rem; + line-height: var(--body-line-height); + text-wrap: balance; + hanging-punctuation: first; +} + +.testimonial-text::before, +.testimonial-text::after { + font-family: Georgia, serif; + font-size: 1.6em; + line-height: 0.1; + position: relative; +} + +.testimonial-text::before { + content: "\201C\00A0"; /* Opening fancy quote */ + color: var(--primary); + margin-right: 4px; + vertical-align: -0.3em; +} + +.testimonial-text::after { + content: "\201D"; /* Closing fancy quote */ + color: var(--primary); + margin-left: 4px; + vertical-align: -0.3em; +} + +.testimonial-card:hover { + box-shadow: 0 8px 20px rgba(0, 0, 0, 0.15); + background: linear-gradient(135deg, rgba(76, 110, 245, 0.12) 0%, rgba(20, 176, 20, 0.08) 50%, rgba(76, 110, 245, 0.2) 100%); +} + +.testimonial-author { + font-weight: 600; + color: var(--dark); + font-size: 0.95rem; +} + +.testimonial-author a { + color: var(--primary); + text-decoration: none; + transition: color 0.3s; +} + +.testimonial-author a:hover { + text-decoration: underline; +} + +.info-section { + padding: 80px 0; + background-color: #f8f9fb; +} + +.info-columns { + display: flex; + gap: 40px; + max-width: 1000px; + margin: 0 auto; +} + +.info-column { + flex: 1; + background-color: white; + border-radius: 8px; + padding: 30px; + box-shadow: 0 4px 10px rgba(0, 0, 0, 0.05); + transition: transform 0.3s, box-shadow 0.3s; + border-left: 3px solid var(--primary); +} + +.info-column:hover { + transform: translateY(-5px); + box-shadow: 0 8px 20px rgba(0, 0, 0, 0.1); +} + +.info-column-title { + font-size: 1.5rem; + margin-bottom: 1.2rem; + color: var(--dark); + position: relative; + padding-bottom: 12px; + font-weight: 600; + letter-spacing: -0.25px; + line-height: var(--heading-line-height); +} + +.info-column-title::after { + content: ""; + position: absolute; + bottom: 0; + left: 0; + width: 60px; + height: 3px; + background-color: var(--primary); +} + +.info-column-desc { + color: #495057; + margin-bottom: 1.4rem; + font-size: 1.05rem; + line-height: var(--body-line-height); +} + +.info-list { + list-style-type: none; + padding: 0; + margin: 0; +} + +.info-list li { + margin-bottom: 12px; + padding-left: 24px; + position: relative; +} + +.info-list li::before { + content: "→"; + position: absolute; + left: 0; + color: var(--primary); + font-weight: bold; +} + +.info-list a { + color: var(--primary); + text-decoration: none; + transition: color 0.2s, transform 0.2s; + display: inline-block; +} + +.info-list a:hover { + color: var(--primary-dark); + transform: translateX(3px); +} + +footer { + background-color: var(--dark); + color: white; + padding: 40px 0; + text-align: center; +} + +.footer-links { + margin-bottom: 20px; +} + +.footer-links a { + color: white; + text-decoration: none; + margin: 0 10px; + transition: color 0.3s; +} + +.footer-links a:hover { + color: var(--primary); +} + +.copyright { + color: var(--gray); + font-size: 0.9rem; +} + +.cta-container { + text-align: center; + margin: 30px 0; +} + +.cta-text { + font-size: 1.2rem; + font-weight: 500; + margin-bottom: 20px; +} + +.cta-buttons { + display: flex; + gap: 15px; + justify-content: center; +} + +.stats-container { + margin-top: 70px; + display: flex; + justify-content: center; + align-items: center; + gap: 12px; + flex-wrap: wrap; + max-width: 1000px; + margin-left: auto; + margin-right: auto; +} + +.github-badge { + display: inline-flex; + height: 28px; + border-radius: 4px; + overflow: hidden; + font-size: var(--text-xs); + font-weight: 600; + line-height: 1; + text-decoration: none; + transition: transform 0.2s; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12); + font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif; + letter-spacing: 0.02em; + text-transform: uppercase; +} + +.github-badge:hover { + transform: translateY(-2px); + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); +} + +.badge-label { + display: flex; + align-items: center; + padding: 0 10px; + background-color: #555; + color: white; + height: 100%; +} + +.badge-value { + display: flex; + align-items: center; + padding: 0 10px; + background-color: var(--primary); + color: white; + height: 100%; +} + +.badge-stars .badge-value { + background-color: #f1c40f; + color: #333; +} + +.badge-installs .badge-value { + background-color: #2ecc71; +} + +.badge-router .badge-value { + background-color: #9b59b6; +} + +.badge-tokens .badge-value { + background-color: #3498db; +} + +.badge-coded .badge-value { + background-color: #e74c3c; +} + +@media (max-width: 992px) { + :root { + --heading-line-height: 1.25; + --body-line-height: 1.65; + --paragraph-spacing: 1.4rem; + --heading-spacing: 1.6rem; + } + + body { + font-size: 15px; + } + + nav { + padding: 12px 0; + } + + .hero { + padding: 50px 0; + } + + .hero-grid { + grid-template-columns: 1fr; + } + + .hero-content { + text-align: center; + order: 1; + max-width: 100%; + padding-left: 0; + margin-left: auto; + margin-right: auto; + } + + .hero-video { + order: 2; + margin: 0; + } + + .stats-container { + margin-top: 40px; + } + + .hero h1 { + font-size: 2.4rem; + text-align: center; + margin-bottom: var(--heading-spacing); + width: 100%; + } + + .hero p { + font-size: 1.15rem; + margin-left: auto; + margin-right: auto; + max-width: 100%; + text-align: center; + margin-bottom: var(--paragraph-spacing); + } + + .buttons { + flex-direction: row; + flex-wrap: wrap; + align-items: center; + justify-content: center; + gap: 10px; + margin-left: auto; + margin-right: auto; + width: 100%; + } + + .btn { + min-width: 120px; + text-align: center; + } + + .stats-container { + justify-content: center; + } + + .section-title { + font-size: 1.8rem; + } + + .feature-title { + font-size: 1.1rem; + } + + .feature-card { + padding: 20px; + } + + .feature-card-header { + padding: 10px 12px; + margin-bottom: 15px; + } + + .feature-card p { + font-size: 0.9rem; + } + + .feature-icon { + font-size: 1.5rem; + } + + .testimonial-text { + font-size: 0.9rem; + } + + .testimonial-author { + font-size: 0.8rem; + } + + .code-block { + font-size: 0.8rem; + } + + .info-columns { + flex-direction: column; + } + + .info-column { + margin-bottom: 20px; + } + + .nav-links { + display: none; + } + + .btn { + padding: 10px 20px; + font-size: 0.95rem; + } + + .btn-primary { + padding: 12px 24px; + font-size: 1rem; + } + + .github-badge { + font-size: 0.75rem; + } +} + +@media (max-width: 768px) { + :root { + /* Adjust scale for mobile */ + --text-4xl: 2.5rem; + --text-2xl: 1.75rem; + --text-xl: 1.25rem; + } + + body { + line-height: 1.5; + } + + .feature-card p { + font-size: 0.9375rem; + } + + /* Optimize testimonial display on mobile */ + .testimonial-text { + font-size: 1rem; + line-height: 1.6; + text-wrap: pretty; /* Modern browsers will balance text */ + } +} + +@media (max-width: 576px) { + :root { + --heading-line-height: 1.3; + --body-line-height: 1.6; + --paragraph-spacing: 1.2rem; + --heading-spacing: 1.4rem; + } + + body { + font-size: 14px; + } + + .hero { + padding: 30px 0; + } + + .stats-container { + margin-top: 25px; + } + + .buttons { + gap: 8px; + } + + .btn { + min-width: 100px; + padding: 8px 12px; + } + + nav { + padding: 8px 0; + } + + .logo { + font-size: 1.4rem; + } + + .hero h1 { + font-size: 1.8rem; + } + + .section-title { + font-size: 1.6rem; + margin-bottom: 40px; + } + + .feature-title { + font-size: 1rem; + } + + .feature-card { + padding: 15px; + } + + .feature-card-header { + padding: 8px 10px; + margin-bottom: 12px; + } + + .code-block { + font-size: 0.8rem; + padding: 15px; + } + + .info-column-title { + font-size: 1.3rem; + } + + .info-column { + padding: 20px; + } + + .btn { + padding: 8px 16px; + font-size: 0.9rem; + } + + .btn-primary { + padding: 10px 20px; + font-size: 0.95rem; + } + + .logo { + font-size: 1.5rem; + } +} diff --git a/aider/website/assets/icons/brain.svg b/aider/website/assets/icons/brain.svg new file mode 100644 index 000000000..e0d3894cf --- /dev/null +++ b/aider/website/assets/icons/brain.svg @@ -0,0 +1 @@ + diff --git a/aider/website/assets/icons/check-all.svg b/aider/website/assets/icons/check-all.svg new file mode 100644 index 000000000..ec5888f9b --- /dev/null +++ b/aider/website/assets/icons/check-all.svg @@ -0,0 +1 @@ + diff --git a/aider/website/assets/icons/code-tags.svg b/aider/website/assets/icons/code-tags.svg new file mode 100644 index 000000000..18b317173 --- /dev/null +++ b/aider/website/assets/icons/code-tags.svg @@ -0,0 +1 @@ + diff --git a/aider/website/assets/icons/content-copy.svg b/aider/website/assets/icons/content-copy.svg new file mode 100644 index 000000000..ff0c6ab8a --- /dev/null +++ b/aider/website/assets/icons/content-copy.svg @@ -0,0 +1 @@ + diff --git a/aider/website/assets/icons/image-multiple.svg b/aider/website/assets/icons/image-multiple.svg new file mode 100644 index 000000000..dc09bec21 --- /dev/null +++ b/aider/website/assets/icons/image-multiple.svg @@ -0,0 +1 @@ + diff --git a/aider/website/assets/icons/map-outline.svg b/aider/website/assets/icons/map-outline.svg new file mode 100644 index 000000000..2bb9e994d --- /dev/null +++ b/aider/website/assets/icons/map-outline.svg @@ -0,0 +1 @@ + diff --git a/aider/website/assets/icons/microphone.svg b/aider/website/assets/icons/microphone.svg new file mode 100644 index 000000000..97068d53f --- /dev/null +++ b/aider/website/assets/icons/microphone.svg @@ -0,0 +1 @@ + diff --git a/aider/website/assets/icons/monitor.svg b/aider/website/assets/icons/monitor.svg new file mode 100644 index 000000000..39ee355f1 --- /dev/null +++ b/aider/website/assets/icons/monitor.svg @@ -0,0 +1 @@ + diff --git a/aider/website/assets/icons/source-branch.svg b/aider/website/assets/icons/source-branch.svg new file mode 100644 index 000000000..db7da8b2b --- /dev/null +++ b/aider/website/assets/icons/source-branch.svg @@ -0,0 +1 @@ + diff --git a/aider/website/assets/logo.svg b/aider/website/assets/logo.svg new file mode 100644 index 000000000..462a045b7 --- /dev/null +++ b/aider/website/assets/logo.svg @@ -0,0 +1,27 @@ + + + + + + + + + + + aider + \ No newline at end of file diff --git a/aider/website/assets/recordings.jpg b/aider/website/assets/recordings.jpg new file mode 100644 index 000000000..00abd68a2 Binary files /dev/null and b/aider/website/assets/recordings.jpg differ diff --git a/aider/website/assets/sample-analytics.jsonl b/aider/website/assets/sample-analytics.jsonl index 65f548b20..18005a8c6 100644 --- a/aider/website/assets/sample-analytics.jsonl +++ b/aider/website/assets/sample-analytics.jsonl @@ -1,1000 +1,1000 @@ -{"event": "ai-comments execute", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738954592} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738954593} -{"event": "message_send", "properties": {"main_model": "claude-3-5-sonnet-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-sonnet-20241022", "edit_format": "diff", "prompt_tokens": 8713, "completion_tokens": 237, "total_tokens": 8950, "cost": 0.029693999999999998, "total_cost": 0.029693999999999998}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738954602} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738954648} -{"event": "message_send", "properties": {"main_model": "claude-3-5-sonnet-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-sonnet-20241022", "edit_format": "diff", "prompt_tokens": 9027, "completion_tokens": 317, "total_tokens": 9344, "cost": 0.031836, "total_cost": 0.06153}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738954656} -{"event": "command_exit", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738954707} -{"event": "exit", "properties": {"reason": "/exit"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738954707} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738960410} -{"event": "repo", "properties": {"num_files": 437}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738960411} -{"event": "exit", "properties": {"reason": "Showed repo map"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738960432} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738960466} -{"event": "repo", "properties": {"num_files": 437}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738960468} -{"event": "cli session", "properties": {"main_model": "claude-3-5-sonnet-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-sonnet-20241022", "edit_format": "diff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738960468} -{"event": "command_ask", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738960468} -{"event": "command_exit", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738960473} -{"event": "exit", "properties": {"reason": "/exit"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738960473} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738960476} -{"event": "repo", "properties": {"num_files": 437}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738960478} -{"event": "cli session", "properties": {"main_model": "claude-3-5-sonnet-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-sonnet-20241022", "edit_format": "diff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738960478} -{"event": "exit", "properties": {"reason": "Control-C"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738960482} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738960490} -{"event": "repo", "properties": {"num_files": 437}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738960491} -{"event": "cli session", "properties": {"main_model": "claude-3-5-sonnet-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-sonnet-20241022", "edit_format": "diff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738960491} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738960511} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738960547} -{"event": "repo", "properties": {"num_files": 437}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738960549} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738960596} -{"event": "repo", "properties": {"num_files": 437}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738960596} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738960685} -{"event": "repo", "properties": {"num_files": 437}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738960685} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738960887} -{"event": "repo", "properties": {"num_files": 437}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738960888} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738960920} -{"event": "repo", "properties": {"num_files": 437}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738960920} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738960967} -{"event": "repo", "properties": {"num_files": 437}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738960968} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738961005} -{"event": "repo", "properties": {"num_files": 437}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738961005} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738961101} -{"event": "repo", "properties": {"num_files": 437}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738961102} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738961118} -{"event": "repo", "properties": {"num_files": 437}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738961118} -{"event": "exit", "properties": {"reason": "Showed repo map"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738961124} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738961155} -{"event": "repo", "properties": {"num_files": 437}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738961156} -{"event": "exit", "properties": {"reason": "Showed repo map"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738961158} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738961166} -{"event": "repo", "properties": {"num_files": 437}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738961166} -{"event": "exit", "properties": {"reason": "Showed repo map"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738961168} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738961210} -{"event": "repo", "properties": {"num_files": 437}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738961210} -{"event": "exit", "properties": {"reason": "Showed repo map"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738961213} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738961224} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738961227} -{"event": "repo", "properties": {"num_files": 437}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738961227} -{"event": "exit", "properties": {"reason": "Showed repo map"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738961231} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738961813} -{"event": "repo", "properties": {"num_files": 437}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738961814} -{"event": "exit", "properties": {"reason": "Showed repo map"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738961839} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738962415} -{"event": "repo", "properties": {"num_files": 437}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738962415} -{"event": "exit", "properties": {"reason": "Showed repo map"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738962419} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738962498} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738962517} -{"event": "repo", "properties": {"num_files": 437}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738962517} -{"event": "cli session", "properties": {"main_model": "claude-3-5-sonnet-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-sonnet-20241022", "edit_format": "diff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738962517} -{"event": "command_ask", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738962520} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738962668} -{"event": "message_send", "properties": {"main_model": "claude-3-5-sonnet-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-sonnet-20241022", "edit_format": "ask", "prompt_tokens": 5322, "completion_tokens": 625, "total_tokens": 5947, "cost": 0.025341000000000002, "total_cost": 0.025341000000000002}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738962684} -{"event": "command_read-only", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738962702} -{"event": "command_clear", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738962703} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738962724} -{"event": "message_send", "properties": {"main_model": "claude-3-5-sonnet-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-sonnet-20241022", "edit_format": "ask", "prompt_tokens": 10498, "completion_tokens": 693, "total_tokens": 11191, "cost": 0.041889, "total_cost": 0.06723000000000001}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738962739} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738962785} -{"event": "message_send", "properties": {"main_model": "claude-3-5-sonnet-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-sonnet-20241022", "edit_format": "ask", "prompt_tokens": 11219, "completion_tokens": 786, "total_tokens": 12005, "cost": 0.045447, "total_cost": 0.11267700000000001}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738962801} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738962816} -{"event": "message_send", "properties": {"main_model": "claude-3-5-sonnet-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-sonnet-20241022", "edit_format": "ask", "prompt_tokens": 12028, "completion_tokens": 754, "total_tokens": 12782, "cost": 0.047394, "total_cost": 0.16007100000000002}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738962831} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738962854} -{"event": "message_send", "properties": {"main_model": "claude-3-5-sonnet-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-sonnet-20241022", "edit_format": "ask", "prompt_tokens": 12812, "completion_tokens": 276, "total_tokens": 13088, "cost": 0.042575999999999996, "total_cost": 0.20264700000000002}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738962862} -{"event": "command_exit", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738967224} -{"event": "exit", "properties": {"reason": "/exit"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738967224} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738967366} -{"event": "repo", "properties": {"num_files": 437}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738967367} -{"event": "exit", "properties": {"reason": "Showed repo map"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738967369} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738967477} -{"event": "repo", "properties": {"num_files": 437}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738967480} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738970006} -{"event": "repo", "properties": {"num_files": 437}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738970006} -{"event": "exit", "properties": {"reason": "Showed repo map"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738970033} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738970041} -{"event": "repo", "properties": {"num_files": 437}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738970041} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738970339} -{"event": "repo", "properties": {"num_files": 438}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738970339} -{"event": "exit", "properties": {"reason": "Showed repo map"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738970343} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738970375} -{"event": "repo", "properties": {"num_files": 438}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738970375} -{"event": "exit", "properties": {"reason": "Showed repo map"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738970379} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738970596} -{"event": "repo", "properties": {"num_files": 438}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738970596} -{"event": "cli session", "properties": {"main_model": "claude-3-5-sonnet-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-sonnet-20241022", "edit_format": "diff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738970596} -{"event": "ai-comments file-add", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738970616} -{"event": "ai-comments execute", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738970616} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738970616} -{"event": "message_send", "properties": {"main_model": "claude-3-5-sonnet-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-sonnet-20241022", "edit_format": "diff", "prompt_tokens": 11518, "completion_tokens": 355, "total_tokens": 11873, "cost": 0.039879, "total_cost": 0.039879}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738970626} -{"event": "command_exit", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738970647} -{"event": "exit", "properties": {"reason": "/exit"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738970647} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738970948} -{"event": "repo", "properties": {"num_files": 458}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738970948} -{"event": "exit", "properties": {"reason": "Showed repo map"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738970952} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738970973} -{"event": "repo", "properties": {"num_files": 438}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738970974} -{"event": "exit", "properties": {"reason": "Showed repo map"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738970977} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738971291} -{"event": "repo", "properties": {"num_files": 438}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738971291} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738973759} -{"event": "repo", "properties": {"num_files": 438}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738973759} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738973823} -{"event": "repo", "properties": {"num_files": 438}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738973823} -{"event": "exit", "properties": {"reason": "Showed repo map"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738973827} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738973866} -{"event": "repo", "properties": {"num_files": 438}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738973866} -{"event": "exit", "properties": {"reason": "Showed repo map"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738973874} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738973946} -{"event": "repo", "properties": {"num_files": 438}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738973946} -{"event": "cli session", "properties": {"main_model": "claude-3-5-sonnet-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-sonnet-20241022", "edit_format": "diff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738973946} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738973951} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738973959} -{"event": "repo", "properties": {"num_files": 438}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738973959} -{"event": "cli session", "properties": {"main_model": "claude-3-5-sonnet-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-sonnet-20241022", "edit_format": "diff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738973959} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738973961} -{"event": "message_send", "properties": {"main_model": "claude-3-5-sonnet-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-sonnet-20241022", "edit_format": "diff", "prompt_tokens": 9866, "completion_tokens": 90, "total_tokens": 9956, "cost": 0.030948, "total_cost": 0.030948}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738973966} -{"event": "command_exit", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738974027} -{"event": "exit", "properties": {"reason": "/exit"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738974027} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738974082} -{"event": "repo", "properties": {"num_files": 437}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738974082} -{"event": "cli session", "properties": {"main_model": "claude-3-5-sonnet-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-sonnet-20241022", "edit_format": "diff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738974082} -{"event": "exit", "properties": {"reason": "Control-C"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738974092} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738974096} -{"event": "no-repo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738974096} -{"event": "cli session", "properties": {"main_model": "claude-3-5-sonnet-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-sonnet-20241022", "edit_format": "diff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738974096} -{"event": "exit", "properties": {"reason": "Control-C"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738974098} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738974103} -{"event": "no-repo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738974103} -{"event": "cli session", "properties": {"main_model": "claude-3-5-haiku-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-haiku-20241022", "edit_format": "diff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738974103} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738974106} -{"event": "message_send", "properties": {"main_model": "claude-3-5-haiku-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-haiku-20241022", "edit_format": "diff", "prompt_tokens": 2344, "completion_tokens": 104, "total_tokens": 2448, "cost": 0.002864, "total_cost": 0.002864}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738974109} -{"event": "exit", "properties": {"reason": "Control-C"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738974143} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738974145} -{"event": "no-repo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738974145} -{"event": "cli session", "properties": {"main_model": "claude-3-5-haiku-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-haiku-20241022", "edit_format": "diff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738974145} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738974151} -{"event": "message_send", "properties": {"main_model": "claude-3-5-haiku-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-haiku-20241022", "edit_format": "diff", "prompt_tokens": 2344, "completion_tokens": 70, "total_tokens": 2414, "cost": 0.0026939999999999998, "total_cost": 0.0026939999999999998}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738974154} -{"event": "exit", "properties": {"reason": "Control-C"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738974186} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738974187} -{"event": "no-repo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738974187} -{"event": "cli session", "properties": {"main_model": "claude-3-5-haiku-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-haiku-20241022", "edit_format": "diff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738974187} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738974190} -{"event": "message_send", "properties": {"main_model": "claude-3-5-haiku-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-haiku-20241022", "edit_format": "diff", "prompt_tokens": 2344, "completion_tokens": 70, "total_tokens": 2414, "cost": 0.0026939999999999998, "total_cost": 0.0026939999999999998}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738974192} -{"event": "exit", "properties": {"reason": "Completed main CLI coder.run"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738974223} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738974228} -{"event": "no-repo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738974228} -{"event": "cli session", "properties": {"main_model": "claude-3-5-haiku-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-haiku-20241022", "edit_format": "diff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738974228} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738974230} -{"event": "message_send", "properties": {"main_model": "claude-3-5-haiku-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-haiku-20241022", "edit_format": "diff", "prompt_tokens": 2344, "completion_tokens": 104, "total_tokens": 2448, "cost": 0.002864, "total_cost": 0.002864}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738974234} -{"event": "command_ask", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738974254} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738974254} -{"event": "message_send", "properties": {"main_model": "claude-3-5-haiku-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-haiku-20241022", "edit_format": "ask", "prompt_tokens": 192, "completion_tokens": 55, "total_tokens": 247, "cost": 0.000467, "total_cost": 0.0033309999999999998}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738974256} -{"event": "command_ask", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738974265} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738974265} -{"event": "message_send", "properties": {"main_model": "claude-3-5-haiku-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-haiku-20241022", "edit_format": "ask", "prompt_tokens": 256, "completion_tokens": 79, "total_tokens": 335, "cost": 0.000651, "total_cost": 0.003981999999999999}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738974267} -{"event": "command_exit", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738974275} -{"event": "exit", "properties": {"reason": "/exit"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738974275} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738974295} -{"event": "repo", "properties": {"num_files": 437}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738974295} -{"event": "cli session", "properties": {"main_model": "claude-3-5-sonnet-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-sonnet-20241022", "edit_format": "diff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738974295} -{"event": "command_add", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738974299} -{"event": "command_ask", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738974308} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738974308} -{"event": "message_send", "properties": {"main_model": "claude-3-5-sonnet-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-sonnet-20241022", "edit_format": "ask", "prompt_tokens": 19429, "completion_tokens": 697, "total_tokens": 20126, "cost": 0.068742, "total_cost": 0.068742}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738974325} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738974336} -{"event": "message_send", "properties": {"main_model": "claude-3-5-sonnet-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-sonnet-20241022", "edit_format": "diff", "prompt_tokens": 22424, "completion_tokens": 1107, "total_tokens": 23531, "cost": 0.08387700000000001, "total_cost": 0.152619}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738974358} -{"event": "command_diff", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738974373} -{"event": "command_exit", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738974382} -{"event": "exit", "properties": {"reason": "/exit"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738974382} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738974393} -{"event": "no-repo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738974394} -{"event": "cli session", "properties": {"main_model": "claude-3-5-haiku-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-haiku-20241022", "edit_format": "diff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738974394} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738974396} -{"event": "message_send", "properties": {"main_model": "claude-3-5-haiku-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-haiku-20241022", "edit_format": "diff", "prompt_tokens": 2344, "completion_tokens": 70, "total_tokens": 2414, "cost": 0.0026939999999999998, "total_cost": 0.0026939999999999998}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738974399} -{"event": "command_ask", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738974406} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738974406} -{"event": "message_send", "properties": {"main_model": "claude-3-5-haiku-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-haiku-20241022", "edit_format": "ask", "prompt_tokens": 158, "completion_tokens": 56, "total_tokens": 214, "cost": 0.000438, "total_cost": 0.003132}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738974408} -{"event": "command_exit", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738974418} -{"event": "exit", "properties": {"reason": "/exit"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738974418} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738974597} -{"event": "no-repo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738974597} -{"event": "cli session", "properties": {"main_model": "claude-3-5-haiku-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-haiku-20241022", "edit_format": "diff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738974597} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738974600} -{"event": "message_send", "properties": {"main_model": "claude-3-5-haiku-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-haiku-20241022", "edit_format": "diff", "prompt_tokens": 2344, "completion_tokens": 70, "total_tokens": 2414, "cost": 0.0026939999999999998, "total_cost": 0.0026939999999999998}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738974603} -{"event": "command_ask", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738974605} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738974605} -{"event": "message_send", "properties": {"main_model": "claude-3-5-haiku-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-haiku-20241022", "edit_format": "ask", "prompt_tokens": 158, "completion_tokens": 50, "total_tokens": 208, "cost": 0.000408, "total_cost": 0.0031019999999999997}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738974606} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738974626} -{"event": "message_send", "properties": {"main_model": "claude-3-5-haiku-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-haiku-20241022", "edit_format": "diff", "prompt_tokens": 2482, "completion_tokens": 75, "total_tokens": 2557, "cost": 0.0028569999999999997, "total_cost": 0.005958999999999999}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738974629} -{"event": "command_code", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738974636} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738974636} -{"event": "message_send", "properties": {"main_model": "claude-3-5-haiku-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-haiku-20241022", "edit_format": "diff", "prompt_tokens": 2567, "completion_tokens": 136, "total_tokens": 2703, "cost": 0.003247, "total_cost": 0.009205999999999999}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738974640} -{"event": "command_exit", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738974657} -{"event": "exit", "properties": {"reason": "/exit"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738974657} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738974956} -{"event": "no-repo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738974956} -{"event": "cli session", "properties": {"main_model": "claude-3-5-haiku-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-haiku-20241022", "edit_format": "diff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738974956} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738974960} -{"event": "message_send", "properties": {"main_model": "claude-3-5-haiku-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-haiku-20241022", "edit_format": "diff", "prompt_tokens": 2344, "completion_tokens": 85, "total_tokens": 2429, "cost": 0.0027689999999999998, "total_cost": 0.0027689999999999998}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738974963} -{"event": "exit", "properties": {"reason": "Control-C"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738974981} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738974983} -{"event": "no-repo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738974983} -{"event": "cli session", "properties": {"main_model": "claude-3-5-haiku-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-haiku-20241022", "edit_format": "diff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738974983} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738974985} -{"event": "message_send", "properties": {"main_model": "claude-3-5-haiku-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-haiku-20241022", "edit_format": "diff", "prompt_tokens": 2344, "completion_tokens": 70, "total_tokens": 2414, "cost": 0.0026939999999999998, "total_cost": 0.0026939999999999998}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738974987} -{"event": "exit", "properties": {"reason": "Control-C"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738975017} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738975018} -{"event": "no-repo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738975018} -{"event": "cli session", "properties": {"main_model": "claude-3-5-haiku-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-haiku-20241022", "edit_format": "diff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738975018} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738975019} -{"event": "message_send", "properties": {"main_model": "claude-3-5-haiku-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-haiku-20241022", "edit_format": "diff", "prompt_tokens": 2344, "completion_tokens": 70, "total_tokens": 2414, "cost": 0.0026939999999999998, "total_cost": 0.0026939999999999998}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738975023} -{"event": "exit", "properties": {"reason": "Control-C"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738975047} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738975049} -{"event": "no-repo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738975049} -{"event": "cli session", "properties": {"main_model": "claude-3-5-haiku-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-haiku-20241022", "edit_format": "diff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738975049} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738975051} -{"event": "message_send", "properties": {"main_model": "claude-3-5-haiku-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-haiku-20241022", "edit_format": "diff", "prompt_tokens": 2344, "completion_tokens": 70, "total_tokens": 2414, "cost": 0.0026939999999999998, "total_cost": 0.0026939999999999998}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738975053} -{"event": "command_ask", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738975062} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738975062} -{"event": "message_send", "properties": {"main_model": "claude-3-5-haiku-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-haiku-20241022", "edit_format": "ask", "prompt_tokens": 158, "completion_tokens": 50, "total_tokens": 208, "cost": 0.000408, "total_cost": 0.0031019999999999997}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738975064} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738975071} -{"event": "message_send", "properties": {"main_model": "claude-3-5-haiku-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-haiku-20241022", "edit_format": "diff", "prompt_tokens": 2482, "completion_tokens": 84, "total_tokens": 2566, "cost": 0.002902, "total_cost": 0.006004}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738975074} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738975081} -{"event": "message_send", "properties": {"main_model": "claude-3-5-haiku-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-haiku-20241022", "edit_format": "diff", "prompt_tokens": 2576, "completion_tokens": 118, "total_tokens": 2694, "cost": 0.0031659999999999995, "total_cost": 0.00917}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738975086} -{"event": "command_ask", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738975104} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738975104} -{"event": "message_send", "properties": {"main_model": "claude-3-5-haiku-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-haiku-20241022", "edit_format": "ask", "prompt_tokens": 439, "completion_tokens": 111, "total_tokens": 550, "cost": 0.000994, "total_cost": 0.010164}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738975108} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738975115} -{"event": "message_send", "properties": {"main_model": "claude-3-5-haiku-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-haiku-20241022", "edit_format": "diff", "prompt_tokens": 2824, "completion_tokens": 101, "total_tokens": 2925, "cost": 0.0033289999999999995, "total_cost": 0.013492999999999998}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738975117} -{"event": "exit", "properties": {"reason": "Control-C"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738975137} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738975169} -{"event": "repo", "properties": {"num_files": 437}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738975169} -{"event": "exit", "properties": {"reason": "Completed lint/test/commit"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738975175} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738975364} -{"event": "gui session", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738975372} -{"event": "exit", "properties": {"reason": "GUI session ended"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738975372} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738980561} -{"event": "repo", "properties": {"num_files": 437}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738980562} -{"event": "cli session", "properties": {"main_model": "claude-3-5-sonnet-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-sonnet-20241022", "edit_format": "diff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738980562} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738980564} -{"event": "command_exit", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738980627} -{"event": "exit", "properties": {"reason": "/exit"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1738980627} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739143173} -{"event": "repo", "properties": {"num_files": 437}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739143174} -{"event": "cli session", "properties": {"main_model": "claude-3-5-haiku-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-haiku-20241022", "edit_format": "diff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739143174} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739143175} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739143205} -{"event": "repo", "properties": {"num_files": 437}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739143206} -{"event": "cli session", "properties": {"main_model": "claude-3-5-haiku-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-haiku-20241022", "edit_format": "diff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739143206} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739143206} -{"event": "message_send", "properties": {"main_model": "claude-3-5-haiku-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-haiku-20241022", "edit_format": "diff", "prompt_tokens": 9802, "completion_tokens": 31, "total_tokens": 9833, "cost": 0.009957, "total_cost": 0.009957}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739143213} -{"event": "exit", "properties": {"reason": "Control-C"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739143214} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739143243} -{"event": "repo", "properties": {"num_files": 437}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739143244} -{"event": "cli session", "properties": {"main_model": "claude-3-5-haiku-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-haiku-20241022", "edit_format": "diff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739143244} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739143245} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739143267} -{"event": "repo", "properties": {"num_files": 437}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739143267} -{"event": "cli session", "properties": {"main_model": "claude-3-5-haiku-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-haiku-20241022", "edit_format": "diff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739143267} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739143269} -{"event": "message_send", "properties": {"main_model": "claude-3-5-haiku-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-haiku-20241022", "edit_format": "diff", "prompt_tokens": 9935, "completion_tokens": 40, "total_tokens": 9975, "cost": 0.010135, "total_cost": 0.010135}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739143273} -{"event": "exit", "properties": {"reason": "Control-C"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739143295} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739143302} -{"event": "repo", "properties": {"num_files": 437}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739143302} -{"event": "cli session", "properties": {"main_model": "claude-3-5-haiku-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-haiku-20241022", "edit_format": "diff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739143302} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739143303} -{"event": "message_send", "properties": {"main_model": "claude-3-5-haiku-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-haiku-20241022", "edit_format": "diff", "prompt_tokens": 9935, "completion_tokens": 30, "total_tokens": 9965, "cost": 0.010085, "total_cost": 0.010085}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739143308} -{"event": "command_exit", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739143326} -{"event": "exit", "properties": {"reason": "/exit"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739143326} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739143334} -{"event": "repo", "properties": {"num_files": 437}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739143334} -{"event": "exit", "properties": {"reason": "Completed lint/test/commit"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739143340} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739143470} -{"event": "gui session", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739143470} -{"event": "exit", "properties": {"reason": "GUI session ended"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739143470} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739143483} -{"event": "no-repo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739143484} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739143484} -{"event": "message_send", "properties": {"main_model": "claude-3-5-sonnet-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-sonnet-20241022", "edit_format": "diff", "prompt_tokens": 7990, "completion_tokens": 177, "total_tokens": 8167, "cost": 0.026625000000000003, "total_cost": 0.026625000000000003}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739143492} -{"event": "exit", "properties": {"reason": "Completed --message"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739143492} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739143648} -{"event": "gui session", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739143648} -{"event": "exit", "properties": {"reason": "GUI session ended"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739143648} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739150197} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739150219} -{"event": "exit", "properties": {"reason": "Install TSLP completed"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739150231} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739150237} -{"event": "repo", "properties": {"num_files": 438}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739150238} -{"event": "cli session", "properties": {"main_model": "claude-3-5-sonnet-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-sonnet-20241022", "edit_format": "diff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739150238} -{"event": "command_map", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739150243} -{"event": "exit", "properties": {"reason": "Control-C"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739150252} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739150256} -{"event": "repo", "properties": {"num_files": 438}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739150258} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739150316} -{"event": "repo", "properties": {"num_files": 438}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739150316} -{"event": "cli session", "properties": {"main_model": "claude-3-5-sonnet-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-sonnet-20241022", "edit_format": "diff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739150316} -{"event": "exit", "properties": {"reason": "Control-C"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739150318} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739150322} -{"event": "repo", "properties": {"num_files": 438}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739150322} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739150333} -{"event": "repo", "properties": {"num_files": 438}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739150334} -{"event": "exit", "properties": {"reason": "Showed repo map"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739150340} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739150352} -{"event": "repo", "properties": {"num_files": 438}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739150352} -{"event": "exit", "properties": {"reason": "Completed lint/test/commit"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739150359} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739216211} -{"event": "repo", "properties": {"num_files": 437}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739216212} -{"event": "cli session", "properties": {"main_model": "claude-3-5-sonnet-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-sonnet-20241022", "edit_format": "diff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739216212} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739216233} -{"event": "message_send", "properties": {"main_model": "claude-3-5-sonnet-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-sonnet-20241022", "edit_format": "diff", "prompt_tokens": 11003, "completion_tokens": 765, "total_tokens": 11768, "cost": 0.044484, "total_cost": 0.044484}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739216248} -{"event": "command_diff", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739216258} -{"event": "command_exit", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739216362} -{"event": "exit", "properties": {"reason": "/exit"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739216362} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739216747} -{"event": "repo", "properties": {"num_files": 437}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739216747} -{"event": "cli session", "properties": {"main_model": "claude-3-5-sonnet-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-sonnet-20241022", "edit_format": "diff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739216747} -{"event": "command_editor", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739216752} -{"event": "command_ask", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739216773} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739216773} -{"event": "message_send", "properties": {"main_model": "claude-3-5-sonnet-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-sonnet-20241022", "edit_format": "ask", "prompt_tokens": 13452, "completion_tokens": 217, "total_tokens": 13669, "cost": 0.043611000000000004, "total_cost": 0.043611000000000004}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739216781} -{"event": "command_exit", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739216875} -{"event": "exit", "properties": {"reason": "/exit"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739216875} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739224786} -{"event": "repo", "properties": {"num_files": 438}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739224786} -{"event": "cli session", "properties": {"main_model": "claude-3-5-sonnet-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-sonnet-20241022", "edit_format": "diff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739224786} -{"event": "command_add", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739224789} -{"event": "command_map", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739224794} -{"event": "command_editor", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739224849} -{"event": "command_exit", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739224980} -{"event": "exit", "properties": {"reason": "/exit"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739224980} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739225123} -{"event": "repo", "properties": {"num_files": 438}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739225123} -{"event": "cli session", "properties": {"main_model": "claude-3-5-sonnet-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-sonnet-20241022", "edit_format": "diff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739225123} -{"event": "command_read-only", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739225148} -{"event": "command_ask", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739225187} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739225187} -{"event": "message_send", "properties": {"main_model": "claude-3-5-sonnet-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-sonnet-20241022", "edit_format": "ask", "prompt_tokens": 6627, "completion_tokens": 213, "total_tokens": 6840, "cost": 0.023076, "total_cost": 0.023076}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739225194} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739225195} -{"event": "message_send", "properties": {"main_model": "claude-3-5-sonnet-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-sonnet-20241022", "edit_format": "ask", "prompt_tokens": 7291, "completion_tokens": 130, "total_tokens": 7421, "cost": 0.023823, "total_cost": 0.046898999999999996}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739225199} -{"event": "command_ask", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739225211} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739225224} -{"event": "message_send", "properties": {"main_model": "claude-3-5-sonnet-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-sonnet-20241022", "edit_format": "ask", "prompt_tokens": 7450, "completion_tokens": 195, "total_tokens": 7645, "cost": 0.025275000000000002, "total_cost": 0.072174}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739225230} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739225253} -{"event": "message_send", "properties": {"main_model": "claude-3-5-sonnet-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-sonnet-20241022", "edit_format": "ask", "prompt_tokens": 7662, "completion_tokens": 179, "total_tokens": 7841, "cost": 0.025671, "total_cost": 0.097845}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739225258} -{"event": "command_exit", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739225261} -{"event": "exit", "properties": {"reason": "/exit"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739225261} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739225711} -{"event": "repo", "properties": {"num_files": 438}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739225711} -{"event": "exit", "properties": {"reason": "Showed repo map"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739225716} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739226193} -{"event": "repo", "properties": {"num_files": 438}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739226194} -{"event": "exit", "properties": {"reason": "Showed repo map"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739226216} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739226226} -{"event": "exit", "properties": {"reason": "Install TSLP completed"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739226233} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739226242} -{"event": "repo", "properties": {"num_files": 438}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739226242} -{"event": "exit", "properties": {"reason": "Showed repo map"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739226250} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739226316} -{"event": "repo", "properties": {"num_files": 438}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739226316} -{"event": "exit", "properties": {"reason": "Showed repo map"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739226319} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739226335} -{"event": "repo", "properties": {"num_files": 438}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739226335} -{"event": "exit", "properties": {"reason": "Completed lint/test/commit"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739226342} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739226578} -{"event": "gui session", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739226585} -{"event": "exit", "properties": {"reason": "GUI session ended"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739226585} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739378013} -{"event": "repo", "properties": {"num_files": 1}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739378013} -{"event": "cli session", "properties": {"main_model": "claude-3-5-sonnet-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-sonnet-20241022", "edit_format": "diff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739378013} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739378014} -{"event": "command_model", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739378039} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739378040} -{"event": "message_send", "properties": {"main_model": "fireworks_ai/accounts/fireworks/models/deepseek-v3", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "fireworks_ai/accounts/fireworks/models/deepseek-v3", "edit_format": "diff", "prompt_tokens": 3530, "completion_tokens": 1191, "total_tokens": 4721, "cost": 0.0042489, "total_cost": 0.0042489}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739378106} -{"event": "command_exit", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739378197} -{"event": "exit", "properties": {"reason": "/exit"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739378197} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739471875} -{"event": "repo", "properties": {"num_files": 198}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739471876} -{"event": "cli session", "properties": {"main_model": "claude-3-5-sonnet-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-sonnet-20241022", "edit_format": "diff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739471876} -{"event": "ai-comments file-add", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739471878} -{"event": "ai-comments execute", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739471878} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739471881} -{"event": "message_send", "properties": {"main_model": "claude-3-5-sonnet-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-sonnet-20241022", "edit_format": "diff", "prompt_tokens": 7533, "completion_tokens": 373, "total_tokens": 7906, "cost": 0.028194, "total_cost": 0.028194}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739471894} -{"event": "command_reset", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739472931} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739472937} -{"event": "message_send", "properties": {"main_model": "claude-3-5-sonnet-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-sonnet-20241022", "edit_format": "diff", "prompt_tokens": 7154, "completion_tokens": 69, "total_tokens": 7223, "cost": 0.022497000000000003, "total_cost": 0.050691}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739472941} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739472943} -{"event": "message_send", "properties": {"main_model": "claude-3-5-sonnet-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-sonnet-20241022", "edit_format": "diff", "prompt_tokens": 9020, "completion_tokens": 1078, "total_tokens": 10098, "cost": 0.043230000000000005, "total_cost": 0.093921}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739472967} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739477536} -{"event": "repo", "properties": {"num_files": 438}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739477536} -{"event": "cli session", "properties": {"main_model": "claude-3-5-sonnet-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-sonnet-20241022", "edit_format": "diff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739477536} -{"event": "ai-comments file-add", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739477537} -{"event": "ai-comments execute", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739477537} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739477537} -{"event": "message_send", "properties": {"main_model": "claude-3-5-sonnet-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-sonnet-20241022", "edit_format": "diff", "prompt_tokens": 14361, "completion_tokens": 138, "total_tokens": 14499, "cost": 0.045153000000000006, "total_cost": 0.045153000000000006}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739477547} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739477547} -{"event": "message_send", "properties": {"main_model": "claude-3-5-sonnet-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-sonnet-20241022", "edit_format": "diff", "prompt_tokens": 14652, "completion_tokens": 126, "total_tokens": 14778, "cost": 0.045846000000000005, "total_cost": 0.09099900000000001}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739477551} -{"event": "command_exit", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739477600} -{"event": "exit", "properties": {"reason": "/exit"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739477600} -{"event": "command_exit", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739490961} -{"event": "exit", "properties": {"reason": "/exit"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739490961} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739649634} -{"event": "repo", "properties": {"num_files": 438}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739649635} -{"event": "exit", "properties": {"reason": "Completed lint/test/commit"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739649639} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739999636} -{"event": "gui session", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739999637} -{"event": "exit", "properties": {"reason": "GUI session ended"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1739999637} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740000393} -{"event": "repo", "properties": {"num_files": 438}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740000394} -{"event": "cli session", "properties": {"main_model": "claude-3-5-sonnet-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-sonnet-20241022", "edit_format": "diff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740000394} -{"event": "command_ask", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740000394} -{"event": "command_add", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740000399} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740000405} -{"event": "command_exit", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740000440} -{"event": "exit", "properties": {"reason": "/exit"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740000440} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740000458} -{"event": "repo", "properties": {"num_files": 438}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740000458} -{"event": "cli session", "properties": {"main_model": "o3-mini", "weak_model": "gpt-4o-mini", "editor_model": "gpt-4o", "edit_format": "diff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740000458} -{"event": "command_ask", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740000460} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740000461} -{"event": "message_send", "properties": {"main_model": "o3-mini", "weak_model": "gpt-4o-mini", "editor_model": "gpt-4o", "edit_format": "ask", "prompt_tokens": 17019, "completion_tokens": 298, "total_tokens": 17317, "cost": 0.0200321, "total_cost": 0.0200321}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740000480} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740001053} -{"event": "message_send", "properties": {"main_model": "o3-mini", "weak_model": "gpt-4o-mini", "editor_model": "gpt-4o", "edit_format": "ask", "prompt_tokens": 17351, "completion_tokens": 460, "total_tokens": 17811, "cost": 0.021110100000000003, "total_cost": 0.041142200000000004}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740001070} -{"event": "command_exit", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740001545} -{"event": "exit", "properties": {"reason": "/exit"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740001545} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740001907} -{"event": "repo", "properties": {"num_files": 438}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740001907} -{"event": "exit", "properties": {"reason": "Completed lint/test/commit"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740001911} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740002539} -{"event": "repo", "properties": {"num_files": 438}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740002539} -{"event": "cli session", "properties": {"main_model": "openrouter/openai/o3-mini", "weak_model": "openrouter/openai/gpt-4o-mini", "editor_model": "openrouter/openai/gpt-4o", "edit_format": "diff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740002539} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740002541} -{"event": "message_send", "properties": {"main_model": "openrouter/openai/o3-mini", "weak_model": "openrouter/openai/gpt-4o-mini", "editor_model": "openrouter/openai/gpt-4o", "edit_format": "diff", "prompt_tokens": 10068, "completion_tokens": 39, "total_tokens": 10107, "cost": 0.0112464, "total_cost": 0.0112464}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740002578} -{"event": "command_exit", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740002610} -{"event": "exit", "properties": {"reason": "/exit"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740002610} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740003012} -{"event": "repo", "properties": {"num_files": 438}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740003013} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740003013} -{"event": "message_send", "properties": {"main_model": "fireworks_ai/accounts/fireworks/models/deepseek-v3", "weak_model": "fireworks_ai/accounts/fireworks/models/deepseek-v3", "editor_model": "fireworks_ai/accounts/fireworks/models/deepseek-v3", "edit_format": "diff", "prompt_tokens": 15075, "completion_tokens": 42, "total_tokens": 15117, "cost": 0.013605299999999999, "total_cost": 0.013605299999999999}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740003019} -{"event": "exit", "properties": {"reason": "Completed --message"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740003019} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740003058} -{"event": "repo", "properties": {"num_files": 438}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740003058} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740003058} -{"event": "message_send", "properties": {"main_model": "fireworks_ai/accounts/fireworks/models/deepseek-v3", "weak_model": "fireworks_ai/accounts/fireworks/models/deepseek-v3", "editor_model": "fireworks_ai/accounts/fireworks/models/deepseek-v3", "edit_format": "diff", "prompt_tokens": 14985, "completion_tokens": 81, "total_tokens": 15066, "cost": 0.013559400000000001, "total_cost": 0.013559400000000001}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740003064} -{"event": "exit", "properties": {"reason": "Completed --message-file"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740003064} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740003081} -{"event": "repo", "properties": {"num_files": 438}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740003081} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740003081} -{"event": "message_send", "properties": {"main_model": "fireworks_ai/accounts/fireworks/models/deepseek-v3", "weak_model": "fireworks_ai/accounts/fireworks/models/deepseek-v3", "editor_model": "fireworks_ai/accounts/fireworks/models/deepseek-v3", "edit_format": "diff", "prompt_tokens": 14988, "completion_tokens": 46, "total_tokens": 15034, "cost": 0.0135306, "total_cost": 0.0135306}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740003087} -{"event": "exit", "properties": {"reason": "Completed --message-file"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740003088} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740003094} -{"event": "repo", "properties": {"num_files": 438}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740003095} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740003095} -{"event": "message_send", "properties": {"main_model": "fireworks_ai/accounts/fireworks/models/deepseek-v3", "weak_model": "fireworks_ai/accounts/fireworks/models/deepseek-v3", "editor_model": "fireworks_ai/accounts/fireworks/models/deepseek-v3", "edit_format": "diff", "prompt_tokens": 15073, "completion_tokens": 54, "total_tokens": 15127, "cost": 0.0136143, "total_cost": 0.0136143}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740003100} -{"event": "exit", "properties": {"reason": "Completed --message-file"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740003100} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740003113} -{"event": "repo", "properties": {"num_files": 438}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740003114} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740003114} -{"event": "message_send", "properties": {"main_model": "fireworks_ai/accounts/fireworks/models/deepseek-v3", "weak_model": "fireworks_ai/accounts/fireworks/models/deepseek-v3", "editor_model": "fireworks_ai/accounts/fireworks/models/deepseek-v3", "edit_format": "diff", "prompt_tokens": 15076, "completion_tokens": 68, "total_tokens": 15144, "cost": 0.0136296, "total_cost": 0.0136296}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740003120} -{"event": "exit", "properties": {"reason": "Completed --message-file"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740003120} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740003130} -{"event": "repo", "properties": {"num_files": 438}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740003130} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740003130} -{"event": "message_send", "properties": {"main_model": "fireworks_ai/accounts/fireworks/models/deepseek-v3", "weak_model": "fireworks_ai/accounts/fireworks/models/deepseek-v3", "editor_model": "fireworks_ai/accounts/fireworks/models/deepseek-v3", "edit_format": "ask", "prompt_tokens": 12842, "completion_tokens": 53, "total_tokens": 12895, "cost": 0.0116055, "total_cost": 0.0116055}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740003136} -{"event": "exit", "properties": {"reason": "Completed --message-file"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740003136} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740003148} -{"event": "repo", "properties": {"num_files": 438}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740003148} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740003148} -{"event": "message_send", "properties": {"main_model": "fireworks_ai/accounts/fireworks/models/deepseek-v3", "weak_model": "fireworks_ai/accounts/fireworks/models/deepseek-v3", "editor_model": "fireworks_ai/accounts/fireworks/models/deepseek-v3", "edit_format": "ask", "prompt_tokens": 12842, "completion_tokens": 53, "total_tokens": 12895, "cost": 0.0116055, "total_cost": 0.0116055}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740003155} -{"event": "exit", "properties": {"reason": "Completed --message-file"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740003155} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740003191} -{"event": "repo", "properties": {"num_files": 438}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740003191} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740003191} -{"event": "message_send", "properties": {"main_model": "gpt-4o", "weak_model": "gpt-4o-mini", "editor_model": "gpt-4o", "edit_format": "ask", "prompt_tokens": 12553, "completion_tokens": 42, "total_tokens": 12595, "cost": 0.0318025, "total_cost": 0.0318025}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740003196} -{"event": "exit", "properties": {"reason": "Completed --message-file"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740003196} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740003834} -{"event": "repo", "properties": {"num_files": 438}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740003837} -{"event": "cli session", "properties": {"main_model": "fake-provider/my-fake-model", "weak_model": "fake-provider/my-fake-model", "editor_model": "fake-provider/my-fake-model", "edit_format": "whole"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740003837} -{"event": "command_exit", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740003838} -{"event": "exit", "properties": {"reason": "/exit"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740003838} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740003844} -{"event": "exit", "properties": {"reason": "Listed models"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740003846} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740003850} -{"event": "exit", "properties": {"reason": "Listed models"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740003851} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740003858} -{"event": "exit", "properties": {"reason": "Listed models"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740003860} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740004240} -{"event": "exit", "properties": {"reason": "Listed models"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740004242} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740329360} -{"event": "no-repo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740329360} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740329360} -{"event": "message_send", "properties": {"main_model": "claude-3-5-sonnet-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-sonnet-20241022", "edit_format": "diff", "prompt_tokens": 19044, "completion_tokens": 210, "total_tokens": 19254, "cost": 0.060282, "total_cost": 0.060282}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740329371} -{"event": "exit", "properties": {"reason": "Completed --message"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740329371} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740329626} -{"event": "gui session", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740329627} -{"event": "exit", "properties": {"reason": "GUI session ended"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740329627} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740417595} -{"event": "repo", "properties": {"num_files": 438}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740417599} -{"event": "cli session", "properties": {"main_model": "claude-3-5-sonnet-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-sonnet-20241022", "edit_format": "diff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740417599} -{"event": "command_editor", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740417610} -{"event": "command_exit", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740417621} -{"event": "exit", "properties": {"reason": "/exit"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740417621} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740417624} -{"event": "repo", "properties": {"num_files": 438}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740417624} -{"event": "cli session", "properties": {"main_model": "claude-3-5-sonnet-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-sonnet-20241022", "edit_format": "diff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740417624} -{"event": "command_editor", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740417626} -{"event": "exit", "properties": {"reason": "Control-C"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740417632} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740417652} -{"event": "repo", "properties": {"num_files": 438}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740417653} -{"event": "cli session", "properties": {"main_model": "claude-3-5-sonnet-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-sonnet-20241022", "edit_format": "diff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740417653} -{"event": "command_editor", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740417655} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740417669} -{"event": "repo", "properties": {"num_files": 438}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740417671} -{"event": "cli session", "properties": {"main_model": "claude-3-5-sonnet-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-sonnet-20241022", "edit_format": "diff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740417671} -{"event": "command_editor", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740417672} -{"event": "ai-comments file-add", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740417700} -{"event": "ai-comments execute", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740417700} -{"event": "command_ask", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740417700} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740417700} -{"event": "message_send", "properties": {"main_model": "claude-3-5-sonnet-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-sonnet-20241022", "edit_format": "ask", "prompt_tokens": 5288, "completion_tokens": 208, "total_tokens": 5496, "cost": 0.018984, "total_cost": 0.018984}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740417706} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740417740} -{"event": "message_send", "properties": {"main_model": "claude-3-5-sonnet-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-sonnet-20241022", "edit_format": "diff", "prompt_tokens": 7792, "completion_tokens": 211, "total_tokens": 8003, "cost": 0.026541000000000002, "total_cost": 0.045525}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740417746} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740417756} -{"event": "repo", "properties": {"num_files": 438}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740417758} -{"event": "cli session", "properties": {"main_model": "claude-3-5-sonnet-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-sonnet-20241022", "edit_format": "diff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740417758} -{"event": "command_editor", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740417759} -{"event": "command_exit", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740417765} -{"event": "exit", "properties": {"reason": "/exit"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740417765} -{"event": "command_add", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740417774} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740417792} -{"event": "message_send", "properties": {"main_model": "claude-3-5-sonnet-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-sonnet-20241022", "edit_format": "diff", "prompt_tokens": 8997, "completion_tokens": 257, "total_tokens": 9254, "cost": 0.030846000000000002, "total_cost": 0.07637100000000001}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740417799} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740417845} -{"event": "message_send", "properties": {"main_model": "claude-3-5-sonnet-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-sonnet-20241022", "edit_format": "diff", "prompt_tokens": 9424, "completion_tokens": 496, "total_tokens": 9920, "cost": 0.035712, "total_cost": 0.11208300000000002}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740417864} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740417902} -{"event": "message_send", "properties": {"main_model": "claude-3-5-sonnet-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-sonnet-20241022", "edit_format": "diff", "prompt_tokens": 10166, "completion_tokens": 138, "total_tokens": 10304, "cost": 0.032568, "total_cost": 0.14465100000000003}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740417907} -{"event": "command_undo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740417922} -{"event": "command_read-only", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740417935} -{"event": "command_read-only", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740417941} -{"event": "command_clear", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740417943} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740417945} -{"event": "message_send", "properties": {"main_model": "claude-3-5-sonnet-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-sonnet-20241022", "edit_format": "diff", "prompt_tokens": 8565, "completion_tokens": 333, "total_tokens": 8898, "cost": 0.03069, "total_cost": 0.17534100000000002}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740417954} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740418004} -{"event": "message_send", "properties": {"main_model": "claude-3-5-sonnet-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-sonnet-20241022", "edit_format": "diff", "prompt_tokens": 8990, "completion_tokens": 237, "total_tokens": 9227, "cost": 0.030525, "total_cost": 0.20586600000000002}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740418010} -{"event": "exit", "properties": {"reason": "Control-C"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740418041} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740418097} -{"event": "repo", "properties": {"num_files": 438}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740418097} -{"event": "exit", "properties": {"reason": "Completed lint/test/commit"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740418098} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740418105} -{"event": "repo", "properties": {"num_files": 438}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740418106} -{"event": "exit", "properties": {"reason": "Completed lint/test/commit"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740418111} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740418207} -{"event": "gui session", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740418207} -{"event": "exit", "properties": {"reason": "GUI session ended"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740418207} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740419116} -{"event": "repo", "properties": {"num_files": 438}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740419116} -{"event": "cli session", "properties": {"main_model": "claude-3-5-sonnet-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-sonnet-20241022", "edit_format": "diff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740419116} -{"event": "command_read-only", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740419121} -{"event": "command_add", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740419125} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740419164} -{"event": "message_send", "properties": {"main_model": "claude-3-5-sonnet-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-sonnet-20241022", "edit_format": "diff", "prompt_tokens": 8309, "completion_tokens": 375, "total_tokens": 8684, "cost": 0.030552000000000003, "total_cost": 0.030552000000000003}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740419174} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740419183} -{"event": "message_send", "properties": {"main_model": "claude-3-5-sonnet-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-sonnet-20241022", "edit_format": "diff", "prompt_tokens": 9228, "completion_tokens": 168, "total_tokens": 9396, "cost": 0.030204, "total_cost": 0.060756000000000004}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740419187} -{"event": "command_ask", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740419211} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740419211} -{"event": "message_send", "properties": {"main_model": "claude-3-5-sonnet-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-sonnet-20241022", "edit_format": "ask", "prompt_tokens": 7199, "completion_tokens": 628, "total_tokens": 7827, "cost": 0.031017000000000003, "total_cost": 0.09177300000000001}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740419222} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740419228} -{"event": "message_send", "properties": {"main_model": "claude-3-5-sonnet-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-sonnet-20241022", "edit_format": "diff", "prompt_tokens": 10167, "completion_tokens": 536, "total_tokens": 10703, "cost": 0.038541, "total_cost": 0.130314}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740419238} -{"event": "exit", "properties": {"reason": "Control-C"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740419248} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740419307} -{"event": "gui session", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740419307} -{"event": "exit", "properties": {"reason": "GUI session ended"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740419307} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740420683} -{"event": "repo", "properties": {"num_files": 438}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740420684} -{"event": "cli session", "properties": {"main_model": "claude-3-5-sonnet-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-sonnet-20241022", "edit_format": "diff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740420684} -{"event": "command_add", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740420688} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740420695} -{"event": "message_send", "properties": {"main_model": "claude-3-5-sonnet-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-sonnet-20241022", "edit_format": "diff", "prompt_tokens": 8452, "completion_tokens": 332, "total_tokens": 8784, "cost": 0.030336000000000002, "total_cost": 0.030336000000000002}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740420703} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740420705} -{"event": "message_send", "properties": {"main_model": "claude-3-5-sonnet-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-sonnet-20241022", "edit_format": "diff", "prompt_tokens": 9060, "completion_tokens": 183, "total_tokens": 9243, "cost": 0.029925, "total_cost": 0.060261}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740420716} -{"event": "command_clear", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740420897} -{"event": "command_ask", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740420899} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740420899} -{"event": "message_send", "properties": {"main_model": "claude-3-5-sonnet-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-sonnet-20241022", "edit_format": "ask", "prompt_tokens": 5302, "completion_tokens": 437, "total_tokens": 5739, "cost": 0.022461000000000002, "total_cost": 0.082722}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740420909} -{"event": "command_ask", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740420932} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740420932} -{"event": "message_send", "properties": {"main_model": "claude-3-5-sonnet-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-sonnet-20241022", "edit_format": "ask", "prompt_tokens": 5634, "completion_tokens": 390, "total_tokens": 6024, "cost": 0.022752, "total_cost": 0.10547400000000001}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740420940} -{"event": "command_add", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740420963} -{"event": "command_add", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740420968} -{"event": "command_clear", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740420969} -{"event": "command_ask", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740420975} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740420975} -{"event": "message_send", "properties": {"main_model": "claude-3-5-sonnet-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-sonnet-20241022", "edit_format": "ask", "prompt_tokens": 6241, "completion_tokens": 419, "total_tokens": 6660, "cost": 0.025008, "total_cost": 0.13048200000000001}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740420984} -{"event": "command_model", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740421014} -{"event": "command_ask", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740421020} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740421020} -{"event": "message_send", "properties": {"main_model": "o3-mini", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "gpt-4o", "edit_format": "ask", "prompt_tokens": 6872, "completion_tokens": 447, "total_tokens": 7319, "cost": 0.009526, "total_cost": 0.14000800000000002}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740421037} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740421079} -{"event": "message_send", "properties": {"main_model": "o3-mini", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "gpt-4o", "edit_format": "diff", "prompt_tokens": 9645, "completion_tokens": 100, "total_tokens": 9745, "cost": 0.0110495, "total_cost": 0.1510575}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740421097} -{"event": "command_clear", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740421189} -{"event": "command_model", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740421192} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740421209} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740421219} -{"event": "command_read-only", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740421230} -{"event": "command_clear", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740421232} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740421235} -{"event": "message_send", "properties": {"main_model": "claude-3-5-sonnet-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-sonnet-20241022", "edit_format": "diff", "prompt_tokens": 8509, "completion_tokens": 419, "total_tokens": 8928, "cost": 0.031812, "total_cost": 0.18286950000000002}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740421244} -{"event": "command_diff", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740421249} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740421297} -{"event": "repo", "properties": {"num_files": 438}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740421297} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740421305} -{"event": "message_send", "properties": {"main_model": "claude-3-5-sonnet-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-sonnet-20241022", "edit_format": "diff", "prompt_tokens": 7667, "completion_tokens": 118, "total_tokens": 7785, "cost": 0.024771, "total_cost": 0.024771}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740421309} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740421320} -{"event": "message_send", "properties": {"main_model": "claude-3-5-sonnet-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-sonnet-20241022", "edit_format": "diff", "prompt_tokens": 7812, "completion_tokens": 93, "total_tokens": 7905, "cost": 0.024831000000000002, "total_cost": 0.04960200000000001}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740421323} -{"event": "exit", "properties": {"reason": "Completed lint/test/commit"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740421327} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740422343} -{"event": "message_send", "properties": {"main_model": "claude-3-5-sonnet-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-sonnet-20241022", "edit_format": "diff", "prompt_tokens": 8968, "completion_tokens": 528, "total_tokens": 9496, "cost": 0.034824, "total_cost": 0.2176935}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740422353} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740422387} -{"event": "repo", "properties": {"num_files": 438}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740422387} -{"event": "cli session", "properties": {"main_model": "claude-3-5-sonnet-20241022", "weak_model": "claude-3-5-haiku-20241022", "editor_model": "claude-3-5-sonnet-20241022", "edit_format": "diff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740422387} -{"event": "command_editor", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740422389} -{"event": "command_exit", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740422395} -{"event": "exit", "properties": {"reason": "/exit"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740422395} -{"event": "command_exit", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740426335} -{"event": "exit", "properties": {"reason": "/exit"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740426335} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740426344} -{"event": "repo", "properties": {"num_files": 438}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740426344} -{"event": "exit", "properties": {"reason": "Completed lint/test/commit"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740426348} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740426518} -{"event": "model warning", "properties": {"main_model": "None", "weak_model": "None", "editor_model": "None"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740426519} -{"event": "repo", "properties": {"num_files": 438}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740426524} -{"event": "cli session", "properties": {"main_model": "None", "weak_model": "None", "editor_model": "None", "edit_format": "whole"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740426524} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740426524} -{"event": "exit", "properties": {"reason": "Control-C"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740426529} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740426539} -{"event": "model warning", "properties": {"main_model": "anthropic/REDACTED", "weak_model": "anthropic/REDACTED", "editor_model": "anthropic/REDACTED"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740426541} -{"event": "repo", "properties": {"num_files": 438}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740426553} -{"event": "cli session", "properties": {"main_model": "anthropic/REDACTED", "weak_model": "anthropic/REDACTED", "editor_model": "anthropic/REDACTED", "edit_format": "whole"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740426553} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740426554} -{"event": "message_send", "properties": {"main_model": "anthropic/REDACTED", "weak_model": "anthropic/REDACTED", "editor_model": "anthropic/REDACTED", "edit_format": "whole", "prompt_tokens": 1897, "completion_tokens": 102, "total_tokens": 1999, "cost": 0, "total_cost": 0.0}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740426558} -{"event": "command_exit", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740426665} -{"event": "exit", "properties": {"reason": "/exit"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740426665} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740426669} -{"event": "repo", "properties": {"num_files": 438}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740426670} -{"event": "cli session", "properties": {"main_model": "claude-3-7-sonnet-20250219", "weak_model": "claude-3-7-sonnet-20250219", "editor_model": "claude-3-7-sonnet-20250219", "edit_format": "whole"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740426671} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740426671} -{"event": "exit", "properties": {"reason": "Completed main CLI coder.run"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740426675} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740426693} -{"event": "repo", "properties": {"num_files": 438}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740426693} -{"event": "cli session", "properties": {"main_model": "anthropic/claude-3-7-sonnet-20250219", "weak_model": "anthropic/claude-3-7-sonnet-20250219", "editor_model": "anthropic/claude-3-7-sonnet-20250219", "edit_format": "whole"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740426693} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740426694} -{"event": "message_send", "properties": {"main_model": "anthropic/claude-3-7-sonnet-20250219", "weak_model": "anthropic/claude-3-7-sonnet-20250219", "editor_model": "anthropic/claude-3-7-sonnet-20250219", "edit_format": "whole", "prompt_tokens": 8253, "completion_tokens": 86, "total_tokens": 8339, "cost": 0.026049, "total_cost": 0.026049}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740426699} -{"event": "command_exit", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740426703} -{"event": "exit", "properties": {"reason": "/exit"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740426703} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740426758} -{"event": "repo", "properties": {"num_files": 438}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740426758} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740426758} -{"event": "message_send", "properties": {"main_model": "anthropic/claude-3-7-sonnet-20250219", "weak_model": "anthropic/claude-3-5-haiku-20241022", "editor_model": "anthropic/claude-3-7-sonnet-20250219", "edit_format": "diff", "prompt_tokens": 10016, "completion_tokens": 49, "total_tokens": 10065, "cost": 0.030783, "total_cost": 0.030783}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740426764} -{"event": "exit", "properties": {"reason": "Completed --message"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740426764} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740427723} -{"event": "repo", "properties": {"num_files": 438}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740427723} -{"event": "cli session", "properties": {"main_model": "anthropic/claude-3-7-sonnet-20250219", "weak_model": "anthropic/claude-3-5-haiku-20241022", "editor_model": "anthropic/claude-3-7-sonnet-20250219", "edit_format": "diff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740427723} -{"event": "command_add", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740427729} -{"event": "command_add", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740427745} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740427746} -{"event": "message_send", "properties": {"main_model": "anthropic/claude-3-7-sonnet-20250219", "weak_model": "anthropic/claude-3-5-haiku-20241022", "editor_model": "anthropic/claude-3-7-sonnet-20250219", "edit_format": "diff", "prompt_tokens": 8921, "completion_tokens": 732, "total_tokens": 9653, "cost": 0.037743, "total_cost": 0.037743}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740427763} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740427766} -{"event": "message_send", "properties": {"main_model": "anthropic/claude-3-7-sonnet-20250219", "weak_model": "anthropic/claude-3-5-haiku-20241022", "editor_model": "anthropic/claude-3-7-sonnet-20250219", "edit_format": "diff", "prompt_tokens": 10000, "completion_tokens": 248, "total_tokens": 10248, "cost": 0.03372, "total_cost": 0.071463}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740427771} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740427871} -{"event": "repo", "properties": {"num_files": 438}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740427871} -{"event": "cli session", "properties": {"main_model": "anthropic/claude-3-7-sonnet-20250219", "weak_model": "anthropic/claude-3-5-haiku-20241022", "editor_model": "anthropic/claude-3-7-sonnet-20250219", "edit_format": "diff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740427871} -{"event": "command_exit", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740427895} -{"event": "exit", "properties": {"reason": "/exit"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740427895} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740427899} -{"event": "repo", "properties": {"num_files": 438}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740427899} -{"event": "cli session", "properties": {"main_model": "anthropic/claude-3-7-sonnet-20250219", "weak_model": "anthropic/claude-3-5-haiku-20241022", "editor_model": "anthropic/claude-3-7-sonnet-20250219", "edit_format": "diff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740427899} -{"event": "command_exit", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740427901} -{"event": "exit", "properties": {"reason": "/exit"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740427901} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740427904} -{"event": "repo", "properties": {"num_files": 438}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740427904} -{"event": "exit", "properties": {"reason": "Completed lint/test/commit"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740427909} -{"event": "command_exit", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740428002} -{"event": "exit", "properties": {"reason": "/exit"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740428002} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740429083} -{"event": "repo", "properties": {"num_files": 438}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740429085} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740429100} -{"event": "gui session", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740429100} -{"event": "exit", "properties": {"reason": "GUI session ended"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740429100} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740429150} -{"event": "repo", "properties": {"num_files": 438}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740429150} -{"event": "cli session", "properties": {"main_model": "anthropic/claude-3-7-sonnet-20250219", "weak_model": "anthropic/claude-3-5-haiku-20241022", "editor_model": "anthropic/claude-3-7-sonnet-20250219", "edit_format": "diff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740429150} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740429152} -{"event": "message_send", "properties": {"main_model": "anthropic/claude-3-7-sonnet-20250219", "weak_model": "anthropic/claude-3-5-haiku-20241022", "editor_model": "anthropic/claude-3-7-sonnet-20250219", "edit_format": "diff", "prompt_tokens": 10816, "completion_tokens": 135, "total_tokens": 10951, "cost": 0.034473, "total_cost": 0.034473}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740429158} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740429159} -{"event": "message_send", "properties": {"main_model": "anthropic/claude-3-7-sonnet-20250219", "weak_model": "anthropic/claude-3-5-haiku-20241022", "editor_model": "anthropic/claude-3-7-sonnet-20250219", "edit_format": "diff", "prompt_tokens": 10675, "completion_tokens": 216, "total_tokens": 10891, "cost": 0.035265, "total_cost": 0.069738}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740429165} -{"event": "exit", "properties": {"reason": "Control-C"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740429176} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740429274} -{"event": "gui session", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740429275} -{"event": "exit", "properties": {"reason": "GUI session ended"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740429275} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740429994} -{"event": "repo", "properties": {"num_files": 438}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740429995} -{"event": "cli session", "properties": {"main_model": "anthropic/claude-3-7-sonnet-20250219", "weak_model": "anthropic/claude-3-5-haiku-20241022", "editor_model": "anthropic/claude-3-7-sonnet-20250219", "edit_format": "diff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740429995} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740429996} -{"event": "command_exit", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740430050} -{"event": "exit", "properties": {"reason": "/exit"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740430050} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740430054} -{"event": "repo", "properties": {"num_files": 438}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740430055} -{"event": "cli session", "properties": {"main_model": "anthropic/claude-3-7-sonnet-20250219", "weak_model": "anthropic/claude-3-5-haiku-20241022", "editor_model": "anthropic/claude-3-7-sonnet-20250219", "edit_format": "diff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740430055} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740430055} -{"event": "command_exit", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740430187} -{"event": "exit", "properties": {"reason": "/exit"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740430187} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740430195} -{"event": "repo", "properties": {"num_files": 438}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740430195} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740430195} -{"event": "exit", "properties": {"reason": "Completed --message"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740430198} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740430205} -{"event": "repo", "properties": {"num_files": 438}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740430205} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740430206} -{"event": "exit", "properties": {"reason": "Completed --message"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740430207} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740430242} -{"event": "repo", "properties": {"num_files": 438}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740430242} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740430242} -{"event": "exit", "properties": {"reason": "Completed --message"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740430245} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740430285} -{"event": "repo", "properties": {"num_files": 438}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740430286} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740430286} -{"event": "exit", "properties": {"reason": "Completed --message"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740430287} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740430297} -{"event": "repo", "properties": {"num_files": 438}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740430297} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740430297} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740430315} -{"event": "repo", "properties": {"num_files": 438}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740430315} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740430315} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740430334} -{"event": "repo", "properties": {"num_files": 438}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740430334} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740430334} -{"event": "message_send", "properties": {"main_model": "anthropic/claude-3-7-sonnet-20250219", "weak_model": "anthropic/claude-3-5-haiku-20241022", "editor_model": "anthropic/claude-3-7-sonnet-20250219", "edit_format": "diff", "prompt_tokens": 2356, "completion_tokens": 100, "total_tokens": 2456, "cost": 0.008568000000000001, "total_cost": 0.008568000000000001}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740430342} -{"event": "exit", "properties": {"reason": "Completed --message"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740430342} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740433345} -{"event": "repo", "properties": {"num_files": 438}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740433346} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740433346} -{"event": "exit", "properties": {"reason": "Completed --message"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740433348} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740433357} -{"event": "repo", "properties": {"num_files": 438}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740433357} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740433357} -{"event": "exit", "properties": {"reason": "Completed --message"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740433359} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740433370} -{"event": "repo", "properties": {"num_files": 438}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740433371} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740433371} -{"event": "exit", "properties": {"reason": "Completed --message"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740433373} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740433507} -{"event": "repo", "properties": {"num_files": 438}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740433508} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740433508} -{"event": "message_send", "properties": {"main_model": "anthropic/claude-3-7-sonnet-20250219", "weak_model": "anthropic/claude-3-5-haiku-20241022", "editor_model": "anthropic/claude-3-7-sonnet-20250219", "edit_format": "diff", "prompt_tokens": 2356, "completion_tokens": 100, "total_tokens": 2456, "cost": 0.008568000000000001, "total_cost": 0.008568000000000001}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740433514} -{"event": "exit", "properties": {"reason": "Completed --message"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740433514} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740433518} -{"event": "repo", "properties": {"num_files": 438}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740433519} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740433519} -{"event": "exit", "properties": {"reason": "Completed --message"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740433521} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740433536} -{"event": "repo", "properties": {"num_files": 438}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740433536} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740433536} -{"event": "exit", "properties": {"reason": "Completed --message"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740433538} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740439991} -{"event": "no-repo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740439991} -{"event": "cli session", "properties": {"main_model": "anthropic/claude-3-7-sonnet-20250219", "weak_model": "anthropic/claude-3-5-haiku-20241022", "editor_model": "anthropic/claude-3-7-sonnet-20250219", "edit_format": "diff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740439991} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740439992} -{"event": "message_send", "properties": {"main_model": "anthropic/claude-3-7-sonnet-20250219", "weak_model": "anthropic/claude-3-5-haiku-20241022", "editor_model": "anthropic/claude-3-7-sonnet-20250219", "edit_format": "diff", "prompt_tokens": 2410, "completion_tokens": 109, "total_tokens": 2519, "cost": 0.008865, "total_cost": 0.008865}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740439999} -{"event": "command_exit", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740440013} -{"event": "exit", "properties": {"reason": "/exit"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740440013} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740440017} -{"event": "model warning", "properties": {"main_model": "openrouter/REDACTED", "weak_model": "anthropic/claude-3-5-haiku-20241022", "editor_model": "anthropic/claude-3-7-sonnet-20250219"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740440019} -{"event": "repo", "properties": {"num_files": 438}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740440024} -{"event": "cli session", "properties": {"main_model": "openrouter/REDACTED", "weak_model": "anthropic/claude-3-5-haiku-20241022", "editor_model": "anthropic/claude-3-7-sonnet-20250219", "edit_format": "diff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740440024} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740440025} -{"event": "message_send", "properties": {"main_model": "openrouter/REDACTED", "weak_model": "anthropic/claude-3-5-haiku-20241022", "editor_model": "anthropic/claude-3-7-sonnet-20250219", "edit_format": "diff", "prompt_tokens": 3676, "completion_tokens": 43, "total_tokens": 3719, "cost": 0, "total_cost": 0.0}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740440029} -{"event": "exit", "properties": {"reason": "Control-C"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740440039} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740440049} -{"event": "model warning", "properties": {"main_model": "openrouter/REDACTED", "weak_model": "openrouter/REDACTED", "editor_model": "openrouter/REDACTED"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740440051} -{"event": "exit", "properties": {"reason": "Keyboard interrupt during model warnings"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740440069} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740440071} -{"event": "model warning", "properties": {"main_model": "openrouter/REDACTED", "weak_model": "openrouter/REDACTED", "editor_model": "openrouter/REDACTED"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740440073} -{"event": "exit", "properties": {"reason": "Keyboard interrupt during model warnings"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740440074} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740440083} -{"event": "repo", "properties": {"num_files": 438}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740440083} -{"event": "cli session", "properties": {"main_model": "openrouter/anthropic/claude-3.7-sonnet", "weak_model": "anthropic/claude-3-5-haiku-20241022", "editor_model": "anthropic/claude-3-7-sonnet-20250219", "edit_format": "diff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740440083} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740440084} -{"event": "message_send", "properties": {"main_model": "openrouter/anthropic/claude-3.7-sonnet", "weak_model": "anthropic/claude-3-5-haiku-20241022", "editor_model": "anthropic/claude-3-7-sonnet-20250219", "edit_format": "diff", "prompt_tokens": 10048, "completion_tokens": 84, "total_tokens": 10132, "cost": 0.031404, "total_cost": 0.031404}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740440089} -{"event": "command_exit", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740440096} -{"event": "exit", "properties": {"reason": "/exit"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740440096} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740440164} -{"event": "repo", "properties": {"num_files": 438}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740440164} -{"event": "cli session", "properties": {"main_model": "openrouter/anthropic/claude-3.7-sonnet", "weak_model": "openrouter/anthropic/claude-3-5-haiku", "editor_model": "openrouter/anthropic/claude-3.7-sonnet", "edit_format": "diff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740440164} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740440169} -{"event": "message_send", "properties": {"main_model": "openrouter/anthropic/claude-3.7-sonnet", "weak_model": "openrouter/anthropic/claude-3-5-haiku", "editor_model": "openrouter/anthropic/claude-3.7-sonnet", "edit_format": "diff", "prompt_tokens": 10016, "completion_tokens": 65, "total_tokens": 10081, "cost": 0.031023000000000002, "total_cost": 0.031023000000000002}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740440174} -{"event": "command_exit", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740440175} -{"event": "exit", "properties": {"reason": "/exit"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740440175} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740440182} -{"event": "repo", "properties": {"num_files": 438}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740440182} -{"event": "exit", "properties": {"reason": "Completed lint/test/commit"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740440185} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740440440} -{"event": "gui session", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740440440} -{"event": "exit", "properties": {"reason": "GUI session ended"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740440440} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740499589} -{"event": "no-repo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740499589} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740499589} -{"event": "message_send_exception", "properties": {"exception": "'name'"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740499591} -{"event": "exit", "properties": {"reason": "Completed --message"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740499591} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740499766} -{"event": "no-repo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740499766} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740499766} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740499944} -{"event": "no-repo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740499944} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740499944} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740499988} -{"event": "no-repo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740499988} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740499988} -{"event": "message_send", "properties": {"main_model": "anthropic/claude-3-7-sonnet-20250219", "weak_model": "anthropic/claude-3-5-haiku-20241022", "editor_model": "anthropic/claude-3-7-sonnet-20250219", "edit_format": "str-replace", "prompt_tokens": 6880, "completion_tokens": 127, "total_tokens": 7007, "cost": 0.025074000000000003, "total_cost": 0.025074000000000003}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740499993} -{"event": "exit", "properties": {"reason": "Completed --message"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740499993} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740502502} -{"event": "repo", "properties": {"num_files": 438}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740502503} -{"event": "cli session", "properties": {"main_model": "anthropic/claude-3-7-sonnet-20250219", "weak_model": "anthropic/claude-3-5-haiku-20241022", "editor_model": "anthropic/claude-3-7-sonnet-20250219", "edit_format": "diff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740502503} -{"event": "command_ask", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740502504} -{"event": "command_add", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740502511} -{"event": "exit", "properties": {"reason": "Control-C"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740502514} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740502525} -{"event": "repo", "properties": {"num_files": 438}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740502526} -{"event": "cli session", "properties": {"main_model": "anthropic/claude-3-7-sonnet-20250219", "weak_model": "anthropic/claude-3-5-haiku-20241022", "editor_model": "anthropic/claude-3-7-sonnet-20250219", "edit_format": "diff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740502526} -{"event": "command_exit", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740502530} -{"event": "exit", "properties": {"reason": "/exit"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740502530} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740502537} -{"event": "repo", "properties": {"num_files": 438}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740502537} -{"event": "cli session", "properties": {"main_model": "anthropic/claude-3-7-sonnet-20250219", "weak_model": "anthropic/claude-3-5-haiku-20241022", "editor_model": "anthropic/claude-3-7-sonnet-20250219", "edit_format": "diff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740502537} -{"event": "command_ask", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740502538} -{"event": "command_editor", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740502544} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740502576} -{"event": "message_send", "properties": {"main_model": "anthropic/claude-3-7-sonnet-20250219", "weak_model": "anthropic/claude-3-5-haiku-20241022", "editor_model": "anthropic/claude-3-7-sonnet-20250219", "edit_format": "ask", "prompt_tokens": 25710, "completion_tokens": 335, "total_tokens": 26045, "cost": 0.082155, "total_cost": 0.082155}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740502613} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740514526} -{"event": "repo", "properties": {"num_files": 438}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740514527} -{"event": "cli session", "properties": {"main_model": "anthropic/claude-3-7-sonnet-20250219", "weak_model": "anthropic/claude-3-5-haiku-20241022", "editor_model": "anthropic/claude-3-7-sonnet-20250219", "edit_format": "diff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740514527} -{"event": "command_ask", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740514596} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740514596} -{"event": "message_send", "properties": {"main_model": "anthropic/claude-3-7-sonnet-20250219", "weak_model": "anthropic/claude-3-5-haiku-20241022", "editor_model": "anthropic/claude-3-7-sonnet-20250219", "edit_format": "ask", "prompt_tokens": 4433, "completion_tokens": 145, "total_tokens": 4578, "cost": 0.015474, "total_cost": 0.015474}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740514608} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740514668} -{"event": "message_send", "properties": {"main_model": "anthropic/claude-3-7-sonnet-20250219", "weak_model": "anthropic/claude-3-5-haiku-20241022", "editor_model": "anthropic/claude-3-7-sonnet-20250219", "edit_format": "diff", "prompt_tokens": 6958, "completion_tokens": 175, "total_tokens": 7133, "cost": 0.023499, "total_cost": 0.038973}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740514680} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740514718} -{"event": "message_send", "properties": {"main_model": "anthropic/claude-3-7-sonnet-20250219", "weak_model": "anthropic/claude-3-5-haiku-20241022", "editor_model": "anthropic/claude-3-7-sonnet-20250219", "edit_format": "diff", "prompt_tokens": 7161, "completion_tokens": 829, "total_tokens": 7990, "cost": 0.033918000000000004, "total_cost": 0.07289100000000001}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740514736} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740514765} -{"event": "message_send", "properties": {"main_model": "anthropic/claude-3-7-sonnet-20250219", "weak_model": "anthropic/claude-3-5-haiku-20241022", "editor_model": "anthropic/claude-3-7-sonnet-20250219", "edit_format": "diff", "prompt_tokens": 8171, "completion_tokens": 826, "total_tokens": 8997, "cost": 0.036903, "total_cost": 0.109794}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740514783} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740514818} -{"event": "message_send", "properties": {"main_model": "anthropic/claude-3-7-sonnet-20250219", "weak_model": "anthropic/claude-3-5-haiku-20241022", "editor_model": "anthropic/claude-3-7-sonnet-20250219", "edit_format": "diff", "prompt_tokens": 9032, "completion_tokens": 872, "total_tokens": 9904, "cost": 0.040176, "total_cost": 0.14997}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740514835} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740514879} -{"event": "message_send", "properties": {"main_model": "anthropic/claude-3-7-sonnet-20250219", "weak_model": "anthropic/claude-3-5-haiku-20241022", "editor_model": "anthropic/claude-3-7-sonnet-20250219", "edit_format": "diff", "prompt_tokens": 9963, "completion_tokens": 124, "total_tokens": 10087, "cost": 0.031749, "total_cost": 0.181719}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740514885} -{"event": "command_exit", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740515106} -{"event": "exit", "properties": {"reason": "/exit"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740515106} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740515245} -{"event": "model warning", "properties": {"main_model": "None", "weak_model": "None", "editor_model": "None"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740515247} -{"event": "exit", "properties": {"reason": "Keyboard interrupt during model warnings"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740515251} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740515265} -{"event": "no-repo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740515267} -{"event": "cli session", "properties": {"main_model": "None", "weak_model": "None", "editor_model": "None", "edit_format": "whole"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740515267} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740515268} -{"event": "exit", "properties": {"reason": "Control-C"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740515270} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740515277} -{"event": "no-repo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740515279} -{"event": "cli session", "properties": {"main_model": "anthropic/REDACTED", "weak_model": "anthropic/REDACTED", "editor_model": "anthropic/REDACTED", "edit_format": "whole"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740515279} -{"event": "command_exit", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740515289} -{"event": "exit", "properties": {"reason": "/exit"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740515289} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740515292} -{"event": "no-repo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740515294} -{"event": "cli session", "properties": {"main_model": "anthropic/REDACTED", "weak_model": "anthropic/REDACTED", "editor_model": "anthropic/REDACTED", "edit_format": "whole"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740515294} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740515297} -{"event": "command_exit", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740515366} -{"event": "exit", "properties": {"reason": "/exit"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740515366} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740515369} -{"event": "no-repo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740515371} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740515371} -{"event": "exit", "properties": {"reason": "Completed --message"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740515372} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740515413} -{"event": "no-repo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740515416} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740515416} -{"event": "exit", "properties": {"reason": "Completed --message"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740515416} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740515447} -{"event": "no-repo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740515447} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740515447} -{"event": "exit", "properties": {"reason": "Completed --message"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740515458} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740515474} -{"event": "no-repo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740515476} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740515476} -{"event": "exit", "properties": {"reason": "Completed --message"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740515477} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740515490} -{"event": "no-repo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740515492} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740515492} -{"event": "exit", "properties": {"reason": "Completed --message"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740515493} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740517756} -{"event": "repo", "properties": {"num_files": 438}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740517757} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740517757} -{"event": "message_send", "properties": {"main_model": "anthropic/claude-3-7-sonnet-20250219", "weak_model": "anthropic/claude-3-5-haiku-20241022", "editor_model": "anthropic/claude-3-7-sonnet-20250219", "edit_format": "diff", "prompt_tokens": 10019, "completion_tokens": 51, "total_tokens": 10070, "cost": 0.030822000000000002, "total_cost": 0.030822000000000002}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740517763} -{"event": "exit", "properties": {"reason": "Completed --message"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740517763} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740533136} -{"event": "repo", "properties": {"num_files": 201}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740533136} -{"event": "cli session", "properties": {"main_model": "anthropic/claude-3-7-sonnet-20250219", "weak_model": "anthropic/claude-3-5-haiku-20241022", "editor_model": "anthropic/claude-3-7-sonnet-20250219", "edit_format": "diff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740533136} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740533152} -{"event": "message_send", "properties": {"main_model": "anthropic/claude-3-7-sonnet-20250219", "weak_model": "anthropic/claude-3-5-haiku-20241022", "editor_model": "anthropic/claude-3-7-sonnet-20250219", "edit_format": "diff", "prompt_tokens": 5853, "completion_tokens": 298, "total_tokens": 6151, "cost": 0.022029, "total_cost": 0.022029}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740533159} -{"event": "command_exit", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740533168} -{"event": "exit", "properties": {"reason": "/exit"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740533168} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740533213} -{"event": "repo", "properties": {"num_files": 201}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740533213} -{"event": "cli session", "properties": {"main_model": "anthropic/claude-3-7-sonnet-20250219", "weak_model": "anthropic/claude-3-5-haiku-20241022", "editor_model": "anthropic/claude-3-7-sonnet-20250219", "edit_format": "diff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740533213} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740533221} -{"event": "message_send", "properties": {"main_model": "anthropic/claude-3-7-sonnet-20250219", "weak_model": "anthropic/claude-3-5-haiku-20241022", "editor_model": "anthropic/claude-3-7-sonnet-20250219", "edit_format": "diff", "prompt_tokens": 5963, "completion_tokens": 326, "total_tokens": 6289, "cost": 0.022779, "total_cost": 0.022779}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740533230} -{"event": "exit", "properties": {"reason": "Control-C"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740533236} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740584956} -{"event": "repo", "properties": {"num_files": 438}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740584959} -{"event": "cli session", "properties": {"main_model": "openrouter/REDACTED", "weak_model": "openrouter/REDACTED", "editor_model": "openrouter/REDACTED", "edit_format": "whole"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740584959} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740584960} -{"event": "message_send", "properties": {"main_model": "openrouter/REDACTED", "weak_model": "openrouter/REDACTED", "editor_model": "openrouter/REDACTED", "edit_format": "whole", "prompt_tokens": 8268, "completion_tokens": 96, "total_tokens": 8364, "cost": 0.026244, "total_cost": 0.026244}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740584966} -{"event": "command_exit", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740584967} -{"event": "exit", "properties": {"reason": "/exit"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740584967} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740587910} -{"event": "repo", "properties": {"num_files": 201}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740587910} -{"event": "cli session", "properties": {"main_model": "anthropic/claude-3-7-sonnet-20250219", "weak_model": "anthropic/claude-3-5-haiku-20241022", "editor_model": "anthropic/claude-3-7-sonnet-20250219", "edit_format": "diff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740587910} -{"event": "command_ask", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740587915} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740587915} -{"event": "message_send", "properties": {"main_model": "anthropic/claude-3-7-sonnet-20250219", "weak_model": "anthropic/claude-3-5-haiku-20241022", "editor_model": "anthropic/claude-3-7-sonnet-20250219", "edit_format": "ask", "prompt_tokens": 3914, "completion_tokens": 124, "total_tokens": 4038, "cost": 0.013602000000000001, "total_cost": 0.013602000000000001}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740587919} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740587933} -{"event": "message_send", "properties": {"main_model": "anthropic/claude-3-7-sonnet-20250219", "weak_model": "anthropic/claude-3-5-haiku-20241022", "editor_model": "anthropic/claude-3-7-sonnet-20250219", "edit_format": "diff", "prompt_tokens": 6328, "completion_tokens": 121, "total_tokens": 6449, "cost": 0.020799, "total_cost": 0.034401}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740587936} -{"event": "command_exit", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740587940} -{"event": "exit", "properties": {"reason": "/exit"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740587940} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740588857} -{"event": "repo", "properties": {"num_files": 438}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740588857} -{"event": "cli session", "properties": {"main_model": "anthropic/claude-3-7-sonnet-20250219", "weak_model": "anthropic/claude-3-5-haiku-20241022", "editor_model": "anthropic/claude-3-7-sonnet-20250219", "edit_format": "diff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740588857} -{"event": "command_add", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740588865} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740588877} -{"event": "message_send", "properties": {"main_model": "anthropic/claude-3-7-sonnet-20250219", "weak_model": "anthropic/claude-3-5-haiku-20241022", "editor_model": "anthropic/claude-3-7-sonnet-20250219", "edit_format": "diff", "prompt_tokens": 6434, "completion_tokens": 249, "total_tokens": 6683, "cost": 0.023037, "total_cost": 0.023037}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740588884} -{"event": "command_exit", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740588893} -{"event": "exit", "properties": {"reason": "/exit"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740588893} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740589070} -{"event": "exit", "properties": {"reason": "Listed models"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740589071} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740589079} -{"event": "exit", "properties": {"reason": "Listed models"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740589081} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740589085} -{"event": "exit", "properties": {"reason": "Listed models"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740589087} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740589097} -{"event": "exit", "properties": {"reason": "Listed models"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740589098} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740589102} -{"event": "exit", "properties": {"reason": "Listed models"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740589104} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740589122} -{"event": "repo", "properties": {"num_files": 438}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740589122} -{"event": "cli session", "properties": {"main_model": "anthropic/claude-3-7-sonnet-20250219", "weak_model": "anthropic/claude-3-5-haiku-20241022", "editor_model": "anthropic/claude-3-7-sonnet-20250219", "edit_format": "diff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740589122} -{"event": "command_add", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740589125} -{"event": "command_run", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740589132} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740589133} -{"event": "exit", "properties": {"reason": "Listed models"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740589135} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740589159} -{"event": "message_send", "properties": {"main_model": "anthropic/claude-3-7-sonnet-20250219", "weak_model": "anthropic/claude-3-5-haiku-20241022", "editor_model": "anthropic/claude-3-7-sonnet-20250219", "edit_format": "diff", "prompt_tokens": 12956, "completion_tokens": 1722, "total_tokens": 14678, "cost": 0.064698, "total_cost": 0.064698}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740589183} -{"event": "command_run", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740589201} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740589203} -{"event": "exit", "properties": {"reason": "Listed models"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740589204} -{"event": "command_run", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740589214} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740589215} -{"event": "exit", "properties": {"reason": "Listed models"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740589216} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740589235} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740589259} -{"event": "message_send", "properties": {"main_model": "anthropic/claude-3-7-sonnet-20250219", "weak_model": "anthropic/claude-3-5-haiku-20241022", "editor_model": "anthropic/claude-3-7-sonnet-20250219", "edit_format": "diff", "prompt_tokens": 17079, "completion_tokens": 1597, "total_tokens": 18676, "cost": 0.07519200000000001, "total_cost": 0.13989000000000001}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740589282} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740589286} -{"event": "command_exit", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740589296} -{"event": "exit", "properties": {"reason": "/exit"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740589296} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740589338} -{"event": "repo", "properties": {"num_files": 438}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740589338} -{"event": "cli session", "properties": {"main_model": "anthropic/claude-3-7-sonnet-20250219", "weak_model": "anthropic/claude-3-5-haiku-20241022", "editor_model": "anthropic/claude-3-7-sonnet-20250219", "edit_format": "diff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740589338} -{"event": "command_add", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740589341} -{"event": "command_run", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740589344} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740589346} -{"event": "exit", "properties": {"reason": "Listed models"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740589348} -{"event": "command_ask", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740589378} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740589378} -{"event": "message_send", "properties": {"main_model": "anthropic/claude-3-7-sonnet-20250219", "weak_model": "anthropic/claude-3-5-haiku-20241022", "editor_model": "anthropic/claude-3-7-sonnet-20250219", "edit_format": "ask", "prompt_tokens": 12550, "completion_tokens": 550, "total_tokens": 13100, "cost": 0.0459, "total_cost": 0.0459}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740589389} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740589406} -{"event": "message_send", "properties": {"main_model": "anthropic/claude-3-7-sonnet-20250219", "weak_model": "anthropic/claude-3-5-haiku-20241022", "editor_model": "anthropic/claude-3-7-sonnet-20250219", "edit_format": "diff", "prompt_tokens": 15422, "completion_tokens": 510, "total_tokens": 15932, "cost": 0.053916000000000006, "total_cost": 0.09981600000000002}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740589416} -{"event": "command_exit", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740589425} -{"event": "exit", "properties": {"reason": "/exit"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740589425} -{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740589447} -{"event": "no-repo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740589448} -{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740589448} -{"event": "message_send", "properties": {"main_model": "anthropic/claude-3-7-sonnet-20250219", "weak_model": "anthropic/claude-3-5-haiku-20241022", "editor_model": "anthropic/claude-3-7-sonnet-20250219", "edit_format": "diff", "prompt_tokens": 6679, "completion_tokens": 333, "total_tokens": 7012, "cost": 0.025032, "total_cost": 0.025032}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740589457} -{"event": "exit", "properties": {"reason": "Completed --message"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1740589457} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744668482} +{"event": "message_send", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "architect", "prompt_tokens": 28214, "completion_tokens": 358, "total_tokens": 28572, "cost": 0, "total_cost": 0.0}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744668500} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744668500} +{"event": "message_send", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "editor-diff-fenced", "prompt_tokens": 24042, "completion_tokens": 694, "total_tokens": 24736, "cost": 0, "total_cost": 0.0}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744668507} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744668515} +{"event": "message_send", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "editor-diff-fenced", "prompt_tokens": 25085, "completion_tokens": 247, "total_tokens": 25332, "cost": 0.0, "total_cost": 0.0}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744668520} +{"event": "exit", "properties": {"reason": "Control-C"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744668953} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744669081} +{"event": "no-repo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744669081} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744669081} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744669081} +{"event": "message_send", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "diff-fenced", "prompt_tokens": 18624, "completion_tokens": 269, "total_tokens": 18893, "cost": 0, "total_cost": 0.0}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744669098} +{"event": "exit", "properties": {"reason": "Completed --message"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744669098} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744669376} +{"event": "repo", "properties": {"num_files": 611}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744669378} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744669378} +{"event": "cli session", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "diff-fenced"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744669378} +{"event": "command_drop", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744669383} +{"event": "command_add", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744669387} +{"event": "command_ask", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744669429} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744669429} +{"event": "message_send", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "ask", "prompt_tokens": 14983, "completion_tokens": 1139, "total_tokens": 16122, "cost": 0, "total_cost": 0.0}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744669461} +{"event": "command_read-only", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744669542} +{"event": "command_architect", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744669548} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744669580} +{"event": "message_send", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "architect", "prompt_tokens": 20805, "completion_tokens": 5572, "total_tokens": 26377, "cost": 0, "total_cost": 0.0}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744669658} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744669688} +{"event": "message_send", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "editor-diff-fenced", "prompt_tokens": 22478, "completion_tokens": 4781, "total_tokens": 27259, "cost": 0, "total_cost": 0.0}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744669730} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744669757} +{"event": "message_send", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "editor-diff-fenced", "prompt_tokens": 33839, "completion_tokens": 1342, "total_tokens": 35181, "cost": 0.0, "total_cost": 0.0}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744669769} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744669771} +{"event": "message_send", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "editor-diff-fenced", "prompt_tokens": 35644, "completion_tokens": 225, "total_tokens": 35869, "cost": 0.0, "total_cost": 0.0}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744669785} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744669796} +{"event": "message_send", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "editor-diff-fenced", "prompt_tokens": 36366, "completion_tokens": 207, "total_tokens": 36573, "cost": 0.0, "total_cost": 0.0}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744669801} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744669833} +{"event": "message_send", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "architect", "prompt_tokens": 26513, "completion_tokens": 316, "total_tokens": 26829, "cost": 0, "total_cost": 0.0}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744669849} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744669849} +{"event": "message_send", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "editor-diff-fenced", "prompt_tokens": 17223, "completion_tokens": 354, "total_tokens": 17577, "cost": 0, "total_cost": 0.0}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744669854} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744669856} +{"event": "message_send", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "editor-diff-fenced", "prompt_tokens": 18094, "completion_tokens": 198, "total_tokens": 18292, "cost": 0.0, "total_cost": 0.0}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744669866} +{"event": "command_add", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744669899} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744669912} +{"event": "message_send", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "architect", "prompt_tokens": 31576, "completion_tokens": 304, "total_tokens": 31880, "cost": 0, "total_cost": 0.0}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744669919} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744669924} +{"event": "message_send", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "architect", "prompt_tokens": 35831, "completion_tokens": 484, "total_tokens": 36315, "cost": 0.0, "total_cost": 0.0}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744669934} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744669934} +{"event": "message_send", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "editor-diff-fenced", "prompt_tokens": 26407, "completion_tokens": 359, "total_tokens": 26766, "cost": 0, "total_cost": 0.0}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744669941} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744669947} +{"event": "message_send", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "editor-diff-fenced", "prompt_tokens": 27299, "completion_tokens": 199, "total_tokens": 27498, "cost": 0.0, "total_cost": 0.0}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744669951} +{"event": "command_drop", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744669957} +{"event": "command_drop", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744669962} +{"event": "command_add", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744669972} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744669984} +{"event": "message_send", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "architect", "prompt_tokens": 42680, "completion_tokens": 195, "total_tokens": 42875, "cost": 0, "total_cost": 0.0}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744669994} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744669997} +{"event": "message_send", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "architect", "prompt_tokens": 43433, "completion_tokens": 183, "total_tokens": 43616, "cost": 0.0, "total_cost": 0.0}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744670004} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744670005} +{"event": "message_send", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "editor-diff-fenced", "prompt_tokens": 32376, "completion_tokens": 232, "total_tokens": 32608, "cost": 0, "total_cost": 0.0}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744670009} +{"event": "command_drop", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744670032} +{"event": "command_clear", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744670033} +{"event": "command_read-only", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744670040} +{"event": "command_add", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744670043} +{"event": "command_ask", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744670066} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744670066} +{"event": "message_send", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "ask", "prompt_tokens": 25630, "completion_tokens": 532, "total_tokens": 26162, "cost": 0, "total_cost": 0.0}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744670080} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744670098} +{"event": "message_send", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "architect", "prompt_tokens": 25583, "completion_tokens": 1013, "total_tokens": 26596, "cost": 0, "total_cost": 0.0}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744670118} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744670118} +{"event": "message_send", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "editor-diff-fenced", "prompt_tokens": 23880, "completion_tokens": 1831, "total_tokens": 25711, "cost": 0, "total_cost": 0.0}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744670134} +{"event": "exit", "properties": {"reason": "Control-C"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744670549} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744670620} +{"event": "repo", "properties": {"num_files": 611}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744670620} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744670620} +{"event": "cli session", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "udiff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744670620} +{"event": "exit", "properties": {"reason": "Control-C"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744670622} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744670775} +{"event": "repo", "properties": {"num_files": 611}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744670775} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744670775} +{"event": "cli session", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "udiff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744670775} +{"event": "command_add", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744670781} +{"event": "command_add", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744670783} +{"event": "command_read-only", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744670788} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744670811} +{"event": "message_send", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "udiff", "prompt_tokens": 23022, "completion_tokens": 473, "total_tokens": 23495, "cost": 0, "total_cost": 0.0}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744670820} +{"event": "command_diff", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744670830} +{"event": "exit", "properties": {"reason": "Completed main CLI coder.run"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744670846} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744670893} +{"event": "no-repo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744670893} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744670893} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744670893} +{"event": "message_send", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "udiff", "prompt_tokens": 20528, "completion_tokens": 181, "total_tokens": 20709, "cost": 0, "total_cost": 0.0}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744670917} +{"event": "exit", "properties": {"reason": "Completed --message"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744670917} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672097} +{"event": "repo", "properties": {"num_files": 611}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672098} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672098} +{"event": "cli session", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "udiff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672098} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672122} +{"event": "message_send", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "udiff", "prompt_tokens": 12357, "completion_tokens": 1870, "total_tokens": 14227, "cost": 0, "total_cost": 0.0}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672157} +{"event": "command_chat-mode", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672179} +{"event": "exit", "properties": {"reason": "Control-C"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672185} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672187} +{"event": "repo", "properties": {"num_files": 611}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672187} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672187} +{"event": "cli session", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "udiff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672188} +{"event": "command_exit", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672198} +{"event": "exit", "properties": {"reason": "/exit"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672198} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672201} +{"event": "repo", "properties": {"num_files": 611}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672201} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672201} +{"event": "cli session", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "diff-fenced"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672201} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672205} +{"event": "message_send", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "diff-fenced", "prompt_tokens": 13588, "completion_tokens": 2991, "total_tokens": 16579, "cost": 0, "total_cost": 0.0}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672244} +{"event": "command_undo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672297} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672335} +{"event": "message_send", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "diff-fenced", "prompt_tokens": 16693, "completion_tokens": 3439, "total_tokens": 20132, "cost": 0, "total_cost": 0.0}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672378} +{"event": "command_undo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672447} +{"event": "command_clear", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672450} +{"event": "command_model", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672455} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672457} +{"event": "command_clear", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672469} +{"event": "command_think-tokens", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672472} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672473} +{"event": "message_send", "properties": {"main_model": "openrouter/anthropic/claude-3.7-sonnet", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "diff", "prompt_tokens": 13664, "completion_tokens": 6303, "total_tokens": 19967, "cost": 0.13553700000000002, "total_cost": 0.13553700000000002}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672573} +{"event": "exit", "properties": {"reason": "Control-C"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672841} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672885} +{"event": "model warning", "properties": {"main_model": "None", "weak_model": "None", "editor_model": "None"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672886} +{"event": "no-repo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672886} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672886} +{"event": "exit", "properties": {"reason": "Unknown edit format"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672886} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672886} +{"event": "model warning", "properties": {"main_model": "None", "weak_model": "None", "editor_model": "None"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672886} +{"event": "no-repo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672886} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672886} +{"event": "exit", "properties": {"reason": "Unknown edit format"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672886} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672886} +{"event": "model warning", "properties": {"main_model": "None", "weak_model": "None", "editor_model": "None"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672887} +{"event": "no-repo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672887} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672887} +{"event": "exit", "properties": {"reason": "Unknown edit format"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672887} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672887} +{"event": "model warning", "properties": {"main_model": "None", "weak_model": "None", "editor_model": "None"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672887} +{"event": "no-repo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672887} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672887} +{"event": "exit", "properties": {"reason": "Unknown edit format"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672887} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672887} +{"event": "model warning", "properties": {"main_model": "None", "weak_model": "None", "editor_model": "None"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672888} +{"event": "no-repo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672888} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672888} +{"event": "exit", "properties": {"reason": "Unknown edit format"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672888} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672888} +{"event": "model warning", "properties": {"main_model": "None", "weak_model": "None", "editor_model": "None"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672888} +{"event": "no-repo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672888} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672888} +{"event": "exit", "properties": {"reason": "Unknown edit format"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672888} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672888} +{"event": "model warning", "properties": {"main_model": "None", "weak_model": "None", "editor_model": "None"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672889} +{"event": "no-repo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672889} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672889} +{"event": "exit", "properties": {"reason": "Unknown edit format"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672889} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672889} +{"event": "model warning", "properties": {"main_model": "None", "weak_model": "None", "editor_model": "None"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672889} +{"event": "no-repo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672889} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672889} +{"event": "exit", "properties": {"reason": "Unknown edit format"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672889} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672889} +{"event": "model warning", "properties": {"main_model": "None", "weak_model": "None", "editor_model": "None"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672890} +{"event": "no-repo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672890} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672890} +{"event": "exit", "properties": {"reason": "Unknown edit format"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672890} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672890} +{"event": "model warning", "properties": {"main_model": "None", "weak_model": "None", "editor_model": "None"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672891} +{"event": "no-repo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672891} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672891} +{"event": "exit", "properties": {"reason": "Unknown edit format"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672891} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672891} +{"event": "model warning", "properties": {"main_model": "None", "weak_model": "None", "editor_model": "None"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672891} +{"event": "no-repo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672891} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672891} +{"event": "exit", "properties": {"reason": "Unknown edit format"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672891} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672891} +{"event": "model warning", "properties": {"main_model": "None", "weak_model": "None", "editor_model": "None"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672892} +{"event": "no-repo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672892} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672892} +{"event": "exit", "properties": {"reason": "Unknown edit format"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672892} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672892} +{"event": "model warning", "properties": {"main_model": "None", "weak_model": "None", "editor_model": "None"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672892} +{"event": "no-repo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672892} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672892} +{"event": "exit", "properties": {"reason": "Unknown edit format"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672892} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672892} +{"event": "model warning", "properties": {"main_model": "None", "weak_model": "None", "editor_model": "None"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672893} +{"event": "no-repo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672893} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672893} +{"event": "exit", "properties": {"reason": "Unknown edit format"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672893} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672893} +{"event": "model warning", "properties": {"main_model": "None", "weak_model": "None", "editor_model": "None"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672894} +{"event": "no-repo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672894} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672894} +{"event": "exit", "properties": {"reason": "Unknown edit format"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672894} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672894} +{"event": "model warning", "properties": {"main_model": "None", "weak_model": "None", "editor_model": "None"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672895} +{"event": "no-repo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672895} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672895} +{"event": "exit", "properties": {"reason": "Unknown edit format"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672895} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672990} +{"event": "repo", "properties": {"num_files": 611}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672992} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672992} +{"event": "exit", "properties": {"reason": "Exit flag set"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672992} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672992} +{"event": "model warning", "properties": {"main_model": "None", "weak_model": "None", "editor_model": "None"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672993} +{"event": "repo", "properties": {"num_files": 611}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672993} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672993} +{"event": "exit", "properties": {"reason": "Unknown edit format"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744672993} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744673037} +{"event": "gui session", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744673038} +{"event": "exit", "properties": {"reason": "GUI session ended"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744673038} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744676838} +{"event": "repo", "properties": {"num_files": 611}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744676839} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744676839} +{"event": "cli session", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "diff-fenced"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744676839} +{"event": "exit", "properties": {"reason": "Control-C"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744678980} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744690929} +{"event": "repo", "properties": {"num_files": 611}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744690930} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744690930} +{"event": "cli session", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "diff-fenced"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744690930} +{"event": "command_chat-mode", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744690933} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744690942} +{"event": "message_send", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "udiff", "prompt_tokens": 12971, "completion_tokens": 1172, "total_tokens": 14143, "cost": 0, "total_cost": 0.0}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744690956} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744690987} +{"event": "exit", "properties": {"reason": "Control-C"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744690990} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744690995} +{"event": "repo", "properties": {"num_files": 611}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744690996} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744690996} +{"event": "cli session", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "diff-fenced"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744690996} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744691005} +{"event": "exit", "properties": {"reason": "Control-C"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744691014} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744691017} +{"event": "repo", "properties": {"num_files": 611}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744691018} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744691018} +{"event": "cli session", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "diff-fenced"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744691018} +{"event": "command_chat-mode", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744691020} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744691022} +{"event": "message_send", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "udiff", "prompt_tokens": 12926, "completion_tokens": 969, "total_tokens": 13895, "cost": 0, "total_cost": 0.0}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744691035} +{"event": "exit", "properties": {"reason": "Control-C"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744691166} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744691169} +{"event": "repo", "properties": {"num_files": 611}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744691170} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744691170} +{"event": "cli session", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "diff-fenced"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744691170} +{"event": "ai-comments file-add", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744691214} +{"event": "ai-comments execute", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744691214} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744691214} +{"event": "message_send", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "diff-fenced", "prompt_tokens": 10036, "completion_tokens": 268, "total_tokens": 10304, "cost": 0, "total_cost": 0.0}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744691224} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744691233} +{"event": "repo", "properties": {"num_files": 611}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744691233} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744691233} +{"event": "cli session", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "diff-fenced"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744691233} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744691235} +{"event": "command_clear", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744691249} +{"event": "command_chat-mode", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744691252} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744691254} +{"event": "message_send", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "udiff", "prompt_tokens": 12944, "completion_tokens": 814, "total_tokens": 13758, "cost": 0, "total_cost": 0.0}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744691264} +{"event": "exit", "properties": {"reason": "Control-C"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744691319} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744691338} +{"event": "repo", "properties": {"num_files": 611}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744691339} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744691339} +{"event": "cli session", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "udiff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744691339} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744691375} +{"event": "exit", "properties": {"reason": "Control-C"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744691383} +{"event": "message_send", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "udiff", "prompt_tokens": 12971, "completion_tokens": 1071, "total_tokens": 14042, "cost": 0, "total_cost": 0.0}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744691391} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744691391} +{"event": "message_send", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "udiff", "prompt_tokens": 14633, "completion_tokens": 1110, "total_tokens": 15743, "cost": 0.0, "total_cost": 0.0}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744691402} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744691403} +{"event": "command_clear", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744691415} +{"event": "command_architect", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744691417} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744691419} +{"event": "message_send", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "architect", "prompt_tokens": 11955, "completion_tokens": 377, "total_tokens": 12332, "cost": 0, "total_cost": 0.0}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744691434} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744691434} +{"event": "message_send", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "udiff", "prompt_tokens": 9676, "completion_tokens": 952, "total_tokens": 10628, "cost": 0, "total_cost": 0.0}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744691443} +{"event": "command_undo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744691586} +{"event": "exit", "properties": {"reason": "Control-C"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744693288} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744693292} +{"event": "repo", "properties": {"num_files": 611}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744693293} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744693293} +{"event": "cli session", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "udiff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744693293} +{"event": "command_model", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744693299} +{"event": "command_think-tokens", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744693302} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744693309} +{"event": "message_send", "properties": {"main_model": "openrouter/anthropic/claude-3.7-sonnet", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "diff", "prompt_tokens": 14284, "completion_tokens": 1017, "total_tokens": 15301, "cost": 0.058107000000000006, "total_cost": 0.058107000000000006}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744693330} +{"event": "exit", "properties": {"reason": "Control-C"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744758990} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744761408} +{"event": "repo", "properties": {"num_files": 611}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744761409} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744761409} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744761409} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744761421} +{"event": "no-repo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744761421} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744761421} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744761421} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744761473} +{"event": "no-repo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744761474} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744761474} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744761474} +{"event": "message_send_exception", "properties": {"exception": "cannot access local variable 'os' where it is not associated with a value"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744761475} +{"event": "exit", "properties": {"reason": "Completed --message"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744761475} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744761490} +{"event": "no-repo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744761490} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744761490} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744761490} +{"event": "exit", "properties": {"reason": "Completed --message"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744761500} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744762281} +{"event": "exit", "properties": {"reason": "Listed models"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744762283} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744835675} +{"event": "repo", "properties": {"num_files": 611}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744835676} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744835676} +{"event": "cli session", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "udiff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744835676} +{"event": "exit", "properties": {"reason": "Control-C"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744835677} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744835693} +{"event": "repo", "properties": {"num_files": 611}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744835696} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744835696} +{"event": "cli session", "properties": {"main_model": "None", "weak_model": "None", "editor_model": "None", "edit_format": "whole"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744835696} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744835698} +{"event": "exit", "properties": {"reason": "Control-C"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744835707} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744835738} +{"event": "repo", "properties": {"num_files": 611}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744835740} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744835740} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744835740} +{"event": "exit", "properties": {"reason": "Completed --message"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744835748} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744835757} +{"event": "repo", "properties": {"num_files": 611}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744835759} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744835759} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744835759} +{"event": "message_send", "properties": {"main_model": "None", "weak_model": "None", "editor_model": "None", "edit_format": "whole", "prompt_tokens": 7906, "completion_tokens": 95, "total_tokens": 8001, "cost": 0.08286, "total_cost": 0.08286}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744835764} +{"event": "exit", "properties": {"reason": "Completed --message"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744835764} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744840697} +{"event": "repo", "properties": {"num_files": 611}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744840697} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744840697} +{"event": "cli session", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "udiff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744840698} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744840708} +{"event": "command_model", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744840722} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744840723} +{"event": "exit", "properties": {"reason": "Completed main CLI coder.run"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744840728} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744840735} +{"event": "repo", "properties": {"num_files": 611}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744840736} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744840736} +{"event": "cli session", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "udiff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744840736} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744840738} +{"event": "command_model", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744840744} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744840747} +{"event": "command_model", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744840756} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744840758} +{"event": "message_send", "properties": {"main_model": "o4-mini", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "diff", "prompt_tokens": 16129, "completion_tokens": 370, "total_tokens": 16499, "cost": 0.019369900000000002, "total_cost": 0.019369900000000002}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744840771} +{"event": "command_undo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744840803} +{"event": "command_model", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744840812} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744840815} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744840836} +{"event": "message_send", "properties": {"main_model": "gemini/gemini-2.5-pro-preview-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "diff-fenced", "prompt_tokens": 16190, "completion_tokens": 334, "total_tokens": 16524, "cost": 0.0235775, "total_cost": 0.042947400000000004}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744840842} +{"event": "command_exit", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744840853} +{"event": "exit", "properties": {"reason": "/exit"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744840853} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744843456} +{"event": "repo", "properties": {"num_files": 611}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744843458} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744843458} +{"event": "cli session", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "udiff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744843458} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744843488} +{"event": "message_send", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "udiff", "prompt_tokens": 15525, "completion_tokens": 179, "total_tokens": 15704, "cost": 0, "total_cost": 0.0}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744843495} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744843533} +{"event": "message_send", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "udiff", "prompt_tokens": 15869, "completion_tokens": 267, "total_tokens": 16136, "cost": 0, "total_cost": 0.0}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744843540} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744849446} +{"event": "message_send", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "udiff", "prompt_tokens": 16314, "completion_tokens": 428, "total_tokens": 16742, "cost": 0, "total_cost": 0.0}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744849452} +{"event": "exit", "properties": {"reason": "Control-C"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744849486} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852257} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852266} +{"event": "no-repo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852268} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852268} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852268} +{"event": "message_send", "properties": {"main_model": "o3", "weak_model": "gpt-4.1-mini", "editor_model": "gpt-4.1", "edit_format": "diff", "prompt_tokens": 2424, "completion_tokens": 97, "total_tokens": 2521, "cost": 0.028120000000000003, "total_cost": 0.028120000000000003}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852271} +{"event": "exit", "properties": {"reason": "Completed --message"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852271} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852277} +{"event": "no-repo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852278} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852278} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852278} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852321} +{"event": "repo", "properties": {"num_files": 611}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852322} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852322} +{"event": "exit", "properties": {"reason": "Completed lint/test/commit"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852324} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852418} +{"event": "model warning", "properties": {"main_model": "None", "weak_model": "None", "editor_model": "None"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852419} +{"event": "no-repo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852419} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852419} +{"event": "exit", "properties": {"reason": "Unknown edit format"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852419} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852419} +{"event": "model warning", "properties": {"main_model": "None", "weak_model": "None", "editor_model": "None"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852420} +{"event": "no-repo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852420} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852420} +{"event": "exit", "properties": {"reason": "Unknown edit format"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852420} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852420} +{"event": "model warning", "properties": {"main_model": "None", "weak_model": "None", "editor_model": "None"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852420} +{"event": "no-repo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852420} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852420} +{"event": "exit", "properties": {"reason": "Unknown edit format"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852420} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852420} +{"event": "model warning", "properties": {"main_model": "None", "weak_model": "None", "editor_model": "None"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852420} +{"event": "no-repo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852420} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852420} +{"event": "exit", "properties": {"reason": "Unknown edit format"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852420} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852420} +{"event": "model warning", "properties": {"main_model": "None", "weak_model": "None", "editor_model": "None"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852421} +{"event": "no-repo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852421} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852421} +{"event": "exit", "properties": {"reason": "Unknown edit format"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852421} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852421} +{"event": "model warning", "properties": {"main_model": "None", "weak_model": "None", "editor_model": "None"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852421} +{"event": "no-repo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852421} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852421} +{"event": "exit", "properties": {"reason": "Unknown edit format"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852421} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852421} +{"event": "model warning", "properties": {"main_model": "None", "weak_model": "None", "editor_model": "None"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852422} +{"event": "no-repo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852422} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852422} +{"event": "exit", "properties": {"reason": "Unknown edit format"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852422} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852422} +{"event": "model warning", "properties": {"main_model": "None", "weak_model": "None", "editor_model": "None"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852423} +{"event": "no-repo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852423} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852423} +{"event": "exit", "properties": {"reason": "Unknown edit format"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852423} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852423} +{"event": "model warning", "properties": {"main_model": "None", "weak_model": "None", "editor_model": "None"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852423} +{"event": "no-repo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852423} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852423} +{"event": "exit", "properties": {"reason": "Unknown edit format"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852423} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852423} +{"event": "model warning", "properties": {"main_model": "None", "weak_model": "None", "editor_model": "None"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852424} +{"event": "no-repo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852424} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852424} +{"event": "exit", "properties": {"reason": "Unknown edit format"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852424} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852424} +{"event": "model warning", "properties": {"main_model": "None", "weak_model": "None", "editor_model": "None"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852424} +{"event": "no-repo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852424} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852424} +{"event": "exit", "properties": {"reason": "Unknown edit format"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852424} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852424} +{"event": "model warning", "properties": {"main_model": "None", "weak_model": "None", "editor_model": "None"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852425} +{"event": "no-repo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852425} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852425} +{"event": "exit", "properties": {"reason": "Unknown edit format"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852425} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852425} +{"event": "model warning", "properties": {"main_model": "None", "weak_model": "None", "editor_model": "None"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852425} +{"event": "no-repo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852425} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852425} +{"event": "exit", "properties": {"reason": "Unknown edit format"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852425} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852425} +{"event": "model warning", "properties": {"main_model": "None", "weak_model": "None", "editor_model": "None"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852425} +{"event": "no-repo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852425} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852425} +{"event": "exit", "properties": {"reason": "Unknown edit format"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852425} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852426} +{"event": "model warning", "properties": {"main_model": "None", "weak_model": "None", "editor_model": "None"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852426} +{"event": "no-repo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852426} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852426} +{"event": "exit", "properties": {"reason": "Unknown edit format"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852426} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852426} +{"event": "model warning", "properties": {"main_model": "None", "weak_model": "None", "editor_model": "None"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852427} +{"event": "no-repo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852427} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852427} +{"event": "exit", "properties": {"reason": "Unknown edit format"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852427} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852513} +{"event": "repo", "properties": {"num_files": 611}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852514} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852514} +{"event": "exit", "properties": {"reason": "Exit flag set"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852514} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852514} +{"event": "model warning", "properties": {"main_model": "None", "weak_model": "None", "editor_model": "None"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852514} +{"event": "repo", "properties": {"num_files": 611}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852515} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852515} +{"event": "exit", "properties": {"reason": "Unknown edit format"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852515} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852551} +{"event": "no-repo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852553} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852553} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852553} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852556} +{"event": "gui session", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852556} +{"event": "exit", "properties": {"reason": "GUI session ended"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852556} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852577} +{"event": "no-repo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852579} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852579} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852579} +{"event": "message_send", "properties": {"main_model": "o3", "weak_model": "gpt-4.1-mini", "editor_model": "gpt-4.1", "edit_format": "diff", "prompt_tokens": 2371, "completion_tokens": 70, "total_tokens": 2441, "cost": 0.026510000000000002, "total_cost": 0.026510000000000002}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852582} +{"event": "exit", "properties": {"reason": "Completed --message"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852582} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852785} +{"event": "repo", "properties": {"num_files": 611}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852788} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852788} +{"event": "cli session", "properties": {"main_model": "huggingface/REDACTED", "weak_model": "huggingface/REDACTED", "editor_model": "huggingface/REDACTED", "edit_format": "whole"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852788} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852789} +{"event": "exit", "properties": {"reason": "Completed main CLI coder.run"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852857} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852879} +{"event": "repo", "properties": {"num_files": 611}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852879} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852879} +{"event": "cli session", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "udiff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852880} +{"event": "command_read-only", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852896} +{"event": "command_read-only", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852905} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852987} +{"event": "repo", "properties": {"num_files": 611}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852990} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852990} +{"event": "cli session", "properties": {"main_model": "huggingface/REDACTED", "weak_model": "huggingface/REDACTED", "editor_model": "huggingface/REDACTED", "edit_format": "whole"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852990} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744852991} +{"event": "exit", "properties": {"reason": "Control-C"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744853024} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744853033} +{"event": "repo", "properties": {"num_files": 611}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744853036} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744853036} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744853036} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744853057} +{"event": "repo", "properties": {"num_files": 611}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744853059} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744853059} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744853059} +{"event": "exit", "properties": {"reason": "Control-C"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744853069} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744853206} +{"event": "repo", "properties": {"num_files": 611}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744853209} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744853209} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744853209} +{"event": "exit", "properties": {"reason": "Completed --message"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744853216} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744853270} +{"event": "no-repo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744853270} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744853270} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744853270} +{"event": "message_send", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "udiff", "prompt_tokens": 7310, "completion_tokens": 173, "total_tokens": 7483, "cost": 0, "total_cost": 0.0}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744853291} +{"event": "exit", "properties": {"reason": "Completed --message"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744853291} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744855133} +{"event": "repo", "properties": {"num_files": 611}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744855134} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744855134} +{"event": "cli session", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "udiff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744855134} +{"event": "command_add", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744855140} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744855150} +{"event": "message_send", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "udiff", "prompt_tokens": 9490, "completion_tokens": 2108, "total_tokens": 11598, "cost": 0, "total_cost": 0.0}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744855168} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744855169} +{"event": "message_send", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "udiff", "prompt_tokens": 12680, "completion_tokens": 2039, "total_tokens": 14719, "cost": 0.0, "total_cost": 0.0}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744855184} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744855185} +{"event": "command_architect", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744855188} +{"event": "command_undo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744855202} +{"event": "command_clear", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744855204} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744855205} +{"event": "message_send", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "architect", "prompt_tokens": 8474, "completion_tokens": 301, "total_tokens": 8775, "cost": 0, "total_cost": 0.0}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744855212} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744855212} +{"event": "message_send", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "udiff", "prompt_tokens": 6080, "completion_tokens": 477, "total_tokens": 6557, "cost": 0, "total_cost": 0.0}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744855217} +{"event": "exit", "properties": {"reason": "Control-C"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744855261} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744916060} +{"event": "model warning", "properties": {"main_model": "gemini/REDACTED", "weak_model": "gemini/REDACTED", "editor_model": "gemini/REDACTED"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744916063} +{"event": "exit", "properties": {"reason": "Keyboard interrupt during model warnings"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744916064} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744916070} +{"event": "repo", "properties": {"num_files": 611}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744916073} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744916073} +{"event": "cli session", "properties": {"main_model": "gemini/REDACTED", "weak_model": "gemini/REDACTED", "editor_model": "gemini/REDACTED", "edit_format": "whole"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744916073} +{"event": "exit", "properties": {"reason": "Control-C"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744916074} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744916083} +{"event": "no-repo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744916085} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744916085} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744916085} +{"event": "message_send", "properties": {"main_model": "gemini/REDACTED", "weak_model": "gemini/REDACTED", "editor_model": "gemini/REDACTED", "edit_format": "whole", "prompt_tokens": 593, "completion_tokens": 13, "total_tokens": 606, "cost": 0, "total_cost": 0.0}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744916086} +{"event": "exit", "properties": {"reason": "Completed --message"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744916086} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744942667} +{"event": "repo", "properties": {"num_files": 611}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744942668} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744942668} +{"event": "cli session", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "udiff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744942668} +{"event": "command_read-only", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744942675} +{"event": "command_read-only", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744942678} +{"event": "exit", "properties": {"reason": "Control-C"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744942692} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744945174} +{"event": "model warning", "properties": {"main_model": "gemini/REDACTED", "weak_model": "gemini/REDACTED", "editor_model": "gemini/REDACTED"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744945175} +{"event": "exit", "properties": {"reason": "Keyboard interrupt during model warnings"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744945188} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744945258} +{"event": "repo", "properties": {"num_files": 611}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744945258} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744945258} +{"event": "cli session", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "udiff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744945258} +{"event": "command_read-only", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744945263} +{"event": "command_add", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744945267} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744945274} +{"event": "message_send", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "udiff", "prompt_tokens": 20283, "completion_tokens": 661, "total_tokens": 20944, "cost": 0, "total_cost": 0.0}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744945285} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744945322} +{"event": "message_send", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "udiff", "prompt_tokens": 21131, "completion_tokens": 177, "total_tokens": 21308, "cost": 0, "total_cost": 0.0}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744945329} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744945347} +{"event": "message_send", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "udiff", "prompt_tokens": 21364, "completion_tokens": 68, "total_tokens": 21432, "cost": 0, "total_cost": 0.0}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744945350} +{"event": "exit", "properties": {"reason": "Control-C"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744945722} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744945734} +{"event": "repo", "properties": {"num_files": 611}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744945735} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744945735} +{"event": "exit", "properties": {"reason": "Completed lint/test/commit"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744945738} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744946266} +{"event": "repo", "properties": {"num_files": 611}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744946266} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744946266} +{"event": "cli session", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "udiff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744946266} +{"event": "command_add", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744946269} +{"event": "command_model", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744946278} +{"event": "command_reasoning-effort", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744946283} +{"event": "command_ask", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744946291} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744946291} +{"event": "message_send", "properties": {"main_model": "o3", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "ask", "prompt_tokens": 14950, "completion_tokens": 889, "total_tokens": 15839, "cost": 0.18506000000000003, "total_cost": 0.18506000000000003}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744946304} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744946345} +{"event": "message_send", "properties": {"main_model": "o3", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "diff", "prompt_tokens": 16775, "completion_tokens": 5498, "total_tokens": 22273, "cost": 0.38767, "total_cost": 0.5727300000000001}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744946391} +{"event": "command_read-only", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744946424} +{"event": "command_ask", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744946440} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744946440} +{"event": "message_send", "properties": {"main_model": "o3", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "ask", "prompt_tokens": 17168, "completion_tokens": 1504, "total_tokens": 18672, "cost": 0.23184000000000005, "total_cost": 0.8045700000000001}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744946459} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744946483} +{"event": "message_send", "properties": {"main_model": "o3", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "diff", "prompt_tokens": 19187, "completion_tokens": 683, "total_tokens": 19870, "cost": 0.21919000000000002, "total_cost": 1.0237600000000002}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744946496} +{"event": "command_add", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744946545} +{"event": "command_chat-mode", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744946557} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744946572} +{"event": "message_send", "properties": {"main_model": "o3", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "whole", "prompt_tokens": 16552, "completion_tokens": 2609, "total_tokens": 19161, "cost": 0.26988, "total_cost": 1.2936400000000003}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744946592} +{"event": "command_diff", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744946606} +{"event": "exit", "properties": {"reason": "Control-C"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1744946701} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745087266} +{"event": "repo", "properties": {"num_files": 611}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745087267} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745087267} +{"event": "exit", "properties": {"reason": "Completed lint/test/commit"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745087270} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745113992} +{"event": "repo", "properties": {"num_files": 611}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745113992} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745113992} +{"event": "cli session", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "udiff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745113992} +{"event": "command_help", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745113994} +{"event": "exit", "properties": {"reason": "Control-C"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745114112} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745161720} +{"event": "no-repo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745161720} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745161720} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745161720} +{"event": "message_send", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "udiff", "prompt_tokens": 1149, "completion_tokens": 23, "total_tokens": 1172, "cost": 0, "total_cost": 0.0}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745161736} +{"event": "exit", "properties": {"reason": "Completed --message"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745161736} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745161749} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745161783} +{"event": "no-repo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745161785} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745161785} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745161785} +{"event": "exit", "properties": {"reason": "Completed --message"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745161788} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745161799} +{"event": "no-repo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745161817} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745161817} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745161817} +{"event": "exit", "properties": {"reason": "Completed --message"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745161818} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162550} +{"event": "model warning", "properties": {"main_model": "None", "weak_model": "None", "editor_model": "None"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162555} +{"event": "no-repo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162555} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162555} +{"event": "exit", "properties": {"reason": "Unknown edit format"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162555} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162555} +{"event": "model warning", "properties": {"main_model": "None", "weak_model": "None", "editor_model": "None"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162556} +{"event": "no-repo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162556} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162556} +{"event": "exit", "properties": {"reason": "Unknown edit format"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162556} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162556} +{"event": "model warning", "properties": {"main_model": "None", "weak_model": "None", "editor_model": "None"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162556} +{"event": "no-repo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162556} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162556} +{"event": "exit", "properties": {"reason": "Unknown edit format"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162556} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162556} +{"event": "model warning", "properties": {"main_model": "None", "weak_model": "None", "editor_model": "None"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162557} +{"event": "no-repo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162557} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162557} +{"event": "exit", "properties": {"reason": "Unknown edit format"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162557} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162557} +{"event": "model warning", "properties": {"main_model": "None", "weak_model": "None", "editor_model": "None"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162557} +{"event": "no-repo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162557} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162557} +{"event": "exit", "properties": {"reason": "Unknown edit format"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162557} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162557} +{"event": "model warning", "properties": {"main_model": "None", "weak_model": "None", "editor_model": "None"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162558} +{"event": "no-repo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162558} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162558} +{"event": "exit", "properties": {"reason": "Unknown edit format"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162558} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162558} +{"event": "model warning", "properties": {"main_model": "None", "weak_model": "None", "editor_model": "None"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162559} +{"event": "no-repo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162559} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162559} +{"event": "exit", "properties": {"reason": "Unknown edit format"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162559} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162559} +{"event": "model warning", "properties": {"main_model": "None", "weak_model": "None", "editor_model": "None"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162559} +{"event": "no-repo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162559} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162559} +{"event": "exit", "properties": {"reason": "Unknown edit format"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162559} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162559} +{"event": "model warning", "properties": {"main_model": "None", "weak_model": "None", "editor_model": "None"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162560} +{"event": "no-repo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162560} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162560} +{"event": "exit", "properties": {"reason": "Unknown edit format"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162560} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162560} +{"event": "model warning", "properties": {"main_model": "None", "weak_model": "None", "editor_model": "None"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162560} +{"event": "no-repo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162560} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162560} +{"event": "exit", "properties": {"reason": "Unknown edit format"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162560} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162561} +{"event": "model warning", "properties": {"main_model": "None", "weak_model": "None", "editor_model": "None"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162561} +{"event": "no-repo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162561} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162561} +{"event": "exit", "properties": {"reason": "Unknown edit format"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162561} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162561} +{"event": "model warning", "properties": {"main_model": "None", "weak_model": "None", "editor_model": "None"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162562} +{"event": "no-repo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162562} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162562} +{"event": "exit", "properties": {"reason": "Unknown edit format"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162562} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162562} +{"event": "model warning", "properties": {"main_model": "None", "weak_model": "None", "editor_model": "None"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162562} +{"event": "no-repo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162562} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162562} +{"event": "exit", "properties": {"reason": "Unknown edit format"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162562} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162562} +{"event": "model warning", "properties": {"main_model": "None", "weak_model": "None", "editor_model": "None"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162563} +{"event": "no-repo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162563} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162563} +{"event": "exit", "properties": {"reason": "Unknown edit format"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162563} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162563} +{"event": "model warning", "properties": {"main_model": "None", "weak_model": "None", "editor_model": "None"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162563} +{"event": "no-repo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162563} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162563} +{"event": "exit", "properties": {"reason": "Unknown edit format"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162563} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162563} +{"event": "model warning", "properties": {"main_model": "None", "weak_model": "None", "editor_model": "None"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162564} +{"event": "no-repo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162564} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162564} +{"event": "exit", "properties": {"reason": "Unknown edit format"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162564} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162620} +{"event": "repo", "properties": {"num_files": 611}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162621} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162621} +{"event": "exit", "properties": {"reason": "Exit flag set"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162621} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162621} +{"event": "model warning", "properties": {"main_model": "None", "weak_model": "None", "editor_model": "None"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162622} +{"event": "repo", "properties": {"num_files": 611}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162622} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162622} +{"event": "exit", "properties": {"reason": "Unknown edit format"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162622} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162754} +{"event": "gui session", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162760} +{"event": "exit", "properties": {"reason": "GUI session ended"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745162760} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745163376} +{"event": "no-repo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745163381} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745163381} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745163393} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745163405} +{"event": "repo", "properties": {"num_files": 611}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745163405} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745163405} +{"event": "cli session", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "udiff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745163405} +{"event": "exit", "properties": {"reason": "Control-C"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745163407} +{"event": "message_send", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "udiff", "prompt_tokens": 5399, "completion_tokens": 256, "total_tokens": 5655, "cost": 0, "total_cost": 0.0}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745163408} +{"event": "exit", "properties": {"reason": "Completed --message"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745163408} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745163508} +{"event": "repo", "properties": {"num_files": 611}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745163513} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745163513} +{"event": "cli session", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "udiff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745163513} +{"event": "exit", "properties": {"reason": "Completed main CLI coder.run"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745163518} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745169424} +{"event": "repo", "properties": {"num_files": 611}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745169425} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745169425} +{"event": "cli session", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "udiff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745169425} +{"event": "exit", "properties": {"reason": "Completed main CLI coder.run"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745169504} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745169513} +{"event": "repo", "properties": {"num_files": 611}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745169513} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745169513} +{"event": "cli session", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "udiff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745169515} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745169818} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745169922} +{"event": "message_send", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "udiff", "prompt_tokens": 12540, "completion_tokens": 4598, "total_tokens": 17138, "cost": 0, "total_cost": 0.0}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745169961} +{"event": "command_undo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745169983} +{"event": "command_clear", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745169986} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745169991} +{"event": "command_drop", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745170026} +{"event": "command_clear", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745170032} +{"event": "command_drop", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745170033} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745170035} +{"event": "message_send", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "udiff", "prompt_tokens": 11415, "completion_tokens": 3674, "total_tokens": 15089, "cost": 0, "total_cost": 0.0}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745170079} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745170445} +{"event": "message_send", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "udiff", "prompt_tokens": 15533, "completion_tokens": 259, "total_tokens": 15792, "cost": 0, "total_cost": 0.0}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745170451} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745171263} +{"event": "repo", "properties": {"num_files": 612}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745171263} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745171263} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745171264} +{"event": "message_send", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "udiff", "prompt_tokens": 8867, "completion_tokens": 17, "total_tokens": 8884, "cost": 0, "total_cost": 0.0}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745171269} +{"event": "exit", "properties": {"reason": "Completed --message"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745171269} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745171274} +{"event": "model warning", "properties": {"main_model": "None", "weak_model": "None", "editor_model": "None"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745171276} +{"event": "exit", "properties": {"reason": "Keyboard interrupt during model warnings"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745171302} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745171306} +{"event": "repo", "properties": {"num_files": 612}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745171307} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745171307} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745171307} +{"event": "message_send", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "udiff", "prompt_tokens": 9113, "completion_tokens": 17, "total_tokens": 9130, "cost": 0, "total_cost": 0.0}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745171312} +{"event": "exit", "properties": {"reason": "Completed --message"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745171312} +{"event": "exit", "properties": {"reason": "Control-C"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745171723} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745171757} +{"event": "repo", "properties": {"num_files": 612}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745171758} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745171758} +{"event": "cli session", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "udiff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745171758} +{"event": "exit", "properties": {"reason": "Completed main CLI coder.run"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745171761} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745171764} +{"event": "repo", "properties": {"num_files": 612}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745171765} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745171765} +{"event": "cli session", "properties": {"main_model": "gemini/gemini-2.5-pro-preview-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-preview-03-25", "edit_format": "diff-fenced"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745171765} +{"event": "exit", "properties": {"reason": "Completed main CLI coder.run"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745171829} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745171854} +{"event": "repo", "properties": {"num_files": 612}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745171854} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745171854} +{"event": "cli session", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "udiff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745171854} +{"event": "command_read-only", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745171874} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745171909} +{"event": "message_send", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "udiff", "prompt_tokens": 12362, "completion_tokens": 445, "total_tokens": 12807, "cost": 0, "total_cost": 0.0}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745171917} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745171960} +{"event": "message_send", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "udiff", "prompt_tokens": 13269, "completion_tokens": 262, "total_tokens": 13531, "cost": 0, "total_cost": 0.0}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745171970} +{"event": "command_add", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745171985} +{"event": "command_read-only", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745171988} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745171994} +{"event": "message_send", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "udiff", "prompt_tokens": 20636, "completion_tokens": 177, "total_tokens": 20813, "cost": 0, "total_cost": 0.0}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745171998} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745172037} +{"event": "message_send", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "udiff", "prompt_tokens": 20894, "completion_tokens": 496, "total_tokens": 21390, "cost": 0, "total_cost": 0.0}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745172048} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745172095} +{"event": "message_send", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "udiff", "prompt_tokens": 21739, "completion_tokens": 251, "total_tokens": 21990, "cost": 0, "total_cost": 0.0}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745172099} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745172115} +{"event": "message_send", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "udiff", "prompt_tokens": 22364, "completion_tokens": 155, "total_tokens": 22519, "cost": 0.0, "total_cost": 0.0}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745172119} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745172137} +{"event": "message_send", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "udiff", "prompt_tokens": 22589, "completion_tokens": 696, "total_tokens": 23285, "cost": 0, "total_cost": 0.0}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745172157} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745172200} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745172211} +{"event": "message_send", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "udiff", "prompt_tokens": 23740, "completion_tokens": 695, "total_tokens": 24435, "cost": 0, "total_cost": 0.0}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745172220} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745172282} +{"event": "message_send", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "udiff", "prompt_tokens": 24460, "completion_tokens": 1797, "total_tokens": 26257, "cost": 0, "total_cost": 0.0}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745172335} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745172348} +{"event": "message_send", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "udiff", "prompt_tokens": 27939, "completion_tokens": 314, "total_tokens": 28253, "cost": 0.0, "total_cost": 0.0}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745172356} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745172356} +{"event": "message_send", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "udiff", "prompt_tokens": 28199, "completion_tokens": 1732, "total_tokens": 29931, "cost": 0.0, "total_cost": 0.0}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745172379} +{"event": "command_clear", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745172493} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745172522} +{"event": "message_send", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "udiff", "prompt_tokens": 19352, "completion_tokens": 467, "total_tokens": 19819, "cost": 0, "total_cost": 0.0}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745172532} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745172581} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745172586} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745172594} +{"event": "message_send", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "udiff", "prompt_tokens": 20067, "completion_tokens": 327, "total_tokens": 20394, "cost": 0, "total_cost": 0.0}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745172610} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745172650} +{"event": "message_send", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "udiff", "prompt_tokens": 22284, "completion_tokens": 426, "total_tokens": 22710, "cost": 0, "total_cost": 0.0}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745172657} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745172722} +{"event": "repo", "properties": {"num_files": 613}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745172724} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745172724} +{"event": "cli session", "properties": {"main_model": "xai/grok-3-fast-beta", "weak_model": "xai/grok-3-fast-beta", "editor_model": "xai/grok-3-fast-beta", "edit_format": "diff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745172724} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745172725} +{"event": "message_send", "properties": {"main_model": "xai/grok-3-fast-beta", "weak_model": "xai/grok-3-fast-beta", "editor_model": "xai/grok-3-fast-beta", "edit_format": "diff", "prompt_tokens": 10210, "completion_tokens": 78, "total_tokens": 10288, "cost": 0.053000000000000005, "total_cost": 0.053000000000000005}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745172729} +{"event": "command_exit", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745172730} +{"event": "exit", "properties": {"reason": "/exit"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745172730} +{"event": "exit", "properties": {"reason": "Control-C"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745172883} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745173009} +{"event": "repo", "properties": {"num_files": 613}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745173009} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745173009} +{"event": "cli session", "properties": {"main_model": "gemini/gemini-2.5-flash-preview-04-17", "weak_model": "gemini/gemini-2.5-flash-preview-04-17", "editor_model": "gemini/gemini-2.5-flash-preview-04-17", "edit_format": "whole"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745173009} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745173017} +{"event": "message_send", "properties": {"main_model": "gemini/gemini-2.5-flash-preview-04-17", "weak_model": "gemini/gemini-2.5-flash-preview-04-17", "editor_model": "gemini/gemini-2.5-flash-preview-04-17", "edit_format": "whole", "prompt_tokens": 8315, "completion_tokens": 25, "total_tokens": 8340, "cost": 0.00126225, "total_cost": 0.00126225}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745173024} +{"event": "exit", "properties": {"reason": "Control-C"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745173227} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745173231} +{"event": "model warning", "properties": {"main_model": "gemini/REDACTED", "weak_model": "gemini/REDACTED", "editor_model": "gemini/REDACTED"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745173234} +{"event": "exit", "properties": {"reason": "Keyboard interrupt during model warnings"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745173235} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745173477} +{"event": "repo", "properties": {"num_files": 613}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745173478} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745173478} +{"event": "cli session", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "udiff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745173478} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745173651} +{"event": "message_send", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "udiff", "prompt_tokens": 13344, "completion_tokens": 1294, "total_tokens": 14638, "cost": 0, "total_cost": 0.0}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745173666} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745173674} +{"event": "message_send", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "udiff", "prompt_tokens": 15409, "completion_tokens": 651, "total_tokens": 16060, "cost": 0.0, "total_cost": 0.0}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745173687} +{"event": "command_undo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745173750} +{"event": "command_undo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745173754} +{"event": "command_clear", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745173757} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745173764} +{"event": "message_send", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "udiff", "prompt_tokens": 13350, "completion_tokens": 721, "total_tokens": 14071, "cost": 0, "total_cost": 0.0}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745173772} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745173790} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745173796} +{"event": "repo", "properties": {"num_files": 613}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745173796} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745173796} +{"event": "cli session", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "udiff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745173796} +{"event": "command_exit", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745173798} +{"event": "exit", "properties": {"reason": "/exit"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745173798} +{"event": "exit", "properties": {"reason": "Control-C"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745173873} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745173898} +{"event": "repo", "properties": {"num_files": 613}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745173898} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745173898} +{"event": "cli session", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "udiff"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745173898} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745173908} +{"event": "message_send", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "udiff", "prompt_tokens": 8723, "completion_tokens": 179, "total_tokens": 8902, "cost": 0, "total_cost": 0.0}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745173914} +{"event": "command_add", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745173939} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745173954} +{"event": "message_send", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "udiff", "prompt_tokens": 7633, "completion_tokens": 328, "total_tokens": 7961, "cost": 0, "total_cost": 0.0}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745173960} +{"event": "command_add", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745173967} +{"event": "command_add", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745173985} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745173997} +{"event": "message_send", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "udiff", "prompt_tokens": 25866, "completion_tokens": 543, "total_tokens": 26409, "cost": 0, "total_cost": 0.0}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745174004} +{"event": "command_undo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745174035} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745174044} +{"event": "message_send", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "udiff", "prompt_tokens": 26460, "completion_tokens": 137, "total_tokens": 26597, "cost": 0, "total_cost": 0.0}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745174049} +{"event": "command_drop", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745174055} +{"event": "command_exit", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745174056} +{"event": "exit", "properties": {"reason": "/exit"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745174056} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745174643} +{"event": "repo", "properties": {"num_files": 615}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745174643} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745174643} +{"event": "cli session", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "udiff-simple"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745174643} +{"event": "command_exit", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745174645} +{"event": "exit", "properties": {"reason": "/exit"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745174645} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745175891} +{"event": "repo", "properties": {"num_files": 615}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745175892} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745175892} +{"event": "exit", "properties": {"reason": "Showed prompts"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745175896} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745189251} +{"event": "repo", "properties": {"num_files": 615}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745189251} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745189251} +{"event": "exit", "properties": {"reason": "Completed lint/test/commit"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745189254} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745189929} +{"event": "repo", "properties": {"num_files": 615}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745189931} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745189931} +{"event": "exit", "properties": {"reason": "Completed lint/test/commit"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745189934} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745189982} +{"event": "exit", "properties": {"reason": "Listed models"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745189984} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745189999} +{"event": "repo", "properties": {"num_files": 615}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745190000} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745190000} +{"event": "cli session", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "udiff-simple"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745190000} +{"event": "command_add", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745190007} +{"event": "command_edit", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745190022} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745190053} +{"event": "message_send", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "udiff-simple", "prompt_tokens": 16858, "completion_tokens": 143, "total_tokens": 17001, "cost": 0, "total_cost": 0.0}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745190062} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745190237} +{"event": "message_send", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "udiff-simple", "prompt_tokens": 17173, "completion_tokens": 312, "total_tokens": 17485, "cost": 0, "total_cost": 0.0}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745190243} +{"event": "command_exit", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745190249} +{"event": "exit", "properties": {"reason": "/exit"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745190249} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745190260} +{"event": "repo", "properties": {"num_files": 615}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745190262} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745190262} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745190262} +{"event": "message_send", "properties": {"main_model": "gemini/gemini-2.5-flash-preview-04-17", "weak_model": "gemini/gemini-2.5-flash-preview-04-17", "editor_model": "gemini/gemini-2.5-flash-preview-04-17", "edit_format": "diff", "prompt_tokens": 10189, "completion_tokens": 116, "total_tokens": 10305, "cost": 0.0015979499999999999, "total_cost": 0.0015979499999999999}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745190272} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745190955} +{"event": "repo", "properties": {"num_files": 615}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745190955} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745190955} +{"event": "cli session", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "udiff-simple"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745190955} +{"event": "command_edit", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745190964} +{"event": "command_ask", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745191011} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745191011} +{"event": "message_send", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "ask", "prompt_tokens": 8442, "completion_tokens": 417, "total_tokens": 8859, "cost": 0, "total_cost": 0.0}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745191025} +{"event": "command_ask", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745191054} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745191054} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745191127} +{"event": "message_send", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "udiff-simple", "prompt_tokens": 14287, "completion_tokens": 1995, "total_tokens": 16282, "cost": 0, "total_cost": 0.0}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745191145} +{"event": "command_clear", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745191182} +{"event": "command_ask", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745191214} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745191214} +{"event": "command_clear", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745191224} +{"event": "command_edit", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745191228} +{"event": "command_ask", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745191286} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745191286} +{"event": "message_send", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "ask", "prompt_tokens": 8481, "completion_tokens": 1848, "total_tokens": 10329, "cost": 0, "total_cost": 0.0}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745191334} +{"event": "command_clear", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745191533} +{"event": "ai-comments execute", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745191535} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745191535} +{"event": "message_send", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "udiff-simple", "prompt_tokens": 8611, "completion_tokens": 89, "total_tokens": 8700, "cost": 0, "total_cost": 0.0}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745191553} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745192191} +{"event": "repo", "properties": {"num_files": 615}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745192191} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745192191} +{"event": "exit", "properties": {"reason": "Completed lint/test/commit"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745192196} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745192301} +{"event": "no-repo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745192301} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745192301} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745192301} +{"event": "message_send", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "udiff-simple", "prompt_tokens": 7873, "completion_tokens": 147, "total_tokens": 8020, "cost": 0, "total_cost": 0.0}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745192314} +{"event": "exit", "properties": {"reason": "Completed --message"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745192314} +{"event": "ai-comments file-add", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745192315} +{"event": "ai-comments file-add", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745192318} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745192612} +{"event": "repo", "properties": {"num_files": 615}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745192612} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745192612} +{"event": "exit", "properties": {"reason": "Completed lint/test/commit"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745192615} +{"event": "launched", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745194554} +{"event": "repo", "properties": {"num_files": 615}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745194556} +{"event": "auto_commits", "properties": {"enabled": true}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745194556} +{"event": "cli session", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "udiff-simple"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745194556} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745194568} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745194656} +{"event": "message_send", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "udiff-simple", "prompt_tokens": 17860, "completion_tokens": 168, "total_tokens": 18028, "cost": 0, "total_cost": 0.0}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745194665} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745194715} +{"event": "message_send", "properties": {"main_model": "gemini/gemini-2.5-pro-exp-03-25", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "udiff-simple", "prompt_tokens": 18087, "completion_tokens": 253, "total_tokens": 18340, "cost": 0, "total_cost": 0.0}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745194725} +{"event": "command_model", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745194753} +{"event": "exit", "properties": {"reason": "Control-C"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745194758} +{"event": "command_clear", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745194776} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745194789} +{"event": "message_send", "properties": {"main_model": "o3", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "diff", "prompt_tokens": 19552, "completion_tokens": 1375, "total_tokens": 20927, "cost": 0.25052, "total_cost": 0.25052}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745194806} +{"event": "command_undo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745194833} +{"event": "command_model", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745194837} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745194840} +{"event": "message_send", "properties": {"main_model": "openrouter/anthropic/claude-3.7-sonnet", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "diff", "prompt_tokens": 20025, "completion_tokens": 286, "total_tokens": 20311, "cost": 0.064365, "total_cost": 0.314885}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745194850} +{"event": "command_think-tokens", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745194889} +{"event": "command_undo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745194910} +{"event": "command_clear", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745194912} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745194925} +{"event": "message_send", "properties": {"main_model": "openrouter/anthropic/claude-3.7-sonnet", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "diff", "prompt_tokens": 19887, "completion_tokens": 819, "total_tokens": 20706, "cost": 0.071946, "total_cost": 0.38683100000000004}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745194945} +{"event": "command_undo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745194972} +{"event": "command_clear", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745194973} +{"event": "command_model", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745194975} +{"event": "command_reasoning-effort", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745194980} +{"event": "command_clear", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745194982} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745194985} +{"event": "message_send", "properties": {"main_model": "o3", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "diff", "prompt_tokens": 19561, "completion_tokens": 3339, "total_tokens": 22900, "cost": 0.32917, "total_cost": 0.7160010000000001}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745195020} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745195108} +{"event": "message_send", "properties": {"main_model": "o3", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "diff", "prompt_tokens": 19855, "completion_tokens": 876, "total_tokens": 20731, "cost": 0.23359000000000002, "total_cost": 0.9495910000000001}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745195117} +{"event": "message_send_starting", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745195161} +{"event": "message_send", "properties": {"main_model": "o3", "weak_model": "gemini/gemini-2.0-flash", "editor_model": "gemini/gemini-2.5-pro-exp-03-25", "edit_format": "diff", "prompt_tokens": 20114, "completion_tokens": 1504, "total_tokens": 21618, "cost": 0.26130000000000003, "total_cost": 1.2108910000000002}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745195174} +{"event": "command_undo", "properties": {}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745195213} +{"event": "exit", "properties": {"reason": "Control-C"}, "user_id": "c42c4e6b-f054-44d7-ae1f-6726cc41da88", "time": 1745195363} diff --git a/aider/website/assets/sample.aider.conf.yml b/aider/website/assets/sample.aider.conf.yml index f14860e26..f5f902c9a 100644 --- a/aider/website/assets/sample.aider.conf.yml +++ b/aider/website/assets/sample.aider.conf.yml @@ -20,39 +20,6 @@ ## Specify the model to use for the main chat #model: xxx -## Use claude-3-opus-20240229 model for the main chat -#opus: false - -## Use anthropic/claude-3-7-sonnet-20250219 model for the main chat -#sonnet: false - -## Use claude-3-5-haiku-20241022 model for the main chat -#haiku: false - -## Use gpt-4-0613 model for the main chat -#4: false - -## Use gpt-4o model for the main chat -#4o: false - -## Use gpt-4o-mini model for the main chat -#mini: false - -## Use gpt-4-1106-preview model for the main chat -#4-turbo: false - -## Use gpt-3.5-turbo model for the main chat -#35turbo: false - -## Use deepseek/deepseek-chat model for the main chat -#deepseek: false - -## Use o1-mini model for the main chat -#o1-mini: false - -## Use o1-preview model for the main chat -#o1-preview: false - ######################## # API Keys and settings: @@ -116,6 +83,9 @@ ## Set the reasoning_effort API parameter (default: not set) #reasoning-effort: xxx +## Set the thinking token budget for models that support it (default: not set) +#thinking-tokens: xxx + ## Verify the SSL cert when connecting to models (default: True) #verify-ssl: true @@ -128,6 +98,9 @@ ## Use architect edit format for the main chat #architect: false +## Enable/disable automatic acceptance of architect changes (default: True) +#auto-accept-architect: true + ## Specify the model to use for commit messages and chat history summarization (default depends on --model) #weak-model: xxx @@ -140,6 +113,9 @@ ## Only work with models that have meta-data available (default: True) #show-model-warnings: true +## Check if model accepts settings like reasoning_effort/thinking_tokens (default: True) +#check-model-accepts-settings: true + ## Soft limit on tokens for chat history, after which summarization begins. If unspecified, defaults to the model's max_chat_history_tokens. #max-chat-history-tokens: xxx @@ -195,31 +171,31 @@ #stream: true ## Set the color for user input (default: #00cc00) -#user-input-color: #00cc00 +#user-input-color: "#00cc00" ## Set the color for tool output (default: None) -#tool-output-color: xxx +#tool-output-color: "xxx" ## Set the color for tool error messages (default: #FF2222) -#tool-error-color: #FF2222 +#tool-error-color: "#FF2222" ## Set the color for tool warning messages (default: #FFA500) -#tool-warning-color: #FFA500 +#tool-warning-color: "#FFA500" ## Set the color for assistant output (default: #0088ff) -#assistant-output-color: #0088ff +#assistant-output-color: "#0088ff" ## Set the color for the completion menu (default: terminal's default text color) -#completion-menu-color: xxx +#completion-menu-color: "xxx" ## Set the background color for the completion menu (default: terminal's default background color) -#completion-menu-bg-color: xxx +#completion-menu-bg-color: "xxx" ## Set the color for the current item in the completion menu (default: terminal's default background color) -#completion-menu-current-color: xxx +#completion-menu-current-color: "xxx" ## Set the background color for the current item in the completion menu (default: terminal's default text color) -#completion-menu-current-bg-color: xxx +#completion-menu-current-bg-color: "xxx" ## Set the markdown code theme (default: default, other options include monokai, solarized-dark, solarized-light, or a Pygments builtin style, see https://pygments.org/styles for available themes) #code-theme: default @@ -260,6 +236,9 @@ ## Prefix all commit messages with 'aider: ' (default: False) #attribute-commit-message-committer: false +## Enable/disable git pre-commit hooks with --no-verify (default: False) +#git-commit-verify: false + ## Commit all pending changes with a suitable commit message, then exit #commit: false @@ -431,11 +410,50 @@ ## Enable/disable multi-line input mode with Meta-Enter to submit (default: False) #multiline: false +## Enable/disable terminal bell notifications when LLM responses are ready (default: False) +#notifications: false + +## Specify a command to run for notifications instead of the terminal bell. If not specified, a default command for your OS may be used. +#notifications-command: xxx + ## Enable/disable detection and offering to add URLs to chat (default: True) #detect-urls: true ## Specify which editor to use for the /editor command #editor: xxx -## Install the tree_sitter_language_pack (experimental) -#install-tree-sitter-language-pack: false +############################ +# Deprecated model settings: + +## Use claude-3-opus-20240229 model for the main chat (deprecated, use --model) +#opus: false + +## Use anthropic/claude-3-7-sonnet-20250219 model for the main chat (deprecated, use --model) +#sonnet: false + +## Use claude-3-5-haiku-20241022 model for the main chat (deprecated, use --model) +#haiku: false + +## Use gpt-4-0613 model for the main chat (deprecated, use --model) +#4: false + +## Use gpt-4o model for the main chat (deprecated, use --model) +#4o: false + +## Use gpt-4o-mini model for the main chat (deprecated, use --model) +#mini: false + +## Use gpt-4-1106-preview model for the main chat (deprecated, use --model) +#4-turbo: false + +## Use gpt-3.5-turbo model for the main chat (deprecated, use --model) +#35turbo: false + +## Use deepseek/deepseek-chat model for the main chat (deprecated, use --model) +#deepseek: false + +## Use o1-mini model for the main chat (deprecated, use --model) +#o1-mini: false + +## Use o1-preview model for the main chat (deprecated, use --model) +#o1-preview: false diff --git a/aider/website/assets/sample.env b/aider/website/assets/sample.env index 61b852798..439e637de 100644 --- a/aider/website/assets/sample.env +++ b/aider/website/assets/sample.env @@ -24,39 +24,6 @@ ## Specify the model to use for the main chat #AIDER_MODEL= -## Use claude-3-opus-20240229 model for the main chat -#AIDER_OPUS= - -## Use anthropic/claude-3-7-sonnet-20250219 model for the main chat -#AIDER_SONNET= - -## Use claude-3-5-haiku-20241022 model for the main chat -#AIDER_HAIKU= - -## Use gpt-4-0613 model for the main chat -#AIDER_4= - -## Use gpt-4o model for the main chat -#AIDER_4O= - -## Use gpt-4o-mini model for the main chat -#AIDER_MINI= - -## Use gpt-4-1106-preview model for the main chat -#AIDER_4_TURBO= - -## Use gpt-3.5-turbo model for the main chat -#AIDER_35TURBO= - -## Use deepseek/deepseek-chat model for the main chat -#AIDER_DEEPSEEK= - -## Use o1-mini model for the main chat -#AIDER_O1_MINI= - -## Use o1-preview model for the main chat -#AIDER_O1_PREVIEW= - ######################## # API Keys and settings: @@ -105,6 +72,9 @@ ## Set the reasoning_effort API parameter (default: not set) #AIDER_REASONING_EFFORT= +## Set the thinking token budget for models that support it (default: not set) +#AIDER_THINKING_TOKENS= + ## Verify the SSL cert when connecting to models (default: True) #AIDER_VERIFY_SSL=true @@ -117,6 +87,9 @@ ## Use architect edit format for the main chat #AIDER_ARCHITECT= +## Enable/disable automatic acceptance of architect changes (default: True) +#AIDER_AUTO_ACCEPT_ARCHITECT=true + ## Specify the model to use for commit messages and chat history summarization (default depends on --model) #AIDER_WEAK_MODEL= @@ -129,6 +102,9 @@ ## Only work with models that have meta-data available (default: True) #AIDER_SHOW_MODEL_WARNINGS=true +## Check if model accepts settings like reasoning_effort/thinking_tokens (default: True) +#AIDER_CHECK_MODEL_ACCEPTS_SETTINGS=true + ## Soft limit on tokens for chat history, after which summarization begins. If unspecified, defaults to the model's max_chat_history_tokens. #AIDER_MAX_CHAT_HISTORY_TOKENS= @@ -249,6 +225,9 @@ ## Prefix all commit messages with 'aider: ' (default: False) #AIDER_ATTRIBUTE_COMMIT_MESSAGE_COMMITTER=false +## Enable/disable git pre-commit hooks with --no-verify (default: False) +#AIDER_GIT_COMMIT_VERIFY=false + ## Commit all pending changes with a suitable commit message, then exit #AIDER_COMMIT=false @@ -399,11 +378,50 @@ ## Enable/disable multi-line input mode with Meta-Enter to submit (default: False) #AIDER_MULTILINE=false +## Enable/disable terminal bell notifications when LLM responses are ready (default: False) +#AIDER_NOTIFICATIONS=false + +## Specify a command to run for notifications instead of the terminal bell. If not specified, a default command for your OS may be used. +#AIDER_NOTIFICATIONS_COMMAND= + ## Enable/disable detection and offering to add URLs to chat (default: True) #AIDER_DETECT_URLS=true ## Specify which editor to use for the /editor command #AIDER_EDITOR= -## Install the tree_sitter_language_pack (experimental) -#AIDER_INSTALL_TREE_SITTER_LANGUAGE_PACK=false +############################ +# Deprecated model settings: + +## Use claude-3-opus-20240229 model for the main chat (deprecated, use --model) +#AIDER_OPUS=false + +## Use anthropic/claude-3-7-sonnet-20250219 model for the main chat (deprecated, use --model) +#AIDER_SONNET=false + +## Use claude-3-5-haiku-20241022 model for the main chat (deprecated, use --model) +#AIDER_HAIKU=false + +## Use gpt-4-0613 model for the main chat (deprecated, use --model) +#AIDER_4=false + +## Use gpt-4o model for the main chat (deprecated, use --model) +#AIDER_4O=false + +## Use gpt-4o-mini model for the main chat (deprecated, use --model) +#AIDER_MINI=false + +## Use gpt-4-1106-preview model for the main chat (deprecated, use --model) +#AIDER_4_TURBO=false + +## Use gpt-3.5-turbo model for the main chat (deprecated, use --model) +#AIDER_35TURBO=false + +## Use deepseek/deepseek-chat model for the main chat (deprecated, use --model) +#AIDER_DEEPSEEK=false + +## Use o1-mini model for the main chat (deprecated, use --model) +#AIDER_O1_MINI=false + +## Use o1-preview model for the main chat (deprecated, use --model) +#AIDER_O1_PREVIEW=false diff --git a/aider/website/assets/thinking.jpg b/aider/website/assets/thinking.jpg new file mode 100644 index 000000000..159c894f4 Binary files /dev/null and b/aider/website/assets/thinking.jpg differ diff --git a/aider/website/docs/config/adv-model-settings.md b/aider/website/docs/config/adv-model-settings.md index bd605f4a4..5bea4bd7c 100644 --- a/aider/website/docs/config/adv-model-settings.md +++ b/aider/website/docs/config/adv-model-settings.md @@ -84,7 +84,21 @@ Files loaded last will take priority. The yaml file should be a list of dictionary objects for each model. -### Global extra params +### Passing extra params to litellm.completion + +The `extra_params` attribute of model settings is used to pass arbitrary +extra parameters to the `litellm.completion()` call when sending data +to the given model. + +For example: + +```yaml +- name: some-provider/my-special-model + extra_params: + extra_headers: + Custom-Header: value + max_tokens: 8192 +``` You can use the special model name `aider/extra_params` to define `extra_params` that will be passed to `litellm.completion()` for all models. @@ -103,40 +117,6 @@ For example: These settings will be merged with any model-specific settings, with the `aider/extra_params` settings taking precedence for any direct conflicts. -### Controlling o1 reasoning effort - -You need this chunk of yaml: - -``` - extra_params: - extra_body: - reasoning_effort: high -``` - -This is a full entry for o1 with that setting, obtained by finding the default -entry in the list below and adding the above `extra_params` entry: - -``` -- name: o1 - edit_format: diff - weak_model_name: gpt-4o-mini - use_repo_map: true - send_undo_reply: false - lazy: false - reminder: user - examples_as_sys_msg: false - cache_control: false - caches_by_default: false - use_system_prompt: true - use_temperature: false - streaming: false - editor_model_name: gpt-4o - editor_edit_format: editor-diff - extra_params: - extra_body: - reasoning_effort: high -``` - ### Default model settings Below are all the pre-configured model settings to give a sense for the settings which are supported. @@ -162,6 +142,7 @@ cog.out("```\n") use_repo_map: false send_undo_reply: false lazy: false + overeager: false reminder: user examples_as_sys_msg: false extra_params: null @@ -172,8 +153,10 @@ cog.out("```\n") streaming: true editor_model_name: null editor_edit_format: null + reasoning_tag: null remove_reasoning: null system_prompt_prefix: null + accepts_settings: null - name: anthropic/claude-3-5-haiku-20241022 edit_format: diff @@ -227,6 +210,7 @@ cog.out("```\n") edit_format: diff weak_model_name: anthropic/claude-3-5-haiku-20241022 use_repo_map: true + overeager: true examples_as_sys_msg: true extra_params: extra_headers: @@ -235,11 +219,14 @@ cog.out("```\n") cache_control: true editor_model_name: anthropic/claude-3-7-sonnet-20250219 editor_edit_format: editor-diff + accepts_settings: + - thinking_tokens - name: anthropic/claude-3-7-sonnet-latest edit_format: diff weak_model_name: anthropic/claude-3-5-haiku-20241022 use_repo_map: true + overeager: true examples_as_sys_msg: true extra_params: extra_headers: @@ -248,6 +235,8 @@ cog.out("```\n") cache_control: true editor_model_name: anthropic/claude-3-7-sonnet-latest editor_edit_format: editor-diff + accepts_settings: + - thinking_tokens - name: anthropic/claude-3-haiku-20240307 weak_model_name: anthropic/claude-3-haiku-20240307 @@ -257,6 +246,18 @@ cog.out("```\n") anthropic-beta: prompt-caching-2024-07-31,pdfs-2024-09-25 cache_control: true +- name: azure/gpt-4.1 + edit_format: diff + weak_model_name: azure/gpt-4.1-mini + use_repo_map: true + reminder: sys + editor_model_name: azure/gpt-4.1-mini + +- name: azure/gpt-4.1-mini + edit_format: diff + use_repo_map: true + reminder: sys + - name: azure/o1 edit_format: diff weak_model_name: azure/gpt-4o-mini @@ -265,6 +266,8 @@ cog.out("```\n") streaming: false editor_model_name: azure/gpt-4o editor_edit_format: editor-diff + accepts_settings: + - reasoning_effort - name: azure/o1-mini weak_model_name: azure/gpt-4o-mini @@ -283,6 +286,18 @@ cog.out("```\n") editor_model_name: azure/gpt-4o editor_edit_format: editor-diff +- name: azure/o3 + edit_format: diff + weak_model_name: azure/gpt-4.1-mini + use_repo_map: true + examples_as_sys_msg: true + streaming: false + editor_model_name: azure/gpt-4.1 + editor_edit_format: editor-diff + system_prompt_prefix: 'Formatting re-enabled. ' + accepts_settings: + - reasoning_effort + - name: azure/o3-mini edit_format: diff weak_model_name: azure/gpt-4o-mini @@ -291,6 +306,68 @@ cog.out("```\n") editor_model_name: azure/gpt-4o editor_edit_format: editor-diff system_prompt_prefix: 'Formatting re-enabled. ' + accepts_settings: + - reasoning_effort + +- name: azure/o4-mini + edit_format: diff + weak_model_name: azure/gpt-4.1-mini + use_repo_map: true + examples_as_sys_msg: true + use_temperature: false + editor_model_name: azure/gpt-4.1 + editor_edit_format: editor-diff + system_prompt_prefix: 'Formatting re-enabled. ' + accepts_settings: + - reasoning_effort + +- name: azure/o4-mini + edit_format: diff + weak_model_name: azure/gpt-4.1-mini + use_repo_map: true + examples_as_sys_msg: true + use_temperature: false + editor_model_name: azure/gpt-4.1 + editor_edit_format: editor-diff + system_prompt_prefix: 'Formatting re-enabled. ' + accepts_settings: + - reasoning_effort + +- name: azure/o4-mini + edit_format: diff + weak_model_name: azure/gpt-4.1-mini + use_repo_map: true + examples_as_sys_msg: true + use_temperature: false + editor_model_name: azure/gpt-4.1 + editor_edit_format: editor-diff + system_prompt_prefix: 'Formatting re-enabled. ' + accepts_settings: + - reasoning_effort + +- name: azure/o4-mini + edit_format: diff + weak_model_name: azure/gpt-4.1-mini + use_repo_map: true + examples_as_sys_msg: true + use_temperature: false + editor_model_name: azure/gpt-4.1 + editor_edit_format: editor-diff + system_prompt_prefix: 'Formatting re-enabled. ' + accepts_settings: + - reasoning_effort + +- name: azure/o4-mini + edit_format: diff + weak_model_name: azure/gpt-4.1-mini + use_repo_map: true + examples_as_sys_msg: true + use_temperature: false + editor_model_name: azure/gpt-4.1 + editor_edit_format: editor-diff + system_prompt_prefix: 'Formatting re-enabled. ' + accepts_settings: + - reasoning_effort - name: bedrock/anthropic.claude-3-5-haiku-20241022-v1:0 edit_format: diff @@ -318,6 +395,7 @@ cog.out("```\n") edit_format: diff weak_model_name: bedrock/anthropic.claude-3-5-haiku-20241022-v1:0 use_repo_map: true + overeager: true examples_as_sys_msg: true extra_params: extra_headers: @@ -326,11 +404,14 @@ cog.out("```\n") cache_control: true editor_model_name: bedrock/anthropic.claude-3-7-sonnet-20250219-v1:0 editor_edit_format: editor-diff + accepts_settings: + - thinking_tokens - name: bedrock/us.anthropic.claude-3-7-sonnet-20250219-v1:0 edit_format: diff weak_model_name: bedrock/us.anthropic.claude-3-5-haiku-20241022-v1:0 use_repo_map: true + overeager: true examples_as_sys_msg: true extra_params: extra_headers: @@ -339,11 +420,14 @@ cog.out("```\n") cache_control: true editor_model_name: bedrock/us.anthropic.claude-3-7-sonnet-20250219-v1:0 editor_edit_format: editor-diff + accepts_settings: + - thinking_tokens - name: bedrock_converse/anthropic.claude-3-7-sonnet-20250219-v1:0 edit_format: diff weak_model_name: bedrock_converse/anthropic.claude-3-5-haiku-20241022-v1:0 use_repo_map: true + overeager: true examples_as_sys_msg: true extra_params: extra_headers: @@ -352,11 +436,14 @@ cog.out("```\n") cache_control: true editor_model_name: bedrock_converse/anthropic.claude-3-7-sonnet-20250219-v1:0 editor_edit_format: editor-diff + accepts_settings: + - thinking_tokens - name: bedrock_converse/us.anthropic.claude-3-7-sonnet-20250219-v1:0 edit_format: diff weak_model_name: bedrock_converse/us.anthropic.claude-3-5-haiku-20241022-v1:0 use_repo_map: true + overeager: true examples_as_sys_msg: true extra_params: extra_headers: @@ -365,6 +452,8 @@ cog.out("```\n") cache_control: true editor_model_name: bedrock_converse/us.anthropic.claude-3-7-sonnet-20250219-v1:0 editor_edit_format: editor-diff + accepts_settings: + - thinking_tokens - name: claude-3-5-haiku-20241022 edit_format: diff @@ -414,11 +503,14 @@ cog.out("```\n") cache_control: true editor_model_name: claude-3-7-sonnet-20250219 editor_edit_format: editor-diff + accepts_settings: + - thinking_tokens - name: claude-3-7-sonnet-latest edit_format: diff weak_model_name: claude-3-5-haiku-20241022 use_repo_map: true + overeager: true examples_as_sys_msg: true extra_params: extra_headers: @@ -427,6 +519,8 @@ cog.out("```\n") cache_control: true editor_model_name: claude-3-7-sonnet-latest editor_edit_format: editor-diff + accepts_settings: + - thinking_tokens - name: claude-3-haiku-20240307 weak_model_name: claude-3-haiku-20240307 @@ -444,6 +538,9 @@ cog.out("```\n") - name: claude-3-sonnet-20240229 weak_model_name: claude-3-5-haiku-20241022 +- name: cohere_chat/command-a-03-2025 + examples_as_sys_msg: true + - name: command-r-08-2024 weak_model_name: command-r-08-2024 use_repo_map: true @@ -512,7 +609,7 @@ cog.out("```\n") use_temperature: false editor_model_name: fireworks_ai/accounts/fireworks/models/deepseek-v3 editor_edit_format: editor-diff - remove_reasoning: think + reasoning_tag: think - name: fireworks_ai/accounts/fireworks/models/deepseek-v3 edit_format: diff @@ -522,6 +619,33 @@ cog.out("```\n") extra_params: max_tokens: 128000 +- name: fireworks_ai/accounts/fireworks/models/deepseek-v3-0324 + edit_format: diff + use_repo_map: true + reminder: sys + examples_as_sys_msg: true + extra_params: + max_tokens: 160000 + +- name: fireworks_ai/accounts/fireworks/models/qwq-32b + edit_format: diff + weak_model_name: fireworks_ai/accounts/fireworks/models/qwen2p5-coder-32b-instruct + use_repo_map: true + examples_as_sys_msg: true + extra_params: + max_tokens: 32000 + top_p: 0.95 + use_temperature: 0.6 + editor_model_name: fireworks_ai/accounts/fireworks/models/qwen2p5-coder-32b-instruct + editor_edit_format: editor-diff + reasoning_tag: think + +- name: gemini-2.5-flash-preview-04-17 + edit_format: diff + use_repo_map: true + accepts_settings: + - thinking_tokens + - name: gemini/gemini-1.5-flash-002 - name: gemini/gemini-1.5-flash-exp-0827 @@ -550,6 +674,22 @@ cog.out("```\n") edit_format: diff use_repo_map: true +- name: gemini/gemini-2.5-flash-preview-04-17 + edit_format: diff + use_repo_map: true + accepts_settings: + - thinking_tokens + +- name: gemini/gemini-2.5-pro-exp-03-25 + edit_format: diff-fenced + weak_model_name: gemini/gemini-2.5-flash-preview-04-17 + use_repo_map: true + +- name: gemini/gemini-2.5-pro-preview-03-25 + edit_format: diff-fenced + weak_model_name: gemini/gemini-2.0-flash + use_repo_map: true + - name: gemini/gemini-exp-1114 edit_format: diff use_repo_map: true @@ -562,6 +702,9 @@ cog.out("```\n") edit_format: diff use_repo_map: true +- name: gemini/gemma-3-27b-it + use_system_prompt: false + - name: gpt-3.5-turbo weak_model_name: gpt-4o-mini reminder: sys @@ -636,6 +779,28 @@ cog.out("```\n") use_repo_map: true reminder: sys +- name: gpt-4.1 + edit_format: diff + weak_model_name: gpt-4.1-mini + use_repo_map: true + reminder: sys + editor_model_name: gpt-4.1-mini + +- name: gpt-4.1-mini + edit_format: diff + use_repo_map: true + reminder: sys + +- name: gpt-4.5-preview + edit_format: diff + weak_model_name: gpt-4o-mini + use_repo_map: true + lazy: true + reminder: sys + examples_as_sys_msg: true + editor_model_name: gpt-4o + editor_edit_format: editor-diff + - name: gpt-4o edit_format: diff weak_model_name: gpt-4o-mini @@ -671,6 +836,18 @@ cog.out("```\n") weak_model_name: groq/llama3-8b-8192 examples_as_sys_msg: true +- name: groq/qwen-qwq-32b + edit_format: diff + weak_model_name: groq/qwen-2.5-coder-32b + use_repo_map: true + extra_params: + max_tokens: 128000 + top_p: 0.95 + use_temperature: 0.6 + editor_model_name: groq/qwen-2.5-coder-32b + editor_edit_format: editor-diff + reasoning_tag: think + - name: o1 edit_format: diff weak_model_name: gpt-4o-mini @@ -680,6 +857,8 @@ cog.out("```\n") editor_model_name: gpt-4o editor_edit_format: editor-diff system_prompt_prefix: 'Formatting re-enabled. ' + accepts_settings: + - reasoning_effort - name: o1-mini weak_model_name: gpt-4o-mini @@ -698,6 +877,18 @@ cog.out("```\n") editor_model_name: gpt-4o editor_edit_format: editor-diff +- name: o3 + edit_format: diff + weak_model_name: gpt-4.1-mini + use_repo_map: true + examples_as_sys_msg: true + streaming: false + editor_model_name: gpt-4.1 + editor_edit_format: editor-diff + system_prompt_prefix: 'Formatting re-enabled. ' + accepts_settings: + - reasoning_effort + - name: o3-mini edit_format: diff weak_model_name: gpt-4o-mini @@ -706,6 +897,42 @@ cog.out("```\n") editor_model_name: gpt-4o editor_edit_format: editor-diff system_prompt_prefix: 'Formatting re-enabled. ' + accepts_settings: + - reasoning_effort + +- name: o4-mini + edit_format: diff + weak_model_name: gpt-4.1-mini + use_repo_map: true + examples_as_sys_msg: true + use_temperature: false + editor_model_name: gpt-4.1 + editor_edit_format: editor-diff + system_prompt_prefix: 'Formatting re-enabled. ' + accepts_settings: + - reasoning_effort + +- name: openai/gpt-4.1 + edit_format: diff + weak_model_name: openai/gpt-4.1-mini + use_repo_map: true + reminder: sys + editor_model_name: openai/gpt-4.1-mini + +- name: openai/gpt-4.1-mini + edit_format: diff + use_repo_map: true + reminder: sys + +- name: openai/gpt-4.5-preview + edit_format: diff + weak_model_name: gpt-4o-mini + use_repo_map: true + lazy: true + reminder: sys + examples_as_sys_msg: true + editor_model_name: openai/gpt-4o + editor_edit_format: editor-diff - name: openai/gpt-4o edit_format: diff @@ -746,6 +973,8 @@ cog.out("```\n") editor_model_name: openai/gpt-4o editor_edit_format: editor-diff system_prompt_prefix: 'Formatting re-enabled. ' + accepts_settings: + - reasoning_effort - name: openai/o1-mini weak_model_name: openai/gpt-4o-mini @@ -764,6 +993,18 @@ cog.out("```\n") editor_model_name: openai/gpt-4o editor_edit_format: editor-diff +- name: openai/o3 + edit_format: diff + weak_model_name: openai/gpt-4.1-mini + use_repo_map: true + examples_as_sys_msg: true + streaming: false + editor_model_name: openai/gpt-4.1 + editor_edit_format: editor-diff + system_prompt_prefix: 'Formatting re-enabled. ' + accepts_settings: + - reasoning_effort + - name: openai/o3-mini edit_format: diff weak_model_name: gpt-4o-mini @@ -772,6 +1013,68 @@ cog.out("```\n") editor_model_name: gpt-4o editor_edit_format: editor-diff system_prompt_prefix: 'Formatting re-enabled. ' + accepts_settings: + - reasoning_effort + +- name: openai/o4-mini + edit_format: diff + weak_model_name: openai/gpt-4.1-mini + use_repo_map: true + examples_as_sys_msg: true + use_temperature: false + editor_model_name: openai/gpt-4.1 + editor_edit_format: editor-diff + system_prompt_prefix: 'Formatting re-enabled. ' + accepts_settings: + - reasoning_effort + +- name: openai/o4-mini + edit_format: diff + weak_model_name: openai/gpt-4.1-mini + use_repo_map: true + examples_as_sys_msg: true + use_temperature: false + editor_model_name: openai/gpt-4.1 + editor_edit_format: editor-diff + system_prompt_prefix: 'Formatting re-enabled. ' + accepts_settings: + - reasoning_effort + +- name: openai/o4-mini + edit_format: diff + weak_model_name: openai/gpt-4.1-mini + use_repo_map: true + examples_as_sys_msg: true + use_temperature: false + editor_model_name: openai/gpt-4.1 + editor_edit_format: editor-diff + system_prompt_prefix: 'Formatting re-enabled. ' + accepts_settings: + - reasoning_effort + +- name: openai/o4-mini + edit_format: diff + weak_model_name: openai/gpt-4.1-mini + use_repo_map: true + examples_as_sys_msg: true + use_temperature: false + editor_model_name: openai/gpt-4.1 + editor_edit_format: editor-diff + system_prompt_prefix: 'Formatting re-enabled. ' + accepts_settings: + - reasoning_effort + +- name: openai/o4-mini + edit_format: diff + weak_model_name: openai/gpt-4.1-mini + use_repo_map: true + examples_as_sys_msg: true + use_temperature: false + editor_model_name: openai/gpt-4.1 + editor_edit_format: editor-diff + system_prompt_prefix: 'Formatting re-enabled. ' + accepts_settings: + - reasoning_effort - name: openrouter/anthropic/claude-3-opus edit_format: diff @@ -804,6 +1107,7 @@ cog.out("```\n") edit_format: diff weak_model_name: openrouter/anthropic/claude-3-5-haiku use_repo_map: true + overeager: true examples_as_sys_msg: true extra_params: extra_headers: @@ -812,11 +1116,14 @@ cog.out("```\n") cache_control: true editor_model_name: openrouter/anthropic/claude-3.7-sonnet editor_edit_format: editor-diff + accepts_settings: + - thinking_tokens - name: openrouter/anthropic/claude-3.7-sonnet:beta edit_format: diff weak_model_name: openrouter/anthropic/claude-3-5-haiku use_repo_map: true + overeager: true examples_as_sys_msg: true extra_params: extra_headers: @@ -825,6 +1132,11 @@ cog.out("```\n") cache_control: true editor_model_name: openrouter/anthropic/claude-3.7-sonnet editor_edit_format: editor-diff + accepts_settings: + - thinking_tokens + +- name: openrouter/cohere/command-a-03-2025 + examples_as_sys_msg: true - name: openrouter/deepseek/deepseek-chat edit_format: diff @@ -832,6 +1144,37 @@ cog.out("```\n") reminder: sys examples_as_sys_msg: true +- name: openrouter/deepseek/deepseek-chat-v3-0324 + edit_format: diff + use_repo_map: true + reminder: sys + examples_as_sys_msg: true + extra_params: + max_tokens: 8192 + caches_by_default: true + +- name: openrouter/deepseek/deepseek-chat-v3-0324:free + edit_format: diff + weak_model_name: openrouter/deepseek/deepseek-chat-v3-0324:free + use_repo_map: true + examples_as_sys_msg: true + caches_by_default: true + use_temperature: false + editor_model_name: openrouter/deepseek/deepseek-r1:free + editor_edit_format: editor-diff + +- name: openrouter/deepseek/deepseek-chat:free + edit_format: diff + weak_model_name: openrouter/deepseek/deepseek-chat:free + use_repo_map: true + examples_as_sys_msg: true + extra_params: + max_tokens: 8192 + caches_by_default: true + use_temperature: false + editor_model_name: openrouter/deepseek/deepseek-chat:free + editor_edit_format: editor-diff + - name: openrouter/deepseek/deepseek-coder edit_format: diff use_repo_map: true @@ -845,8 +1188,8 @@ cog.out("```\n") examples_as_sys_msg: true extra_params: max_tokens: 8192 + include_reasoning: true caches_by_default: true - use_temperature: false editor_model_name: openrouter/deepseek/deepseek-chat editor_edit_format: editor-diff @@ -870,15 +1213,35 @@ cog.out("```\n") extra_params: max_tokens: 8192 caches_by_default: true - use_temperature: false - editor_model_name: openrouter/deepseek/deepseek-r1:free - editor_edit_format: editor-diff + +- name: openrouter/google/gemini-2.5-pro-exp-03-25:free + edit_format: diff-fenced + weak_model_name: openrouter/google/gemini-2.0-flash-exp:free + use_repo_map: true + +- name: openrouter/google/gemma-3-27b-it + use_system_prompt: false + +- name: openrouter/google/gemma-3-27b-it:free + use_system_prompt: false - name: openrouter/meta-llama/llama-3-70b-instruct edit_format: diff weak_model_name: openrouter/meta-llama/llama-3-70b-instruct examples_as_sys_msg: true +- name: openrouter/openai/gpt-4.1 + edit_format: diff + weak_model_name: openrouter/openai/gpt-4.1-mini + use_repo_map: true + reminder: sys + editor_model_name: openrouter/openai/gpt-4.1-mini + +- name: openrouter/openai/gpt-4.1-mini + edit_format: diff + use_repo_map: true + reminder: sys + - name: openrouter/openai/gpt-4o edit_format: diff weak_model_name: openrouter/openai/gpt-4o-mini @@ -897,6 +1260,8 @@ cog.out("```\n") editor_model_name: openrouter/openai/gpt-4o editor_edit_format: editor-diff system_prompt_prefix: 'Formatting re-enabled. ' + accepts_settings: + - reasoning_effort - name: openrouter/openai/o1-mini weak_model_name: openrouter/openai/gpt-4o-mini @@ -917,6 +1282,18 @@ cog.out("```\n") editor_model_name: openrouter/openai/gpt-4o editor_edit_format: editor-diff +- name: openrouter/openai/o3 + edit_format: diff + weak_model_name: openrouter/openai/gpt-4.1-mini + use_repo_map: true + examples_as_sys_msg: true + streaming: false + editor_model_name: openrouter/openai/gpt-4.1 + editor_edit_format: editor-diff + system_prompt_prefix: 'Formatting re-enabled. ' + accepts_settings: + - reasoning_effort + - name: openrouter/openai/o3-mini edit_format: diff weak_model_name: openrouter/openai/gpt-4o-mini @@ -925,6 +1302,8 @@ cog.out("```\n") editor_model_name: openrouter/openai/gpt-4o editor_edit_format: editor-diff system_prompt_prefix: 'Formatting re-enabled. ' + accepts_settings: + - reasoning_effort - name: openrouter/openai/o3-mini-high edit_format: diff @@ -934,6 +1313,78 @@ cog.out("```\n") editor_model_name: openrouter/openai/gpt-4o editor_edit_format: editor-diff system_prompt_prefix: 'Formatting re-enabled. ' + accepts_settings: + - reasoning_effort + +- name: openrouter/openai/o4-mini + edit_format: diff + weak_model_name: openrouter/openai/gpt-4.1-mini + use_repo_map: true + examples_as_sys_msg: true + use_temperature: false + editor_model_name: openrouter/openai/gpt-4.1 + editor_edit_format: editor-diff + system_prompt_prefix: 'Formatting re-enabled. ' + accepts_settings: + - reasoning_effort + +- name: openrouter/openai/o4-mini + edit_format: diff + weak_model_name: openrouter/openai/gpt-4.1-mini + use_repo_map: true + examples_as_sys_msg: true + use_temperature: false + editor_model_name: openrouter/openai/gpt-4.1 + editor_edit_format: editor-diff + system_prompt_prefix: 'Formatting re-enabled. ' + accepts_settings: + - reasoning_effort + +- name: openrouter/openai/o4-mini + edit_format: diff + weak_model_name: openrouter/openai/gpt-4.1-mini + use_repo_map: true + examples_as_sys_msg: true + use_temperature: false + editor_model_name: openrouter/openai/gpt-4.1 + editor_edit_format: editor-diff + system_prompt_prefix: 'Formatting re-enabled. ' + accepts_settings: + - reasoning_effort + +- name: openrouter/openai/o4-mini + edit_format: diff + weak_model_name: openrouter/openai/gpt-4.1-mini + use_repo_map: true + examples_as_sys_msg: true + use_temperature: false + editor_model_name: openrouter/openai/gpt-4.1 + editor_edit_format: editor-diff + system_prompt_prefix: 'Formatting re-enabled. ' + accepts_settings: + - reasoning_effort + +- name: openrouter/openai/o4-mini + edit_format: diff + weak_model_name: openrouter/openai/gpt-4.1-mini + use_repo_map: true + examples_as_sys_msg: true + use_temperature: false + editor_model_name: openrouter/openai/gpt-4.1 + editor_edit_format: editor-diff + system_prompt_prefix: 'Formatting re-enabled. ' + accepts_settings: + - reasoning_effort + +- name: openrouter/openrouter/optimus-alpha + edit_format: diff + use_repo_map: true + examples_as_sys_msg: true + +- name: openrouter/openrouter/quasar-alpha + edit_format: diff + use_repo_map: true + examples_as_sys_msg: true - name: openrouter/qwen/qwen-2.5-coder-32b-instruct edit_format: diff @@ -942,15 +1393,42 @@ cog.out("```\n") editor_model_name: openrouter/qwen/qwen-2.5-coder-32b-instruct editor_edit_format: editor-diff +- name: openrouter/x-ai/grok-3-beta + edit_format: diff + use_repo_map: true + +- name: openrouter/x-ai/grok-3-fast-beta + edit_format: diff + use_repo_map: true + +- name: openrouter/x-ai/grok-3-mini-beta + use_repo_map: true + accepts_settings: + - reasoning_effort + +- name: openrouter/x-ai/grok-3-mini-fast-beta + use_repo_map: true + accepts_settings: + - reasoning_effort + - name: vertex_ai-anthropic_models/vertex_ai/claude-3-7-sonnet@20250219 edit_format: diff weak_model_name: vertex_ai/claude-3-5-haiku@20241022 use_repo_map: true + overeager: true examples_as_sys_msg: true extra_params: max_tokens: 64000 editor_model_name: vertex_ai-anthropic_models/vertex_ai/claude-3-7-sonnet@20250219 editor_edit_format: editor-diff + accepts_settings: + - thinking_tokens + +- name: vertex_ai-language-models/gemini-2.5-flash-preview-04-17 + edit_format: diff + use_repo_map: true + accepts_settings: + - thinking_tokens - name: vertex_ai/claude-3-5-haiku@20241022 edit_format: diff @@ -983,11 +1461,14 @@ cog.out("```\n") edit_format: diff weak_model_name: vertex_ai/claude-3-5-haiku@20241022 use_repo_map: true + overeager: true examples_as_sys_msg: true extra_params: max_tokens: 64000 editor_model_name: vertex_ai/claude-3-7-sonnet@20250219 editor_edit_format: editor-diff + accepts_settings: + - thinking_tokens - name: vertex_ai/claude-3-opus@20240229 edit_format: diff @@ -997,9 +1478,39 @@ cog.out("```\n") - name: vertex_ai/claude-3-sonnet@20240229 weak_model_name: vertex_ai/claude-3-5-haiku@20241022 +- name: vertex_ai/gemini-2.5-pro-exp-03-25 + edit_format: diff-fenced + weak_model_name: vertex_ai-language-models/gemini-2.5-flash-preview-04-17 + use_repo_map: true + editor_model_name: vertex_ai-language-models/gemini-2.5-flash-preview-04-17 + +- name: vertex_ai/gemini-2.5-pro-preview-03-25 + edit_format: diff-fenced + weak_model_name: vertex_ai-language-models/gemini-2.5-flash-preview-04-17 + use_repo_map: true + editor_model_name: vertex_ai-language-models/gemini-2.5-flash-preview-04-17 + - name: vertex_ai/gemini-pro-experimental edit_format: diff-fenced use_repo_map: true + +- name: xai/grok-3-beta + edit_format: diff + use_repo_map: true + +- name: xai/grok-3-fast-beta + edit_format: diff + use_repo_map: true + +- name: xai/grok-3-mini-beta + use_repo_map: true + accepts_settings: + - reasoning_effort + +- name: xai/grok-3-mini-fast-beta + use_repo_map: true + accepts_settings: + - reasoning_effort ``` diff --git a/aider/website/docs/config/aider_conf.md b/aider/website/docs/config/aider_conf.md index 356026a7a..40483ae4b 100644 --- a/aider/website/docs/config/aider_conf.md +++ b/aider/website/docs/config/aider_conf.md @@ -74,39 +74,6 @@ cog.outl("```") ## Specify the model to use for the main chat #model: xxx -## Use claude-3-opus-20240229 model for the main chat -#opus: false - -## Use anthropic/claude-3-7-sonnet-20250219 model for the main chat -#sonnet: false - -## Use claude-3-5-haiku-20241022 model for the main chat -#haiku: false - -## Use gpt-4-0613 model for the main chat -#4: false - -## Use gpt-4o model for the main chat -#4o: false - -## Use gpt-4o-mini model for the main chat -#mini: false - -## Use gpt-4-1106-preview model for the main chat -#4-turbo: false - -## Use gpt-3.5-turbo model for the main chat -#35turbo: false - -## Use deepseek/deepseek-chat model for the main chat -#deepseek: false - -## Use o1-mini model for the main chat -#o1-mini: false - -## Use o1-preview model for the main chat -#o1-preview: false - ######################## # API Keys and settings: @@ -170,6 +137,9 @@ cog.outl("```") ## Set the reasoning_effort API parameter (default: not set) #reasoning-effort: xxx +## Set the thinking token budget for models that support it (default: not set) +#thinking-tokens: xxx + ## Verify the SSL cert when connecting to models (default: True) #verify-ssl: true @@ -182,6 +152,9 @@ cog.outl("```") ## Use architect edit format for the main chat #architect: false +## Enable/disable automatic acceptance of architect changes (default: True) +#auto-accept-architect: true + ## Specify the model to use for commit messages and chat history summarization (default depends on --model) #weak-model: xxx @@ -194,6 +167,9 @@ cog.outl("```") ## Only work with models that have meta-data available (default: True) #show-model-warnings: true +## Check if model accepts settings like reasoning_effort/thinking_tokens (default: True) +#check-model-accepts-settings: true + ## Soft limit on tokens for chat history, after which summarization begins. If unspecified, defaults to the model's max_chat_history_tokens. #max-chat-history-tokens: xxx @@ -249,31 +225,31 @@ cog.outl("```") #stream: true ## Set the color for user input (default: #00cc00) -#user-input-color: #00cc00 +#user-input-color: "#00cc00" ## Set the color for tool output (default: None) -#tool-output-color: xxx +#tool-output-color: "xxx" ## Set the color for tool error messages (default: #FF2222) -#tool-error-color: #FF2222 +#tool-error-color: "#FF2222" ## Set the color for tool warning messages (default: #FFA500) -#tool-warning-color: #FFA500 +#tool-warning-color: "#FFA500" ## Set the color for assistant output (default: #0088ff) -#assistant-output-color: #0088ff +#assistant-output-color: "#0088ff" ## Set the color for the completion menu (default: terminal's default text color) -#completion-menu-color: xxx +#completion-menu-color: "xxx" ## Set the background color for the completion menu (default: terminal's default background color) -#completion-menu-bg-color: xxx +#completion-menu-bg-color: "xxx" ## Set the color for the current item in the completion menu (default: terminal's default background color) -#completion-menu-current-color: xxx +#completion-menu-current-color: "xxx" ## Set the background color for the current item in the completion menu (default: terminal's default text color) -#completion-menu-current-bg-color: xxx +#completion-menu-current-bg-color: "xxx" ## Set the markdown code theme (default: default, other options include monokai, solarized-dark, solarized-light, or a Pygments builtin style, see https://pygments.org/styles for available themes) #code-theme: default @@ -314,6 +290,9 @@ cog.outl("```") ## Prefix all commit messages with 'aider: ' (default: False) #attribute-commit-message-committer: false +## Enable/disable git pre-commit hooks with --no-verify (default: False) +#git-commit-verify: false + ## Commit all pending changes with a suitable commit message, then exit #commit: false @@ -485,13 +464,52 @@ cog.outl("```") ## Enable/disable multi-line input mode with Meta-Enter to submit (default: False) #multiline: false +## Enable/disable terminal bell notifications when LLM responses are ready (default: False) +#notifications: false + +## Specify a command to run for notifications instead of the terminal bell. If not specified, a default command for your OS may be used. +#notifications-command: xxx + ## Enable/disable detection and offering to add URLs to chat (default: True) #detect-urls: true ## Specify which editor to use for the /editor command #editor: xxx -## Install the tree_sitter_language_pack (experimental) -#install-tree-sitter-language-pack: false +############################ +# Deprecated model settings: + +## Use claude-3-opus-20240229 model for the main chat (deprecated, use --model) +#opus: false + +## Use anthropic/claude-3-7-sonnet-20250219 model for the main chat (deprecated, use --model) +#sonnet: false + +## Use claude-3-5-haiku-20241022 model for the main chat (deprecated, use --model) +#haiku: false + +## Use gpt-4-0613 model for the main chat (deprecated, use --model) +#4: false + +## Use gpt-4o model for the main chat (deprecated, use --model) +#4o: false + +## Use gpt-4o-mini model for the main chat (deprecated, use --model) +#mini: false + +## Use gpt-4-1106-preview model for the main chat (deprecated, use --model) +#4-turbo: false + +## Use gpt-3.5-turbo model for the main chat (deprecated, use --model) +#35turbo: false + +## Use deepseek/deepseek-chat model for the main chat (deprecated, use --model) +#deepseek: false + +## Use o1-mini model for the main chat (deprecated, use --model) +#o1-mini: false + +## Use o1-preview model for the main chat (deprecated, use --model) +#o1-preview: false ``` diff --git a/aider/website/docs/config/dotenv.md b/aider/website/docs/config/dotenv.md index 3a18d6433..5a1c616e8 100644 --- a/aider/website/docs/config/dotenv.md +++ b/aider/website/docs/config/dotenv.md @@ -64,39 +64,6 @@ cog.outl("```") ## Specify the model to use for the main chat #AIDER_MODEL= -## Use claude-3-opus-20240229 model for the main chat -#AIDER_OPUS= - -## Use anthropic/claude-3-7-sonnet-20250219 model for the main chat -#AIDER_SONNET= - -## Use claude-3-5-haiku-20241022 model for the main chat -#AIDER_HAIKU= - -## Use gpt-4-0613 model for the main chat -#AIDER_4= - -## Use gpt-4o model for the main chat -#AIDER_4O= - -## Use gpt-4o-mini model for the main chat -#AIDER_MINI= - -## Use gpt-4-1106-preview model for the main chat -#AIDER_4_TURBO= - -## Use gpt-3.5-turbo model for the main chat -#AIDER_35TURBO= - -## Use deepseek/deepseek-chat model for the main chat -#AIDER_DEEPSEEK= - -## Use o1-mini model for the main chat -#AIDER_O1_MINI= - -## Use o1-preview model for the main chat -#AIDER_O1_PREVIEW= - ######################## # API Keys and settings: @@ -145,6 +112,9 @@ cog.outl("```") ## Set the reasoning_effort API parameter (default: not set) #AIDER_REASONING_EFFORT= +## Set the thinking token budget for models that support it (default: not set) +#AIDER_THINKING_TOKENS= + ## Verify the SSL cert when connecting to models (default: True) #AIDER_VERIFY_SSL=true @@ -157,6 +127,9 @@ cog.outl("```") ## Use architect edit format for the main chat #AIDER_ARCHITECT= +## Enable/disable automatic acceptance of architect changes (default: True) +#AIDER_AUTO_ACCEPT_ARCHITECT=true + ## Specify the model to use for commit messages and chat history summarization (default depends on --model) #AIDER_WEAK_MODEL= @@ -169,6 +142,9 @@ cog.outl("```") ## Only work with models that have meta-data available (default: True) #AIDER_SHOW_MODEL_WARNINGS=true +## Check if model accepts settings like reasoning_effort/thinking_tokens (default: True) +#AIDER_CHECK_MODEL_ACCEPTS_SETTINGS=true + ## Soft limit on tokens for chat history, after which summarization begins. If unspecified, defaults to the model's max_chat_history_tokens. #AIDER_MAX_CHAT_HISTORY_TOKENS= @@ -289,6 +265,9 @@ cog.outl("```") ## Prefix all commit messages with 'aider: ' (default: False) #AIDER_ATTRIBUTE_COMMIT_MESSAGE_COMMITTER=false +## Enable/disable git pre-commit hooks with --no-verify (default: False) +#AIDER_GIT_COMMIT_VERIFY=false + ## Commit all pending changes with a suitable commit message, then exit #AIDER_COMMIT=false @@ -439,13 +418,52 @@ cog.outl("```") ## Enable/disable multi-line input mode with Meta-Enter to submit (default: False) #AIDER_MULTILINE=false +## Enable/disable terminal bell notifications when LLM responses are ready (default: False) +#AIDER_NOTIFICATIONS=false + +## Specify a command to run for notifications instead of the terminal bell. If not specified, a default command for your OS may be used. +#AIDER_NOTIFICATIONS_COMMAND= + ## Enable/disable detection and offering to add URLs to chat (default: True) #AIDER_DETECT_URLS=true ## Specify which editor to use for the /editor command #AIDER_EDITOR= -## Install the tree_sitter_language_pack (experimental) -#AIDER_INSTALL_TREE_SITTER_LANGUAGE_PACK=false +############################ +# Deprecated model settings: + +## Use claude-3-opus-20240229 model for the main chat (deprecated, use --model) +#AIDER_OPUS=false + +## Use anthropic/claude-3-7-sonnet-20250219 model for the main chat (deprecated, use --model) +#AIDER_SONNET=false + +## Use claude-3-5-haiku-20241022 model for the main chat (deprecated, use --model) +#AIDER_HAIKU=false + +## Use gpt-4-0613 model for the main chat (deprecated, use --model) +#AIDER_4=false + +## Use gpt-4o model for the main chat (deprecated, use --model) +#AIDER_4O=false + +## Use gpt-4o-mini model for the main chat (deprecated, use --model) +#AIDER_MINI=false + +## Use gpt-4-1106-preview model for the main chat (deprecated, use --model) +#AIDER_4_TURBO=false + +## Use gpt-3.5-turbo model for the main chat (deprecated, use --model) +#AIDER_35TURBO=false + +## Use deepseek/deepseek-chat model for the main chat (deprecated, use --model) +#AIDER_DEEPSEEK=false + +## Use o1-mini model for the main chat (deprecated, use --model) +#AIDER_O1_MINI=false + +## Use o1-preview model for the main chat (deprecated, use --model) +#AIDER_O1_PREVIEW=false ``` diff --git a/aider/website/docs/config/model-aliases.md b/aider/website/docs/config/model-aliases.md index abffe3f61..73eed2937 100644 --- a/aider/website/docs/config/model-aliases.md +++ b/aider/website/docs/config/model-aliases.md @@ -20,7 +20,8 @@ Multiple aliases can be defined by using the `--alias` option multiple times. Ea ## Configuration File -You can also define aliases in your [`.aider.conf.yml` file](https://aider.chat/docs/config/aider_conf.html): +Of course, +you can also define aliases in your [`.aider.conf.yml` file](https://aider.chat/docs/config/aider_conf.html): ```yaml alias: @@ -31,13 +32,35 @@ alias: ## Using Aliases -Once defined, you can use the alias instead of the full model name: +Once defined, you can use the alias instead of the full model name from the command line: ```bash aider --model fast # Uses gpt-4o-mini aider --model smart # Uses o3-mini ``` +Or with the `/model` command in-chat: + +``` +Aider v0.75.3 +Main model: anthropic/claude-3-7-sonnet-20250219 with diff edit format, prompt cache, infinite output +Weak model: claude-3-5-sonnet-20241022 +Git repo: .git with 406 files +Repo-map: using 4096 tokens, files refresh +───────────────────────────────────────────────────────────────────────────────────────────────────── +> /model fast + +Aider v0.75.3 +Main model: gpt-4o-mini with diff edit format +───────────────────────────────────────────────────────────────────────────────────────────────────── +diff> /model smart + +Aider v0.75.3 +Main model: o3-mini with diff edit format +───────────────────────────────────────────────────────────────────────────────────────────────────── +> +``` + ## Built-in Aliases Aider includes some built-in aliases for convenience: @@ -56,9 +79,15 @@ for alias, model in sorted(MODEL_ALIASES.items()): - `4-turbo`: gpt-4-1106-preview - `4o`: gpt-4o - `deepseek`: deepseek/deepseek-chat -- `flash`: gemini/gemini-2.0-flash-exp +- `flash`: gemini/gemini-2.5-flash-preview-04-17 +- `gemini`: gemini/gemini-2.5-pro-preview-03-25 +- `gemini-2.5-pro`: gemini/gemini-2.5-pro-exp-03-25 +- `gemini-exp`: gemini/gemini-2.5-pro-exp-03-25 +- `grok3`: xai/grok-3-beta - `haiku`: claude-3-5-haiku-20241022 +- `optimus`: openrouter/openrouter/optimus-alpha - `opus`: claude-3-opus-20240229 +- `quasar`: openrouter/openrouter/quasar-alpha - `r1`: deepseek/deepseek-reasoner - `sonnet`: anthropic/claude-3-7-sonnet-20250219 diff --git a/aider/website/docs/config/options.md b/aider/website/docs/config/options.md index 31547a357..d07234921 100644 --- a/aider/website/docs/config/options.md +++ b/aider/website/docs/config/options.md @@ -22,19 +22,18 @@ from aider.args import get_md_help cog.out(get_md_help()) ]]]--> ``` -usage: aider [-h] [--model] [--opus] [--sonnet] [--haiku] [--4] - [--4o] [--mini] [--4-turbo] [--35turbo] [--deepseek] - [--o1-mini] [--o1-preview] [--openai-api-key] - [--anthropic-api-key] [--openai-api-base] - [--openai-api-type] [--openai-api-version] - [--openai-api-deployment-id] [--openai-organization-id] - [--set-env] [--api-key] [--list-models] - [--model-settings-file] [--model-metadata-file] - [--alias] [--reasoning-effort] - [--verify-ssl | --no-verify-ssl] [--timeout] - [--edit-format] [--architect] [--weak-model] - [--editor-model] [--editor-edit-format] +usage: aider [-h] [--model] [--openai-api-key] [--anthropic-api-key] + [--openai-api-base] [--openai-api-type] + [--openai-api-version] [--openai-api-deployment-id] + [--openai-organization-id] [--set-env] [--api-key] + [--list-models] [--model-settings-file] + [--model-metadata-file] [--alias] [--reasoning-effort] + [--thinking-tokens] [--verify-ssl | --no-verify-ssl] + [--timeout] [--edit-format] [--architect] + [--auto-accept-architect | --no-auto-accept-architect] + [--weak-model] [--editor-model] [--editor-edit-format] [--show-model-warnings | --no-show-model-warnings] + [--check-model-accepts-settings | --no-check-model-accepts-settings] [--max-chat-history-tokens] [--cache-prompts | --no-cache-prompts] [--cache-keepalive-pings] [--map-tokens] @@ -57,6 +56,7 @@ usage: aider [-h] [--model] [--opus] [--sonnet] [--haiku] [--4] [--attribute-committer | --no-attribute-committer] [--attribute-commit-message-author | --no-attribute-commit-message-author] [--attribute-commit-message-committer | --no-attribute-commit-message-committer] + [--git-commit-verify | --no-git-commit-verify] [--commit] [--commit-prompt] [--dry-run | --no-dry-run] [--skip-sanity-check-repo] [--watch-files | --no-watch-files] [--lint] @@ -78,8 +78,11 @@ usage: aider [-h] [--model] [--opus] [--sonnet] [--haiku] [--4] [--suggest-shell-commands | --no-suggest-shell-commands] [--fancy-input | --no-fancy-input] [--multiline | --no-multiline] - [--detect-urls | --no-detect-urls] [--editor] - [--install-tree-sitter-language-pack] + [--notifications | --no-notifications] + [--notifications-command] + [--detect-urls | --no-detect-urls] [--editor] [--opus] + [--sonnet] [--haiku] [--4] [--4o] [--mini] [--4-turbo] + [--35turbo] [--deepseek] [--o1-mini] [--o1-preview] ``` @@ -97,58 +100,6 @@ Aliases: Specify the model to use for the main chat Environment variable: `AIDER_MODEL` -### `--opus` -Use claude-3-opus-20240229 model for the main chat -Environment variable: `AIDER_OPUS` - -### `--sonnet` -Use anthropic/claude-3-7-sonnet-20250219 model for the main chat -Environment variable: `AIDER_SONNET` - -### `--haiku` -Use claude-3-5-haiku-20241022 model for the main chat -Environment variable: `AIDER_HAIKU` - -### `--4` -Use gpt-4-0613 model for the main chat -Environment variable: `AIDER_4` -Aliases: - - `--4` - - `-4` - -### `--4o` -Use gpt-4o model for the main chat -Environment variable: `AIDER_4O` - -### `--mini` -Use gpt-4o-mini model for the main chat -Environment variable: `AIDER_MINI` - -### `--4-turbo` -Use gpt-4-1106-preview model for the main chat -Environment variable: `AIDER_4_TURBO` - -### `--35turbo` -Use gpt-3.5-turbo model for the main chat -Environment variable: `AIDER_35TURBO` -Aliases: - - `--35turbo` - - `--35-turbo` - - `--3` - - `-3` - -### `--deepseek` -Use deepseek/deepseek-chat model for the main chat -Environment variable: `AIDER_DEEPSEEK` - -### `--o1-mini` -Use o1-mini model for the main chat -Environment variable: `AIDER_O1_MINI` - -### `--o1-preview` -Use o1-preview model for the main chat -Environment variable: `AIDER_O1_PREVIEW` - ## API Keys and settings: ### `--openai-api-key VALUE` @@ -216,6 +167,10 @@ Environment variable: `AIDER_ALIAS` Set the reasoning_effort API parameter (default: not set) Environment variable: `AIDER_REASONING_EFFORT` +### `--thinking-tokens VALUE` +Set the thinking token budget for models that support it (default: not set) +Environment variable: `AIDER_THINKING_TOKENS` + ### `--verify-ssl` Verify the SSL cert when connecting to models (default: True) Default: True @@ -239,6 +194,14 @@ Aliases: Use architect edit format for the main chat Environment variable: `AIDER_ARCHITECT` +### `--auto-accept-architect` +Enable/disable automatic acceptance of architect changes (default: True) +Default: True +Environment variable: `AIDER_AUTO_ACCEPT_ARCHITECT` +Aliases: + - `--auto-accept-architect` + - `--no-auto-accept-architect` + ### `--weak-model WEAK_MODEL` Specify the model to use for commit messages and chat history summarization (default depends on --model) Environment variable: `AIDER_WEAK_MODEL` @@ -259,6 +222,14 @@ Aliases: - `--show-model-warnings` - `--no-show-model-warnings` +### `--check-model-accepts-settings` +Check if model accepts settings like reasoning_effort/thinking_tokens (default: True) +Default: True +Environment variable: `AIDER_CHECK_MODEL_ACCEPTS_SETTINGS` +Aliases: + - `--check-model-accepts-settings` + - `--no-check-model-accepts-settings` + ### `--max-chat-history-tokens VALUE` Soft limit on tokens for chat history, after which summarization begins. If unspecified, defaults to the model's max_chat_history_tokens. Environment variable: `AIDER_MAX_CHAT_HISTORY_TOKENS` @@ -472,6 +443,14 @@ Aliases: - `--attribute-commit-message-committer` - `--no-attribute-commit-message-committer` +### `--git-commit-verify` +Enable/disable git pre-commit hooks with --no-verify (default: False) +Default: False +Environment variable: `AIDER_GIT_COMMIT_VERIFY` +Aliases: + - `--git-commit-verify` + - `--no-git-commit-verify` + ### `--commit` Commit all pending changes with a suitable commit message, then exit Default: False @@ -751,6 +730,18 @@ Aliases: - `--multiline` - `--no-multiline` +### `--notifications` +Enable/disable terminal bell notifications when LLM responses are ready (default: False) +Default: False +Environment variable: `AIDER_NOTIFICATIONS` +Aliases: + - `--notifications` + - `--no-notifications` + +### `--notifications-command COMMAND` +Specify a command to run for notifications instead of the terminal bell. If not specified, a default command for your OS may be used. +Environment variable: `AIDER_NOTIFICATIONS_COMMAND` + ### `--detect-urls` Enable/disable detection and offering to add URLs to chat (default: True) Default: True @@ -763,8 +754,68 @@ Aliases: Specify which editor to use for the /editor command Environment variable: `AIDER_EDITOR` -### `--install-tree-sitter-language-pack` -Install the tree_sitter_language_pack (experimental) +## Deprecated model settings: + +### `--opus` +Use claude-3-opus-20240229 model for the main chat (deprecated, use --model) Default: False -Environment variable: `AIDER_INSTALL_TREE_SITTER_LANGUAGE_PACK` +Environment variable: `AIDER_OPUS` + +### `--sonnet` +Use anthropic/claude-3-7-sonnet-20250219 model for the main chat (deprecated, use --model) +Default: False +Environment variable: `AIDER_SONNET` + +### `--haiku` +Use claude-3-5-haiku-20241022 model for the main chat (deprecated, use --model) +Default: False +Environment variable: `AIDER_HAIKU` + +### `--4` +Use gpt-4-0613 model for the main chat (deprecated, use --model) +Default: False +Environment variable: `AIDER_4` +Aliases: + - `--4` + - `-4` + +### `--4o` +Use gpt-4o model for the main chat (deprecated, use --model) +Default: False +Environment variable: `AIDER_4O` + +### `--mini` +Use gpt-4o-mini model for the main chat (deprecated, use --model) +Default: False +Environment variable: `AIDER_MINI` + +### `--4-turbo` +Use gpt-4-1106-preview model for the main chat (deprecated, use --model) +Default: False +Environment variable: `AIDER_4_TURBO` + +### `--35turbo` +Use gpt-3.5-turbo model for the main chat (deprecated, use --model) +Default: False +Environment variable: `AIDER_35TURBO` +Aliases: + - `--35turbo` + - `--35-turbo` + - `--3` + - `-3` + +### `--deepseek` +Use deepseek/deepseek-chat model for the main chat (deprecated, use --model) +Default: False +Environment variable: `AIDER_DEEPSEEK` + +### `--o1-mini` +Use o1-mini model for the main chat (deprecated, use --model) +Default: False +Environment variable: `AIDER_O1_MINI` + +### `--o1-preview` +Use o1-preview model for the main chat (deprecated, use --model) +Default: False +Environment variable: `AIDER_O1_PREVIEW` diff --git a/aider/website/docs/config/reasoning.md b/aider/website/docs/config/reasoning.md index 9147b0338..168516feb 100644 --- a/aider/website/docs/config/reasoning.md +++ b/aider/website/docs/config/reasoning.md @@ -6,29 +6,170 @@ description: How to configure reasoning model settings from secondary providers. # Reasoning models -Many -"reasoning" models have restrictions on how they can be used: -they sometimes prohibit streaming, use of temperature and/or the system prompt. -Some also support different levels of "reasoning effort". +![Thinking demo](/assets/thinking.jpg) -Aider is configured to work properly with these models -when served through major provider APIs. +## Basic usage -You may need to [configure model settings](/docs/config/adv-model-settings.html) -if you are using them through another provider -and see errors related to temperature or system prompt. +Aider is configured to work with most popular reasoning models out of the box. +You can use them like this: -Include settings for your new provider in `.aider.model.setting.yml` file -at the root of your project or in your home directory. +```bash +# Sonnet uses a thinking token budget +aider --model sonnet --thinking-tokens 8k -## Reasoning effort +# o3-mini uses low/medium/high reasoning effort +aider --model o3-mini --reasoning-effort high + +# R1 doesn't have configurable thinking/reasoning +aider --model r1 +``` + +Inside the aider chat, you can use `/thinking-tokens 4k` or `/reasoning-effort low` to change +the amount of reasoning. + +The rest of this document describes more advanced details which are mainly needed +if you're configuring aider to work with a lesser known reasoning model or one served +via an unusual provider. + +## Reasoning settings + +Different models support different reasoning settings. Aider provides several ways to control reasoning behavior: + +### Reasoning effort You can use the `--reasoning-effort` switch to control the reasoning effort of models which support this setting. +This switch is useful for OpenAI's reasoning models, which accept "low", "medium" and "high". -## Temperature, streaming and system prompt +### Thinking tokens -You should find one of the existing model setting configuration entries +You can use the `--thinking-tokens` switch to request +the model use a certain number of thinking tokens. +This switch is useful for Sonnet 3.7. +You can specify the token budget like "1024", "1k", "8k" or "0.01M". + +### Model compatibility and settings + +Not all models support these two settings. Aider uses the +[model's metadata](/docs/config/adv-model-settings.html) +to determine which settings each model accepts: + +```yaml +- name: o3-mini + ... + accepts_settings: ["reasoning_effort"] +``` + +If you try to use a setting that a model doesn't explicitly support, Aider will warn you: + +``` +Warning: o3-mini does not support 'thinking_tokens', ignoring. +Use --no-check-model-accepts-settings to force the 'thinking_tokens' setting. +``` + +The warning informs you that: +1. The setting won't be applied because the model doesn't list it in `accepts_settings` +2. You can use `--no-check-model-accepts-settings` to force the setting anyway + +This functionality helps prevent API errors while still allowing you to experiment with settings when needed. + +Each model has a predefined list of supported settings in its configuration. For example: + +- OpenAI reasoning models generally support `reasoning_effort` +- Anthropic reasoning models generally support `thinking_tokens` + + +### How `accepts_settings` works + +Models define which reasoning settings they accept using the `accepts_settings` property: + +```yaml +- name: a-fancy-reasoning-model + edit_format: diff + use_repo_map: true + accepts_settings: # <--- + - reasoning_effort # <--- +``` + +This configuration: +1. Tells Aider that the model accepts the `reasoning_effort` setting +2. Indicates the model does NOT accept `thinking_tokens` (since it's not listed) +3. Causes Aider to ignore any `--thinking-tokens` value passed for this model +4. Generates a warning if you try to use `--thinking-tokens` with this model + +You can override this behavior with `--no-check-model-accepts-settings`, which will: +1. Force Aider to apply all settings passed via command line +2. Skip all compatibility checks +3. Potentially cause API errors if the model truly doesn't support the setting + +This is useful when testing new models or using models through custom API providers. + + +## Thinking tokens in XML tags + +There is also a `reasoning_tag` setting, which takes the name of an XML tag +that the model uses to wrap its reasoning/thinking output. + +For example when using DeepSeek R1 from Fireworks, the reasoning comes back inside +`...` tags, so aider's settings +include `reasoning_tag: think`. + +``` + +The user wants me to greet them! + + +Hello! +``` + +Aider will display the thinking/reasoning output, +but it won't be used for file editing instructions, added to the chat history, etc. +Aider will rely on the non-thinking output for instructions on how to make code changes, etc. + +### Model-specific reasoning tags + +Different models use different XML tags for their reasoning: +When using custom or self-hosted models, you may need to specify the appropriate reasoning tag in your configuration. + +```yaml +- name: fireworks_ai/accounts/fireworks/models/deepseek-r1 + edit_format: diff + weak_model_name: fireworks_ai/accounts/fireworks/models/deepseek-v3 + use_repo_map: true + extra_params: + max_tokens: 160000 + use_temperature: false + editor_model_name: fireworks_ai/accounts/fireworks/models/deepseek-v3 + editor_edit_format: editor-diff + reasoning_tag: think # <--- +``` + +## Reasoning model limitations + +Many "reasoning" models have restrictions on how they can be used: +they sometimes prohibit streaming, use of temperature and/or the system prompt. +Aider is configured to work properly with popular models +when served through major provider APIs. + +If you're using a model through a different provider (like Azure or custom deployment), +you may need to [configure model settings](/docs/config/adv-model-settings.html) +if you see errors related to temperature or system prompt. + +Include settings for your new provider in `.aider.model.settings.yml` file +at the root of your project or in your home directory. + +### Temperature, streaming and system prompt + +Reasoning models often have specific requirements for these settings: + +| Setting | Description | Common Restrictions | +|---------|-------------|---------------------| +| `use_temperature` | Whether to use temperature sampling | Many reasoning models require this set to `false` | +| `streaming` | Whether to stream responses | Some reasoning models don't support streaming | +| `use_system_prompt` | Whether to use system prompt | Some reasoning models don't support system prompts | + +It may be helpful to find one of the +[existing model setting configuration entries](https://github.com/Aider-AI/aider/blob/main/aider/resources/model-settings.yml) for the model you are interested in, say o3-mini: ```yaml @@ -39,6 +180,7 @@ for the model you are interested in, say o3-mini: use_temperature: false # <--- editor_model_name: gpt-4o editor_edit_format: editor-diff + accepts_settings: ["reasoning_effort"] ``` Pay attention to these settings, which must be set to `false` @@ -48,8 +190,9 @@ for certain reasoning models: - `streaming` - `use_system_prompt` -Here's an example of -the settings to use o3-mini via Azure. +### Custom provider example + +Here's an example of the settings to use o3-mini via Azure. Note that aider already has these settings pre-configured, but they serve as a good example of how to adapt the main model settings for a different provider. @@ -62,29 +205,5 @@ settings for a different provider. use_temperature: false # <--- editor_model_name: azure/gpt-4o editor_edit_format: editor-diff -``` - -## Thinking tokens - -There is also a `remove_reasoning` setting, which takes the name of a tag. -This is used to remove everything inside that XML tag pair. - -For example when using DeepSeek R1 from Fireworks, the reasoning comes back inside -`...` tags, so aider's settings -include `remove_reasoning: think` to remove that part of the response. - -Aider will still *display* think reasoning output, it just won't use it -to find file editing instructions, etc. - -```yaml -- name: fireworks_ai/accounts/fireworks/models/deepseek-r1 - edit_format: diff - weak_model_name: fireworks_ai/accounts/fireworks/models/deepseek-v3 - use_repo_map: true - extra_params: - max_tokens: 160000 - use_temperature: false - editor_model_name: fireworks_ai/accounts/fireworks/models/deepseek-v3 - editor_edit_format: editor-diff - remove_reasoning: think # <--- + accepts_settings: ["reasoning_effort"] ``` diff --git a/aider/website/docs/faq.md b/aider/website/docs/faq.md index 1e23ec88c..eaaaa4fc2 100644 --- a/aider/website/docs/faq.md +++ b/aider/website/docs/faq.md @@ -153,6 +153,21 @@ Add 6.9k tokens of command output to the chat? (Y)es/(N)o [Yes]: Yes /ask Are there any problems with the way this change works with the FooBar class? ``` +And of course you can prepare diff output outside of aider and provide it as +a file for aider to read: + +``` +$ git diff -C10 v1..v2 > v1-v2-changes.diff +$ aider --read v1-v2-changes.diff + +Aider v0.77.2.dev+import +Main model: anthropic/claude-3-7-sonnet-20250219 with diff edit format, 8k think tokens +────────────────────────────────── +v1-v2-changes.diff +> Do you see any potential bugs in this PR? +``` + + {: .tip } The `/git` command will not work for this purpose, as its output is not included in the chat. @@ -249,16 +264,15 @@ tr:hover { background-color: #f5f5f5; } - - - - - - - - - - + + + + + + + + +
    Model NameTotal TokensPercent
    claude-3-5-sonnet-20241022444,17845.0%
    anthropic/claude-3-7-sonnet-20250219258,39726.2%
    fireworks_ai/accounts/fireworks/models/deepseek-v3105,99910.7%
    claude-3-5-haiku-2024102269,2037.0%
    o3-mini52,1925.3%
    openrouter/anthropic/claude-3.7-sonnet20,2132.0%
    gpt-4o12,5951.3%
    openrouter/REDACTED12,0831.2%
    openrouter/openai/o3-mini10,1071.0%
    anthropic/REDACTED1,9990.2%
    gemini/gemini-2.5-pro-exp-03-251,637,70083.1%
    o3186,9539.5%
    openrouter/anthropic/claude-3.7-sonnet76,2853.9%
    gemini/gemini-2.5-flash-preview-04-1718,6450.9%
    gemini/gemini-2.5-pro-preview-03-2516,5240.8%
    o4-mini16,4990.8%
    xai/grok-3-fast-beta10,2880.5%
    None8,0010.4%
    gemini/REDACTED6060.0%
    {: .note :} @@ -276,6 +290,16 @@ by doing something like `git blame` on the repo, and counting up who wrote all the new lines of code in each release. Only lines in source code files are counted, not documentation or prompt files. +## Why did aider ignore/discard its proposed edits after it asked to add a new file to the chat? + +If aider prompts you to add a new file to the chat and you say yes, +it will re-submit the original request. +The fact that the LLM's reply indicated that it needed to see another file (and you said yes) +is often a sign that the LLM should have been able to see/edit that file in the first place. +Without access to it, there is increased chance that it's done a bad implementation of the requested change. +Often LLMs will hallucinate content for the files they needed but didn't have. +So aider re-submits the original request in this situation. + ## Why does aider sometimes stop highlighting code in its replies? Aider displays the markdown responses that are coming back from the LLM. diff --git a/aider/website/docs/git.md b/aider/website/docs/git.md index 3c17de47f..00ee5a272 100644 --- a/aider/website/docs/git.md +++ b/aider/website/docs/git.md @@ -40,6 +40,7 @@ While it is not recommended, you can disable aider's use of git in a few ways: - `--no-auto-commits` will stop aider from git committing each of its changes. - `--no-dirty-commits` will stop aider from committing dirty files before applying its edits. - `--no-git` will completely stop aider from using git on your files. You should ensure you are keeping sensible backups of the files you are working with. + - `--git-commit-verify` will run pre-commit hooks when making git commits. By default, aider skips pre-commit hooks by using the `--no-verify` flag (`--git-commit-verify=False`). ## Commit messages diff --git a/aider/website/docs/index.md b/aider/website/docs/index.md new file mode 100644 index 000000000..e0b952565 --- /dev/null +++ b/aider/website/docs/index.md @@ -0,0 +1,47 @@ +--- +nav_exclude: true +--- + +# Aider Documentation + +Aider is AI pair programming in your terminal. This documentation will help you get the most out of aider. + +
    +{% assign pages_list = site.html_pages | sort: "nav_order" %} + +
      +{% for page in pages_list %} + {% if page.title and page.url != "/" and page.parent == nil and page.nav_exclude != true %} +
    • + {{ page.title }}{% if page.description %} — {{ page.description }}{% endif %} + + {% assign children = site.html_pages | where: "parent", page.title | sort: "nav_order" %} + {% if children.size > 0 %} +
        + {% for child in children %} + {% if child.title %} +
      • + {{ child.title }}{% if child.description %} — {{ child.description }}{% endif %} + + {% assign grandchildren = site.html_pages | where: "parent", child.title | sort: "nav_order" %} + {% if grandchildren.size > 0 %} +
          + {% for grandchild in grandchildren %} + {% if grandchild.title %} +
        • + {{ grandchild.title }}{% if grandchild.description %} — {{ grandchild.description }}{% endif %} +
        • + {% endif %} + {% endfor %} +
        + {% endif %} +
      • + {% endif %} + {% endfor %} +
      + {% endif %} +
    • + {% endif %} +{% endfor %} +
    +
    diff --git a/aider/website/docs/install/optional.md b/aider/website/docs/install/optional.md index 99b70267b..1e122c2a9 100644 --- a/aider/website/docs/install/optional.md +++ b/aider/website/docs/install/optional.md @@ -22,7 +22,7 @@ Here are You need an key from an API provider to work with most models: - [OpenAI](https://help.openai.com/en/articles/4936850-where-do-i-find-my-secret-api-key) provides o1, o3-mini, gpt-4o and other models. Note that paying for an API key is different than being a "ChatGPT" subscriber. -- [Anthropic](https://docs.anthropic.com/claude/reference/getting-started-with-the-api) provides Claude 3.5 Sonnet and Haiku. +- [Anthropic](https://docs.anthropic.com/claude/reference/getting-started-with-the-api) provides Claude 3.7 Sonnet and Haiku. - [DeepSeek](https://platform.deepseek.com/api_keys) provides DeepSeek R1 and DeepSeek Chat V3. - [OpenRouter](https://openrouter.ai/keys) allows you to access models from many providers using a single key. diff --git a/aider/website/docs/languages.md b/aider/website/docs/languages.md index 09eb1b4e0..ff9c14bfc 100644 --- a/aider/website/docs/languages.md +++ b/aider/website/docs/languages.md @@ -36,17 +36,16 @@ If you can find and share that file in a [GitHub issue](https://github.com/Aider-AI/aider/issues), then it may be possible to add repo map support. -If aider doesn't support linting, it will be complicated to -add linting and repo map support. -That is because aider relies on -[py-tree-sitter-languages](https://github.com/grantjenks/py-tree-sitter-languages) +If aider doesn't already support linting your language, +it will be more complicated to add support. +Aider relies on +[tree-sitter-language-pack](https://github.com/Goldziher/tree-sitter-language-pack) to provide pre-packaged versions of tree-sitter -parsers for many languages. - -Aider needs to be easy for users to install in many environments, -and it is probably too complex to add dependencies on -additional individual tree-sitter parsers. - +language parsers. +This makes it easy for users to install aider in many diverse environments. +You probably need to work with that project to get your language +supported, which will easily allow aider to lint that language. +For repo-map support, you will also need to find or create a `tags.scm` file. diff --git a/aider/website/docs/leaderboards/edit.md b/aider/website/docs/leaderboards/edit.md index 01a5dc57a..07cb664c4 100644 --- a/aider/website/docs/leaderboards/edit.md +++ b/aider/website/docs/leaderboards/edit.md @@ -128,6 +128,6 @@ mod_dates = [get_last_modified_date(file) for file in files] latest_mod_date = max(mod_dates) cog.out(f"{latest_mod_date.strftime('%B %d, %Y.')}") ]]]--> -January 16, 2025. +April 12, 2025.

    diff --git a/aider/website/docs/leaderboards/index.md b/aider/website/docs/leaderboards/index.md index e4ef0ff8b..fa03fb2d7 100644 --- a/aider/website/docs/leaderboards/index.md +++ b/aider/website/docs/leaderboards/index.md @@ -8,92 +8,261 @@ has_children: true # Aider LLM Leaderboards -Aider works best with LLMs which are good at *editing* code, not just good at writing -code. -To evaluate an LLM's editing skill, aider uses benchmarks that -assess a model's ability to consistently follow the system prompt -to successfully edit code. +Aider excels with LLMs skilled at writing and *editing* code, +and uses benchmarks to +evaluate an LLM's ability to follow instructions and edit code successfully without +human intervention. +[Aider's polyglot benchmark](https://aider.chat/2024/12/21/polyglot.html#the-polyglot-benchmark) tests LLMs on 225 challenging Exercism coding exercises across C++, Go, Java, JavaScript, Python, and Rust. -The leaderboards report the results from a number of popular LLMs. -While [aider can connect to almost any LLM](/docs/llms.html), -it works best with models that score well on the benchmarks. +

    Aider polyglot coding leaderboard

    - -## Polyglot leaderboard - -[Aider's polyglot benchmark](https://aider.chat/2024/12/21/polyglot.html#the-polyglot-benchmark) -asks the LLM to edit source files to complete 225 coding exercises -from Exercism. -It contains exercises in many popular programming languages: -C++, Go, Java, JavaScript, Python and Rust. -The 225 exercises were purposely selected to be the *hardest* -that Exercism offered in those languages, to provide -a strong coding challenge to LLMs. - -This benchmark measures the LLM's coding ability in popular languages, -and whether it can -write new code that integrates into existing code. -The model also has to successfully apply all its changes to the source file without human intervention. - - +
    + +
    + + + +
    + +
    + - - - - - + + + + + + {% assign max_cost = 0 %} + {% for row in site.data.polyglot_leaderboard %} + {% if row.total_cost > max_cost %} + {% assign max_cost = row.total_cost %} + {% endif %} + {% endfor %} + {% if max_cost == 0 %}{% assign max_cost = 1 %}{% endif %} {% assign edit_sorted = site.data.polyglot_leaderboard | sort: 'pass_rate_2' | reverse %} - {% for row in edit_sorted %} - - - - - - - + {% for row in edit_sorted %} {% comment %} Add loop index for unique IDs {% endcomment %} + {% assign row_index = forloop.index0 %} + + + + + + + + + + + {% endfor %}
    + + ModelPercent completed correctlyPercent using correct edit formatCommandEdit formatTotal CostPercent correctCostCommandCorrect edit formatEdit Format
    {{ row.model }}{{ row.pass_rate_2 }}%{{ row.percent_cases_well_formed }}%{{ row.command }}{{ row.edit_format }}{% if row.total_cost == 0 %}?{% else %}${{ row.total_cost | times: 1.0 | round: 2 }}{% endif %}
    + + + {{ row.model }} +
    + {{ row.pass_rate_2 }}% +
    + {% if row.total_cost > 0 %} +
    + {% endif %} + {% assign rounded_cost = row.total_cost | times: 1.0 | round: 2 %} + {% if row.total_cost == 0 or rounded_cost == 0.00 %}{% else %}${{ rounded_cost }}{% endif %} +
    {{ row.command }}{{ row.percent_cases_well_formed }}%{{ row.edit_format }}
    -### Aider polyglot benchmark results - - - - - + - - - diff --git a/aider/website/docs/leaderboards/refactor.md b/aider/website/docs/leaderboards/refactor.md index 96a7bea2b..50d8e3ebb 100644 --- a/aider/website/docs/leaderboards/refactor.md +++ b/aider/website/docs/leaderboards/refactor.md @@ -73,6 +73,6 @@ mod_dates = [get_last_modified_date(file) for file in files] latest_mod_date = max(mod_dates) cog.out(f"{latest_mod_date.strftime('%B %d, %Y.')}") ]]]--> -January 16, 2025. +April 12, 2025.

    diff --git a/aider/website/docs/legal/privacy.md b/aider/website/docs/legal/privacy.md index 1d1a2192a..1c12d245d 100644 --- a/aider/website/docs/legal/privacy.md +++ b/aider/website/docs/legal/privacy.md @@ -98,7 +98,7 @@ if result.returncode == 0: date = datetime.datetime.fromtimestamp(timestamp) cog.out(f"{date.strftime('%B %d, %Y.')}") ]]]--> -December 06, 2024. +April 12, 2025.

    diff --git a/aider/website/docs/llms.md b/aider/website/docs/llms.md index 4f1ec44f5..c2475431c 100644 --- a/aider/website/docs/llms.md +++ b/aider/website/docs/llms.md @@ -16,9 +16,10 @@ description: Aider can connect to most LLMs for AI pair programming. Aider works best with these models, which are skilled at editing code: +- [Gemini 2.5 Pro](/docs/llms/gemini.html) - [DeepSeek R1 and V3](/docs/llms/deepseek.html) -- [Claude 3.5 Sonnet](/docs/llms/anthropic.html) -- [OpenAI o1, o3-mini and GPT-4o](/docs/llms/openai.html) +- [Claude 3.7 Sonnet](/docs/llms/anthropic.html) +- [OpenAI o3, o4-mini and GPT-4.1](/docs/llms/openai.html) ## Free models @@ -26,10 +27,8 @@ Aider works best with these models, which are skilled at editing code: Aider works with a number of **free** API providers: -- Google's [Gemini 1.5 Pro](/docs/llms/gemini.html) works with aider, with -code editing capabilities similar to GPT-3.5. -- You can use [Llama 3 70B on Groq](/docs/llms/groq.html) which is comparable to GPT-3.5 in code editing performance. -- Cohere also offers free API access to their [Command-R+ model](/docs/llms/cohere.html), which works with aider as a *very basic* coding assistant. +- [OpenRouter offers free access to many models](https://openrouter.ai/models/?q=free), with limitations on daily usage. +- Google's [Gemini 2.5 Pro Exp](/docs/llms/gemini.html) works very well with aider. ## Local models {: .no_toc } diff --git a/aider/website/docs/llms/anthropic.md b/aider/website/docs/llms/anthropic.md index fcbb96ef9..26748b101 100644 --- a/aider/website/docs/llms/anthropic.md +++ b/aider/website/docs/llms/anthropic.md @@ -10,21 +10,26 @@ To work with Anthropic's models, you need to provide your either in the `ANTHROPIC_API_KEY` environment variable or via the `--anthropic-api-key` command line switch. -Aider has some built in shortcuts for the most popular Anthropic models and -has been tested and benchmarked to work well with them: +First, install aider: + +{% include install.md %} + +Then configure your API keys: ``` -python -m pip install -U aider-chat - export ANTHROPIC_API_KEY= # Mac/Linux setx ANTHROPIC_API_KEY # Windows, restart shell after setx +``` -# Aider uses Claude 3.5 Sonnet by default (or use --sonnet) +Start working with aider and Anthropic on your codebase: + +```bash +# Change directory into your codebase +cd /to/your/project + +# Aider uses Claude 3.7 Sonnet by default aider -# Claude 3 Opus -aider --opus - # List models available from Anthropic aider --list-models anthropic/ ``` diff --git a/aider/website/docs/llms/azure.md b/aider/website/docs/llms/azure.md index 55686afa2..7e20fc83d 100644 --- a/aider/website/docs/llms/azure.md +++ b/aider/website/docs/llms/azure.md @@ -7,21 +7,32 @@ nav_order: 500 Aider can connect to the OpenAI models on Azure. -``` -python -m pip install -U aider-chat +First, install aider: +{% include install.md %} + +Then configure your API keys and endpoint: + +``` # Mac/Linux: export AZURE_API_KEY= -export AZURE_API_VERSION=2023-05-15 +export AZURE_API_VERSION=2024-12-01-preview export AZURE_API_BASE=https://myendpt.openai.azure.com # Windows setx AZURE_API_KEY -setx AZURE_API_VERSION 2023-05-15 +setx AZURE_API_VERSION 2024-12-01-preview setx AZURE_API_BASE https://myendpt.openai.azure.com # ... restart your shell after setx commands +``` -aider --model azure/ +Start working with aider and Azure on your codebase: + +```bash +# Change directory into your codebase +cd /to/your/project + +aider --model azure/ # List models available from Azure aider --list-models azure/ @@ -29,3 +40,9 @@ aider --list-models azure/ Note that aider will also use environment variables like `AZURE_OPENAI_API_xxx`. + +The `aider --list-models azure/` command will list all models that aider supports through Azure, not the models that are available for the provided endpoint. + +When setting the model to use with `--model azure/`, `` is likely just the name of the model you have deployed to the endpoint for example `o3-mini` or `gpt-4o`. The screenshow below shows `o3-mini` and `gpt-4o` deployments in the Azure portal done under the `myendpt` resource. + +![example azure deployment](/assets/azure-deployment.png) \ No newline at end of file diff --git a/aider/website/docs/llms/bedrock.md b/aider/website/docs/llms/bedrock.md index c7705918f..51a7d0822 100644 --- a/aider/website/docs/llms/bedrock.md +++ b/aider/website/docs/llms/bedrock.md @@ -6,8 +6,6 @@ nav_order: 560 # Amazon Bedrock Aider can connect to models provided by Amazon Bedrock. -You will need to have an AWS account with access to the Bedrock service. - To configure Aider to use the Amazon Bedrock API, you need to set up your AWS credentials. This can be done using the AWS CLI or by setting environment variables. @@ -37,6 +35,14 @@ feature, you will receive an error message like the following: anthropic.claude-3-7-sonnet-20250219-v1:0 with on-demand throughput isn\xe2\x80\x99t supported. Retry your request with the ID or ARN of an inference profile that contains this model."}' +## Installation and Configuration + +First, install aider: + +{% include install.md %} + +Next, configure your AWS credentials. This can be done using the AWS CLI or by setting environment variables. + ## AWS CLI Configuration If you haven't already, install the [AWS CLI](https://aws.amazon.com/cli/) and configure it with your credentials: @@ -49,7 +55,7 @@ This will prompt you to enter your AWS Access Key ID, Secret Access Key, and def ## Environment Variables -Alternatively, you can set the following environment variables: +You can set the following environment variables: ```bash export AWS_REGION=your_preferred_region @@ -75,32 +81,15 @@ $env:AWS_SECRET_ACCESS_KEY = 'your_secret_key' $env:AWS_REGION = 'us-west-2' # Put whichever AWS region that you'd like, that the Bedrock service supports. ``` -## Install boto3 -The AWS Bedrock provider requires the `boto3` package in order to function correctly: - -```bash -pip install boto3 -``` - -To use aider installed via `pipx` with AWS Bedrock, you must add the `boto3` dependency to aider's virtual environment by running - -```bash -pipx inject aider-chat boto3 -``` - -You must install `boto3` dependency to aider's virtual environment installed via one-liner or uv by running - -```bash -uv tool run --from aider-chat pip install boto3 -``` - - -## Running Aider with Bedrock +## Get Started Once your AWS credentials are set up, you can run Aider with the `--model` command line switch, specifying the Bedrock model you want to use: ```bash +# Change directory into your codebase +cd /to/your/project + aider --model bedrock/anthropic.claude-3-5-sonnet-20240620-v1:0 ``` @@ -121,6 +110,20 @@ aider --list-models bedrock/ Make sure you have access to these models in your AWS account before attempting to use them with Aider. +## Install boto3 +You may need to install the `boto3` package. + +```bash +# If you installed with aider-install or `uv tool` +uv tool run --from aider-chat pip install boto3 + +# Or with pipx... +pipx inject aider-chat boto3 + +# Or with pip +pip install -U boto3 +``` + # More info For more information on Amazon Bedrock and its models, refer to the [official AWS documentation](https://docs.aws.amazon.com/bedrock/latest/userguide/what-is-bedrock.html). diff --git a/aider/website/docs/llms/cohere.md b/aider/website/docs/llms/cohere.md index 66ab3c842..ce3e1a795 100644 --- a/aider/website/docs/llms/cohere.md +++ b/aider/website/docs/llms/cohere.md @@ -10,13 +10,22 @@ Their Command-R+ model works well with aider as a *very basic* coding assistant. You'll need a [Cohere API key](https://dashboard.cohere.com/welcome/login). -To use **Command-R+**: +First, install aider: + +{% include install.md %} + +Then configure your API keys: ``` -python -m pip install -U aider-chat - export COHERE_API_KEY= # Mac/Linux setx COHERE_API_KEY # Windows, restart shell after setx +``` + +Start working with aider and Cohere on your codebase: + +```bash +# Change directory into your codebase +cd /to/your/project aider --model command-r-plus-08-2024 diff --git a/aider/website/docs/llms/deepseek.md b/aider/website/docs/llms/deepseek.md index c49c49c7e..0abbf51a9 100644 --- a/aider/website/docs/llms/deepseek.md +++ b/aider/website/docs/llms/deepseek.md @@ -9,13 +9,24 @@ Aider can connect to the DeepSeek.com API. To work with DeepSeek's models, you need to set the `DEEPSEEK_API_KEY` environment variable with your [DeepSeek API key](https://platform.deepseek.com/api_keys). The DeepSeek Chat V3 model has a top score on aider's code editing benchmark. -``` -python -m pip install -U aider-chat +First, install aider: +{% include install.md %} + +Then configure your API keys: + +``` export DEEPSEEK_API_KEY= # Mac/Linux setx DEEPSEEK_API_KEY # Windows, restart shell after setx - -# Use DeepSeek Chat v3 -aider --deepseek +``` + +Start working with aider and DeepSeek on your codebase: + +```bash +# Change directory into your codebase +cd /to/your/project + +# Use DeepSeek Chat v3 +aider --model deepseek/deepseek-chat ``` diff --git a/aider/website/docs/llms/gemini.md b/aider/website/docs/llms/gemini.md index 8d8234d88..261512fda 100644 --- a/aider/website/docs/llms/gemini.md +++ b/aider/website/docs/llms/gemini.md @@ -7,21 +7,43 @@ nav_order: 300 You'll need a [Gemini API key](https://aistudio.google.com/app/u/2/apikey). -``` -python -m pip install -U aider-chat +First, install aider: -# You may need to install google-generativeai -pip install -U google-generativeai +{% include install.md %} -# Or with pipx... -pipx inject aider-chat google-generativeai +Then configure your API keys: +```bash export GEMINI_API_KEY= # Mac/Linux setx GEMINI_API_KEY # Windows, restart shell after setx +``` -aider --model gemini/gemini-1.5-pro-latest +Start working with aider and Gemini on your codebase: + + +```bash +# Change directory into your codebase +cd /to/your/project + +# You can run the Gemini 2.5 Pro model with this shortcut: +aider --model gemini + +# You can run the Gemini 2.5 Pro Exp for free, with usage limits: +aider --model gemini-exp # List models available from Gemini aider --list-models gemini/ ``` +You may need to install the `google-generativeai` package. + +```bash +# If you installed with aider-install or `uv tool` +uv tool run --from aider-chat pip install google-generativeai + +# Or with pipx... +pipx inject aider-chat google-generativeai + +# Or with pip +pip install -U google-generativeai +``` diff --git a/aider/website/docs/llms/groq.md b/aider/website/docs/llms/groq.md index f258e6848..b8e60e719 100644 --- a/aider/website/docs/llms/groq.md +++ b/aider/website/docs/llms/groq.md @@ -10,13 +10,22 @@ The Llama 3 70B model works well with aider and is comparable to GPT-3.5 in code editing performance. You'll need a [Groq API key](https://console.groq.com/keys). -To use **Llama3 70B**: +First, install aider: + +{% include install.md %} + +Then configure your API keys: ``` -python -m pip install -U aider-chat - export GROQ_API_KEY= # Mac/Linux setx GROQ_API_KEY # Windows, restart shell after setx +``` + +Start working with aider and Groq on your codebase: + +```bash +# Change directory into your codebase +cd /to/your/project aider --model groq/llama3-70b-8192 diff --git a/aider/website/docs/llms/lm-studio.md b/aider/website/docs/llms/lm-studio.md index 909d3afe1..be9e53845 100644 --- a/aider/website/docs/llms/lm-studio.md +++ b/aider/website/docs/llms/lm-studio.md @@ -5,11 +5,15 @@ nav_order: 400 # LM Studio -To use LM Studio: +Aider can connect to models served by LM Studio. + +First, install aider: + +{% include install.md %} + +Then configure your API key and endpoint: ``` -python -m pip install -U aider-chat - # Must set a value here even if its a dummy value export LM_STUDIO_API_KEY=dummy-api-key # Mac/Linux setx LM_STUDIO_API_KEY dummy-api-key # Windows, restart shell after setx @@ -17,12 +21,19 @@ setx LM_STUDIO_API_KEY dummy-api-key # Windows, restart shell after setx # LM Studio default server URL is http://localhost:1234/v1 export LM_STUDIO_API_BASE=http://localhost:1234/v1 # Mac/Linux setx LM_STUDIO_API_BASE http://localhost:1234/v1 # Windows, restart shell after setx - -aider --model lm_studio/ ``` **Note:** Even though LM Studio doesn't require an API Key out of the box the `LM_STUDIO_API_KEY` must have a dummy value like `dummy-api-key` set or the client request will fail trying to send an empty `Bearer` token. +Start working with aider and LM Studio on your codebase: + +```bash +# Change directory into your codebase +cd /to/your/project + +aider --model lm_studio/ +``` + See the [model warnings](warnings.html) section for information on warnings which will occur when working with models that aider is not familiar with. diff --git a/aider/website/docs/llms/ollama.md b/aider/website/docs/llms/ollama.md index 014baa175..a9dbf6c07 100644 --- a/aider/website/docs/llms/ollama.md +++ b/aider/website/docs/llms/ollama.md @@ -7,18 +7,28 @@ nav_order: 500 Aider can connect to local Ollama models. +First, install aider: + +{% include install.md %} + +Then configure your Ollama API endpoint (usually the default): + +```bash +export OLLAMA_API_BASE=http://127.0.0.1:11434 # Mac/Linux +setx OLLAMA_API_BASE http://127.0.0.1:11434 # Windows, restart shell after setx +``` + +Start working with aider and Ollama on your codebase: + ``` # Pull the model ollama pull -# Start your ollama server -ollama serve +# Start your ollama server, increasing the context window to 8k tokens +OLLAMA_CONTEXT_LENGTH=8192 ollama serve -# In another terminal window... -python -m pip install -U aider-chat - -export OLLAMA_API_BASE=http://127.0.0.1:11434 # Mac/Linux -setx OLLAMA_API_BASE http://127.0.0.1:11434 # Windows, restart shell after setx +# In another terminal window, change directory into your codebase +cd /to/your/project aider --model ollama_chat/ ``` diff --git a/aider/website/docs/llms/openai-compat.md b/aider/website/docs/llms/openai-compat.md index e1b2a73f2..ea45a574f 100644 --- a/aider/website/docs/llms/openai-compat.md +++ b/aider/website/docs/llms/openai-compat.md @@ -7,10 +7,13 @@ nav_order: 500 Aider can connect to any LLM which is accessible via an OpenAI compatible API endpoint. -``` -python -m pip install aider-install -aider-install +First, install aider: +{% include install.md %} + +Then configure your API key and endpoint: + +``` # Mac/Linux: export OPENAI_API_BASE= export OPENAI_API_KEY= @@ -19,6 +22,13 @@ export OPENAI_API_KEY= setx OPENAI_API_BASE setx OPENAI_API_KEY # ... restart shell after setx commands +``` + +Start working with aider and your OpenAI compatible API on your codebase: + +```bash +# Change directory into your codebase +cd /to/your/project # Prefix the model name with openai/ aider --model openai/ diff --git a/aider/website/docs/llms/openai.md b/aider/website/docs/llms/openai.md index 638087d32..e88944644 100644 --- a/aider/website/docs/llms/openai.md +++ b/aider/website/docs/llms/openai.md @@ -10,27 +10,34 @@ To work with OpenAI's models, you need to provide your either in the `OPENAI_API_KEY` environment variable or via the `--api-key openai=` command line switch. -Aider has some built in shortcuts for the most popular OpenAI models and -has been tested and benchmarked to work well with them: +First, install aider: + +{% include install.md %} + +Then configure your API keys: ``` -python -m pip install -U aider-chat +export OPENAI_API_KEY= # Mac/Linux +setx OPENAI_API_KEY # Windows, restart shell after setx +``` + +Start working with aider and OpenAI on your codebase: + +```bash +# Change directory into your codebase +cd /to/your/project # o3-mini -aider --model o3-mini --api-key openai= +aider --model o3-mini # o1-mini -aider --model o1-mini --api-key openai= +aider --model o1-mini # GPT-4o -aider --4o --api-key openai= +aider --model gpt-4o # List models available from OpenAI aider --list-models openai/ - -# You can also store you API key in environment variables (or .env) -export OPENAI_API_KEY= # Mac/Linux -setx OPENAI_API_KEY # Windows, restart shell after setx ``` You can use `aider --model ` to use any other OpenAI model. diff --git a/aider/website/docs/llms/openrouter.md b/aider/website/docs/llms/openrouter.md index ecf18568d..e5e8a48cc 100644 --- a/aider/website/docs/llms/openrouter.md +++ b/aider/website/docs/llms/openrouter.md @@ -8,11 +8,22 @@ nav_order: 500 Aider can connect to [models provided by OpenRouter](https://openrouter.ai/models?o=top-weekly): You'll need an [OpenRouter API key](https://openrouter.ai/keys). -``` -python -m pip install -U aider-chat +First, install aider: +{% include install.md %} + +Then configure your API keys: + +``` export OPENROUTER_API_KEY= # Mac/Linux setx OPENROUTER_API_KEY # Windows, restart shell after setx +``` + +Start working with aider and OpenRouter on your codebase: + +```bash +# Change directory into your codebase +cd /to/your/project # Or any other open router model aider --model openrouter// @@ -23,16 +34,6 @@ aider --list-models openrouter/ In particular, many aider users access Sonnet via OpenRouter: -``` -python -m pip install -U aider-chat - -export OPENROUTER_API_KEY= # Mac/Linux -setx OPENROUTER_API_KEY # Windows, restart shell after setx - -aider --model openrouter/anthropic/claude-3.5-sonnet -``` - - {: .tip } If you get errors, check your [OpenRouter privacy settings](https://openrouter.ai/settings/privacy). @@ -54,7 +55,7 @@ Place that file in your home directory or the root of your git project, with entries like this: ```yaml -- name: openrouter/anthropic/claude-3.5-sonnet +- name: openrouter/anthropic/claude-3.7-sonnet extra_params: extra_body: provider: diff --git a/aider/website/docs/llms/vertex.md b/aider/website/docs/llms/vertex.md index b7afee42f..9dc82ea38 100644 --- a/aider/website/docs/llms/vertex.md +++ b/aider/website/docs/llms/vertex.md @@ -13,6 +13,10 @@ or service account with permission to use the Vertex AI API. With your chosen login method, the gcloud CLI should automatically set the `GOOGLE_APPLICATION_CREDENTIALS` environment variable which points to the credentials file. +First, install aider: + +{% include install.md %} + To configure Aider to use the Vertex AI API, you need to set `VERTEXAI_PROJECT` (the GCP project ID) and `VERTEXAI_LOCATION` (the GCP region) [environment variables for Aider](/docs/config/dotenv.html). @@ -27,9 +31,12 @@ VERTEXAI_PROJECT=my-project VERTEXAI_LOCATION=us-east5 ``` -Then you can run aider with the `--model` command line switch, like this: +Start working with aider and Vertex AI on your codebase: ``` +# Change directory into your codebase +cd /to/your/project + aider --model vertex_ai/claude-3-5-sonnet@20240620 ``` diff --git a/aider/website/docs/llms/xai.md b/aider/website/docs/llms/xai.md index a1fd48d0a..c2334fa3c 100644 --- a/aider/website/docs/llms/xai.md +++ b/aider/website/docs/llms/xai.md @@ -7,18 +7,47 @@ nav_order: 400 You'll need a [xAI API key](https://console.x.ai.). -To use xAI: +First, install aider: -``` -python -m pip install -U aider-chat +{% include install.md %} +Then configure your API keys: + +```bash export XAI_API_KEY= # Mac/Linux setx XAI_API_KEY # Windows, restart shell after setx +``` -aider --model xai/grok-beta +Start working with aider and xAI on your codebase: + +```bash +# Change directory into your codebase +cd /to/your/project + +# Grok 3 +aider --model xai/grok-3-beta + +# Grok 3 fast (faster, more expensive) +aider --model xai/grok-3-fast-beta + +# Grok 3 Mini +aider --model xai/grok-3-mini-beta + +# Grok 3 Mini fast (faster, more expensive) +aider --model xai/grok-3-mini-fast-beta # List models available from xAI aider --list-models xai/ ``` +The Grok 3 Mini models support the `--reasoning-effort` flag. +See the [reasoning settings documentation](../config/reasoning.md) for details. +Example: + +```bash +aider --model xai/grok-3-mini-beta --reasoning-effort high +``` + + + diff --git a/aider/website/docs/more/infinite-output.md b/aider/website/docs/more/infinite-output.md index 51e1470a0..be61226d6 100644 --- a/aider/website/docs/more/infinite-output.md +++ b/aider/website/docs/more/infinite-output.md @@ -71,6 +71,7 @@ cog.out(model_list) - claude-3-sonnet-20240229 - codestral/codestral-2405 - codestral/codestral-latest +- databricks/databricks-claude-3-7-sonnet - deepseek/deepseek-chat - deepseek/deepseek-coder - deepseek/deepseek-reasoner diff --git a/aider/website/docs/recordings/auto-accept-architect.md b/aider/website/docs/recordings/auto-accept-architect.md new file mode 100644 index 000000000..a2d741e22 --- /dev/null +++ b/aider/website/docs/recordings/auto-accept-architect.md @@ -0,0 +1,31 @@ +--- +parent: Screen recordings +nav_order: 1 +layout: minimal +highlight_image: /assets/recordings.jpg +description: See how a new command-line option is added to automatically accept edits proposed by the architect model, with implementation. Aider also updates the project's HISTORY file. +--- + +# Add --auto-accept-architect feature + + + +{% include recording.md %} + +## Commentary + +- 0:01 We're going to add a new feature to automatically accept edits proposed by the architect model. +- 0:11 First, let's add the new switch. +- 0:40 Aider figured out that it should be passed to the Coder class. +- 0:48 Now we need to implement the functionality. +- 1:00 Let's do some manual testing. +- 1:28 That worked. Let's make sure we can turn it off too. +- 1:42 That worked too. Let's have aider update the HISTORY file to document the new feature. +- 2:00 Let's quickly tidy up the changes to HISTORY. +- 2:05 All done! + + + diff --git a/aider/website/docs/recordings/dont-drop-original-read-files.md b/aider/website/docs/recordings/dont-drop-original-read-files.md new file mode 100644 index 000000000..675f4dc21 --- /dev/null +++ b/aider/website/docs/recordings/dont-drop-original-read-files.md @@ -0,0 +1,35 @@ +--- +parent: Screen recordings +nav_order: 1 +layout: minimal +highlight_image: /assets/recordings.jpg +description: Follow along as aider is modified to preserve read-only files specified at launch when using the /drop command. Aider does this implementation and adds test coverage. +--- + +# Don't /drop read-only files added at launch + + + +{% include recording.md %} + +## Commentary + +- 0:01 We're going to update the /drop command to keep any read only files that were originally specified at launch. +- 0:10 We've added files that handle the main CLI and in-chat slash commands like /drop. +- 0:20 Let's explain the needed change to aider. +- 1:20 Ok, let's look at the code. +- 1:30 I'd prefer not to use "hasattr()", let's ask for improvements. +- 1:45 Let's try some manual testing. +- 2:10 Looks good. Let's check the existing test suite to ensure we didn't break anything. +- 2:19 Let's ask aider to add tests for this. +- 2:50 Tests look reasonable, we're done! + + + + + + + diff --git a/aider/website/docs/recordings/index.md b/aider/website/docs/recordings/index.md new file mode 100644 index 000000000..ac549039d --- /dev/null +++ b/aider/website/docs/recordings/index.md @@ -0,0 +1,21 @@ +--- +title: Screen recordings +has_children: true +nav_order: 75 +has_toc: false +description: Screen recordings of aider building aider. +highlight_image: /assets/recordings.jpg +--- + +# Screen recordings + +Below are a series of screen recordings of the aider developer using aider +to enhance aider. +They contain commentary that describes how aider is being used, +and might provide some inspiration for your own use of aider. + +{% assign sorted_pages = site.pages | where: "parent", "Screen recordings" | sort: "nav_order" %} +{% for page in sorted_pages %} +- [{{ page.title }}]({{ page.url | relative_url }}) - {{ page.description }} +{% endfor %} + diff --git a/aider/website/docs/recordings/model-accepts-settings.md b/aider/website/docs/recordings/model-accepts-settings.md new file mode 100644 index 000000000..3cf5d3e20 --- /dev/null +++ b/aider/website/docs/recordings/model-accepts-settings.md @@ -0,0 +1,69 @@ +--- +parent: Screen recordings +nav_order: 1 +layout: minimal +highlight_image: /assets/recordings.jpg +description: Watch the implementation of a warning system that alerts users when they try to apply reasoning settings to models that don't support them. Includes adding model metadata, confirmation dialogs, refactoring, and comprehensive test coverage. +--- + +# Warn when users apply unsupported reasoning settings + + + +{% include recording.md %} + +## Commentary + +- 0:01 Users sometimes run aider with "reasoning" settings that aren't supported by the model they're using. This can cause LLM API calls to completely fail, with non-specific error messages from the API provider. We're going to warn users up front to prevent this. +- 0:25 Ok, let's ask aider to add a new model setting where we can note which reasoning settings it supports. And then print a warning if the user tries to apply an unsupported setting. +- 1:30 Looks like it's including some extra changes we don't want. +- 1:45 Let's have a look at the models code and clean up some stray lines. +- 2:00 It also made the warning logic too conservative. We want to warn unless the setting is explicitly known to be supported. +- 3:00 Ok, good. Now lets add a setting to silence these warnings for power users who are doing something intentional. +- 3:45 Now we need to update the database of model settings to annotate which models support which reasoning settings. We'll start with the code that handles "fallback" settings for known models on unknown providers. +- 4:45 Oh, we forgot to give aider the actual file with that code! Aider asks to see it. +- 5:00 Ok, we've confused aider by asking it to change code it couldn't see. +- 5:10 Let's clear the chat and refine the prompt and try again. +- 6:00 Ok, looks good. Let's move on and update the full model settings database YAML file. Each main model like "o1" appears here from many providers, like OpenAI, OpenRouter, etc. We want to update them all. +- 7:43 Let's interrupt and refine the prompt to be more clear about which models to update. +- 9:20 Looks good. Let's review the YAML file and eyeball all the relevant models. +- 10:20 Now let's do some manual testing. +- 10:41 Ok, it should not be warning us about using "thinking tokens" with Sonnet 3.7. +- 10:55 Let's see if aider can spot the problem? +- 11:28 That doesn't sound like a promising solution. Let's add more of the relevant code, clear history and try again. +- 12:00 Ok, let's try aider's proposed solution. +- 12:32 And see if it worked... Nope! Still getting the unneeded warning. Undo that change! +- 12:48 Time for some manual print debugging. +- 13:00 It seems like the "accept_settings" value is not being set? +- 14:30 Aha! I have a local model settings file for Sonnet which overrides aider's built in settings. And we did not update it. Let's add "accepts_settings" there. +- 14:45 That was the problem, it wasn't a bug. +- 14:59 Ok, let's add test coverage for all this stuff. +- 15:09 And while aider writes tests, let's use "git diff" to review all the changes we've made. +- 15:34 Aider is done writing tests, let's try them. +- 15:44 One passed, one failed. Let's eyeball the passing test first. +- 16:04 And let's see if aider can fix the failing test. +- 16:14 Aider needs to see another file, which makes sense. +- 16:29 It's found the problem, but is trying to "fix" the code. We want it to fix the test. +- 16:47 Ok, tests are passing. +- 16:55 We should stop and ask the user "are you sure?", not just flash a warning if they're about to break their API calls. +- 17:59 Ok, that confirmation dialog looks good. +- 18:35 This code is a little bit repetitive. Let's do a bit of refactoring. +- 19:44 Sonnet is messing up the code editing instructions, so aider is retrying. +- 19:54 Let's clear the chat history and try again. +- 20:25 Are tests still passing after the refactor? +- 20:55 Tests passed, good. Let's tweak the warning text. +- 21:10 And now let's have aider update the docs to explain these changes. +- 22:32 Let's proofread and edit the updated docs. +- 24:25 And a "git diff" of all the docs changes to do a final check. +- 24:56 Let's have aider update the project's HISTORY file. +- 25:35 We can refine the HISTORY entries a bit. +- 26:20 All done! + + + + + + diff --git a/aider/website/docs/recordings/tree-sitter-language-pack.md b/aider/website/docs/recordings/tree-sitter-language-pack.md new file mode 100644 index 000000000..f51ef0ad3 --- /dev/null +++ b/aider/website/docs/recordings/tree-sitter-language-pack.md @@ -0,0 +1,80 @@ +--- +parent: Screen recordings +nav_order: 0 +layout: minimal +highlight_image: /assets/recordings.jpg +description: Watch how aider adds support for tons of new programming languages by integrating with tree-sitter-language-pack. Demonstrates using aider to script downloading a collection of files, and using ad-hoc bash scripts to have aider modify a collection of files. +--- + +# Add language support via tree-sitter-language-pack + + + +{% include recording.md %} + + +## Commentary + +- 0:01 We're going to add a ton of new languages to aider via tree-sitter-language-pack. +- 0:10 First, lets try and find which languages it supports. +- 1:00 Ok, there's a language definitions json file +- 1:10 Does it have the github repos for each language? +- 1:29 Ok, this is what we need. +- 1:45 We need to get all the tags files from each repository for aider's repo-map. Let's have aider write a script to fetch them all. +- 2:05 We'll show aider the language definitions json file. +- 3:37 Looks like it can't find most of the tags.scm files. +- 4:19 Maybe we should have it try other branches besides master? +- 5:02 Ok, it seems to be downloading them now. +- 5:55 Let's make it so we can re-run the script and only download files we haven't fetched yet. +- 6:12 I see lots of tags files, so it's working. +- 6:30 Ok, restart to run with latest code. This will take awhile to fetch them all. +- 9:02 The Grep-AST module needs to know about all the new languages. +- 9:45 Let's have aider add them all, and register each using their commonly used file extensions. +- 10:15 Some of the languages need to be recognized by their base name, not by their extension. +- 11:15 Let's sanity check if Grep-AST can handle PowerShell, one of the new languages. +- 12:00 Looks like it's parsing PowerShell fine. +- 13:00 Ok, let's download all the tags files into the right spot in the aider repo. +- 14:00 This will take a minute... +- 16:07 Delete some no-op or empty tags files. +- 16:16 Let's commit all the unmodified tags files. +- 16:33 We need to update each tag file, so that aider can identify names of functions, classes, etc in all these languages. +- 17:01 Let's use a bash loop to script aider to modify each tags file. +- 17:12 I'm giving aider a read-only example of an already modified tags file, as an example to follow. +- 19:04 Looks like it correctly updated the first couple of tags files. +- 19:28 Let's grep to watch aider's progress working through the list of files. +- 20:20 It's working on the Dart language now... +- 20:50 E-lisp is up next... +- 21:30 This is going to take a little while... +- 24:39 Let's add a README file with attribution for these tags files. +- 26:55 Ok, all the files are updated with tags for definitions and references to named code objects. +- 27:10 Let's add test coverage to be sure these languages work with the repo-map. +- 27:19 Each language needs a "fixture" with some sample code to parse during the test. Let's show aider the layout of the fixtures directory. +- 27:50 We can use a bash loop to ask aider to add test coverage for each new tags file. +- 28:12 We'll pass the fixtures directory listing to aider. +- 28:52 Just need to fix the bash to correctly iterate through the list of tags files. +- 29:27 I forgot to ask aider to actually generate a sample code fixture for each language. +- 30:25 Lets run the repo-map tests to see if the first new test works. +- 30:37 Tests for the Arduino language failed, with an empty repo-map? That's not good. +- 31:52 Can aider figure out what's wrong? +- 32:27 Well, aider made the test pass by basically skipping Arduino. +- 32:36 Let me see if I can use Grep-AST on the new Arduino fixture code. +- 32:42 Oh! I'm not using the updated Grep-AST that knows about all the new languages. +- 32:54 Ok, now we're parsing Arduino code properly. Undo aider's bogus test fix. +- 33:05 Ok, arduino passes now but there seems to be a regression with tsx? +- 33:20 Can aider figure out why? +- 34:10 Let's check the parsers map. +- 35:00 Well, that's all for this recording. The tsx problem was due to a bad mapping from ".tsx" to "typescript" in the map that aider generated earlier. + + + + + + + + + + + diff --git a/aider/website/docs/repomap.md b/aider/website/docs/repomap.md index 4174860f9..900c31f1a 100644 --- a/aider/website/docs/repomap.md +++ b/aider/website/docs/repomap.md @@ -87,6 +87,8 @@ Aider optimizes the repo map by selecting the most important parts of the codebase which will fit into the active token budget. +The optimization identifies and maps the portions of the code base +which are most relevant to the current state of the chat. The token budget is influenced by the `--map-tokens` switch, which defaults to 1k tokens. diff --git a/aider/website/docs/troubleshooting/edit-errors.md b/aider/website/docs/troubleshooting/edit-errors.md index a6de214e3..cf28fd9e1 100644 --- a/aider/website/docs/troubleshooting/edit-errors.md +++ b/aider/website/docs/troubleshooting/edit-errors.md @@ -35,8 +35,8 @@ Aider also sends the LLM a [map of your entire git repo](https://aider.chat/docs ## Use a more capable model -If possible try using GPT-4o, Claude 3.5 Sonnet, DeepSeek V3 or DeepSeek R1. -They are the strongest and most capable models. +If possible try using GPT-4o, o3-mini, Claude 3.7 Sonnet, DeepSeek V3 or DeepSeek R1. +They are the strong and capable models. Weaker models are more prone to diff --git a/aider/website/docs/troubleshooting/models-and-keys.md b/aider/website/docs/troubleshooting/models-and-keys.md index 1ee24733c..7b80ec3e4 100644 --- a/aider/website/docs/troubleshooting/models-and-keys.md +++ b/aider/website/docs/troubleshooting/models-and-keys.md @@ -5,7 +5,29 @@ nav_order: 28 # Models and API keys -You need to tell aider which LLM to use and provide an API key. +Aider needs to know which LLM model you would like to work with and which keys +to provide when accessing it via API. + +## Defaults + +If you don't explicitly name a model, aider will try to select a model +for you to work with. + +First, aider will check which +[keys you have provided via the environment, config files, or command line arguments](https://aider.chat/docs/config/api-keys.html). +Based on the available keys, aider will select the best model to use. + +## OpenRouter + +If you have not provided any keys, aider will offer to help you connect to +[OpenRouter](http://openrouter.ai) +which provides both free and paid access to most popular LLMs. +Once connected, aider will select the best model available on OpenRouter +based on whether you have a free or paid account there. + +## Specifying model & key + +You can also tell aider which LLM to use and provide an API key. The easiest way is to use the `--model` and `--api-key` command line arguments, like this: @@ -13,14 +35,14 @@ command line arguments, like this: # Work with DeepSeek via DeepSeek's API aider --model deepseek --api-key deepseek=your-key-goes-here -# Work with Claude 3.5 Sonnet via Anthropic's API +# Work with Claude 3.7 Sonnet via Anthropic's API aider --model sonnet --api-key anthropic=your-key-goes-here # Work with o3-mini via OpenAI's API aider --model o3-mini --api-key openai=your-key-goes-here # Work with Sonnet via OpenRouter's API -aider --model openrouter/anthropic/claude-3.5-sonnet --api-key openrouter=your-key-goes-here +aider --model openrouter/anthropic/claude-3.7-sonnet --api-key openrouter=your-key-goes-here # Work with DeepSeek Chat V3 via OpenRouter's API aider --model openrouter/deepseek/deepseek-chat --api-key openrouter=your-key-goes-here diff --git a/aider/website/docs/usage.md b/aider/website/docs/usage.md index 76f0ac980..f3a5b3dee 100644 --- a/aider/website/docs/usage.md +++ b/aider/website/docs/usage.md @@ -71,7 +71,7 @@ relevant context from the rest of your repo. # o3-mini $ aider --model o3-mini --api-key openai= -# Claude 3.5 Sonnet +# Claude 3.7 Sonnet $ aider --model sonnet --api-key anthropic= ``` diff --git a/aider/website/docs/usage/browser.md b/aider/website/docs/usage/browser.md index a43122702..ae153dc29 100644 --- a/aider/website/docs/usage/browser.md +++ b/aider/website/docs/usage/browser.md @@ -41,8 +41,8 @@ Aider will directly edit the code in your local source files, and [git commit the changes](https://aider.chat/docs/git.html) with sensible commit messages. You can start a new project or work with an existing git repo. -Aider works well with GPT 3.5, GPT-4, GPT-4 Turbo with Vision, -and Claude 3 Opus. +Aider works well with +GPT-4o, Sonnet 3.7, and DeepSeek Chat V3 & R1. It also supports [connecting to almost any LLM](https://aider.chat/docs/llms.html). Use the `--browser` switch to launch the browser version of aider: diff --git a/aider/website/docs/usage/commands.md b/aider/website/docs/usage/commands.md index b73d82ac8..9a0caca19 100644 --- a/aider/website/docs/usage/commands.md +++ b/aider/website/docs/usage/commands.md @@ -28,11 +28,14 @@ cog.out(get_help_md()) | **/clear** | Clear the chat history | | **/code** | Ask for changes to your code. If no prompt provided, switches to code mode. | | **/commit** | Commit edits to the repo made outside the chat (commit message optional) | +| **/context** | Enter context mode to see surrounding code context. If no prompt provided, switches to context mode. | | **/copy** | Copy the last assistant message to the clipboard | | **/copy-context** | Copy the current chat context as markdown, suitable to paste into a web UI | | **/diff** | Display the diff of changes since the last message | | **/drop** | Remove files from the chat session to free up context space | +| **/edit** | Alias for /editor: Open an editor to write a prompt | | **/editor** | Open an editor to write a prompt | +| **/editor-model** | Switch the Editor Model to a new LLM | | **/exit** | Exit the application | | **/git** | Run a git command (output excluded from chat) | | **/help** | Ask questions about aider | @@ -41,21 +44,24 @@ cog.out(get_help_md()) | **/ls** | List all known files and indicate which are included in the chat session | | **/map** | Print out the current repository map | | **/map-refresh** | Force a refresh of the repository map | -| **/model** | Switch to a new LLM | +| **/model** | Switch the Main Model to a new LLM | | **/models** | Search the list of available models | | **/multiline-mode** | Toggle multiline mode (swaps behavior of Enter and Meta+Enter) | | **/paste** | Paste image/text from the clipboard into the chat. Optionally provide a name for the image. | | **/quit** | Exit the application | | **/read-only** | Add files to the chat that are for reference only, or turn added files to read-only | +| **/reasoning-effort** | Set the reasoning effort level (values: number or low/medium/high depending on model) | | **/report** | Report a problem by opening a GitHub Issue | | **/reset** | Drop all files and clear the chat history | | **/run** | Run a shell command and optionally add the output to the chat (alias: !) | | **/save** | Save commands to a file that can reconstruct the current chat session's files | | **/settings** | Print out the current settings | | **/test** | Run a shell command and add the output to the chat on non-zero exit code | +| **/think-tokens** | Set the thinking token budget (supports formats like 8096, 8k, 10.5k, 0.5M) | | **/tokens** | Report on the number of tokens used by the current chat context | | **/undo** | Undo the last git commit if it was done by aider | | **/voice** | Record and transcribe voice input | +| **/weak-model** | Switch the Weak Model to a new LLM | | **/web** | Scrape a webpage, convert to markdown and send in a message | @@ -93,6 +99,8 @@ The interactive prompt is built with [prompt-toolkit](https://github.com/prompt- - `Ctrl-N` : Move down to the next history entry. - `Ctrl-P` : Move up to the previous history entry. - `Ctrl-R` : Reverse search in command history. +- `Ctrl-X Ctrl-E` : Open the current input in an external editor +- `Ctrl-Y` : Paste (yank) text that was previously cut. ### Vi diff --git a/aider/website/docs/usage/images-urls.md b/aider/website/docs/usage/images-urls.md index 5b750b498..beda151d4 100644 --- a/aider/website/docs/usage/images-urls.md +++ b/aider/website/docs/usage/images-urls.md @@ -11,7 +11,7 @@ You can add images and URLs to the aider chat. ## Images Aider supports working with image files for many vision-capable models -like GPT-4o and Claude 3.5 Sonnet. +like GPT-4o and Claude 3.7 Sonnet. Adding images to a chat can be helpful in many situations: - Add screenshots of web pages or UIs that you want aider to build or modify. diff --git a/aider/website/docs/usage/lint-test.md b/aider/website/docs/usage/lint-test.md index 4c18baf8e..0dfdccbae 100644 --- a/aider/website/docs/usage/lint-test.md +++ b/aider/website/docs/usage/lint-test.md @@ -29,6 +29,33 @@ This is how most linters normally operate. By default, aider will lint any files which it edits. You can disable this with the `--no-auto-lint` switch. +### Per-language linters + +To specify different linters based on the code language, use `--lint "language: cmd"`. + +### Code formatting "linters" + +Many people use code formatters as linters, to format and pretty their code. +These tools sometimes return non-zero exit codes if they make changes, which will +confuse aider into thinking there's an actual lint error that needs to be fixed. + +You can use formatters by wrapping them in a shell script like this and setting +the script as your linter. + +```bash +#!/bin/bash + +# Run it twice. +# +# First attempt may reformat/modify files, and therefore exit with non-zero status. +# +# Second attempt will not do anything and exit 0 unless there's a real problem beyond +# the code formatting that was completed. + +pre-commit run --files $* >/dev/null \ + || pre-commit run --files $* +``` + ## Testing You can run tests with `/test `. diff --git a/aider/website/docs/usage/modes.md b/aider/website/docs/usage/modes.md index f3d9a3359..e5e3d2db6 100644 --- a/aider/website/docs/usage/modes.md +++ b/aider/website/docs/usage/modes.md @@ -9,9 +9,9 @@ description: Using the code, architect, ask and help chat modes. Aider has a few different chat modes: - `code` - Aider will make changes to your code to satisfy your requests. -- `architect` - Aider will first propose a solution, then ask if you want it to turn that proposal into edits to your files. -- `ask` - Aider will answer questions about your code, but never edit it. -- `help` - Aider will answer questions about using aider, configuring, troubleshooting, etc. +- `ask` - Aider will discuss your code and answer questions about it, but never make changes. +- `architect` - Like code mode, aider will change your files. An architect model will propose changes and an editor model will translate that proposal into specific file edits. +- `help` - Aider will answer questions about aider: usage, configuration, troubleshooting, etc. By default, aider starts in "code" mode. As you are talking, you can send individual messages in a specific mode using @@ -40,31 +40,104 @@ Or you can switch between coding modes using these commands without arguments: Or you can launch aider in one of the modes with the `--chat-mode ` switch. There is also a special shortcut `--architect` to launch in `--chat-mode architect`. +The aider prompt will indicate the active mode: + +``` +> This is code mode. +ask> This is ask mode. +architect> This is architect mode. +``` + + +## Ask/code workflow + +A recommended workflow is to bounce back and forth between `/ask` and `/code` modes. + +Use ask mode to discuss what you want to do, get suggestions or options from aider +and provide feedback on the approach. +Once aider understands the mission, switch to code mode to have it start editing +your files. +All the conversation and decision making from ask mode will +help ensure that the correct code changes are performed. + +You can be very terse when you finally switch from ask to code mode. +Saying something as simple as "go ahead" in code mode will +have aider execute on the plan you've been discussing. + +Here's an example with two ask mode messages to agree on the plan, +followed by two terse code mode messages to edit the code. + +```` +───────────────────────────────────────────────────────────────────────────────────── +Aider v0.79.0 +Model: gemini/gemini-2.5-pro-exp-03-25 with diff-fenced edit format + +> /ask What's the best thing to print if we're making a quick little demo program? + +It sounds like you'd like to write a "Hello, world!" program. That's a very +popular phrase to use for small demo programs. + +> /ask Yes, that's the idea. What are some more creative options? + +1. "Greetings, Earthlings!" +2. "Beep boop, I am a computer!" +3. "To code, or not to code, that is the question." +4. "I think, therefore I print." +5. "01001000 01101001!" (Binary for "Hi!") + +> do 4 + +hello.py +>>>>>>> SEARCH +======= +def main(): + print("I think, therefore I print.") +<<<<<<< REPLACE + +> ALL CAPS! + +hello.py +>>>>>>> SEARCH + print("I think, therefore I print.") +======= + print("I THINK, THEREFORE I PRINT!") +<<<<<<< REPLACE + +```` + +You can think of this ask/code workflow as a more fluid version of +architect mode, but working just with one model the whole time. + ## Architect mode and the editor model -When you are in architect mode, aider sends your request to two models: +When you are in architect mode, aider sends your requests to two models: -1. First, it sends your request to the main active model. -The main model is configured with `/model`, `--model` or the shortcut switches like `--sonnet`. -After the main model replies, aider will offer to edit the files based on the response. +1. First, it sends your request to the main model which will act as an architect +to propose how to solve your coding request. +The main model is configured with `/model` or `--model`. -2. To edit the files, aider sends a second LLM request asking for specific code editing instructions. -This request goes to the "editor" model. +2. Aider then sends another request to an "editor model", +asking it to turn the architect's proposal into specific file editing instructions. Aider has built in defaults to select an editor model based on your main model. -Or, you can choose an editor model yourself with `--editor-model `. +Or, you can choose a specific editor model with `--editor-model `. -Architect mode produces better results than code mode, but uses two LLM requests. -This probably makes it slower and more expensive than using code mode. +Certain LLMs aren't able to propose coding solutions *and* +specify detailed file edits all in one go. +For these models, architect mode can produce better results than code mode +by pairing them +with an editor model that is responsible for generating the file editing instructions. +But this uses two LLM requests, +which can take longer and increase costs. Architect mode is especially useful with OpenAI's o1 models, which are strong at reasoning but less capable at editing files. Pairing an o1 architect with an editor model like GPT-4o or Sonnet will give the best results. -But architect mode is also quite helpful when you use GPT-4o or Sonnet -at both the architect and the editor. +But architect mode can also be helpful when you use the same model +as both the architect and the editor. Allowing the model two requests to solve the problem and edit the files -usually provides a better result. +can sometimes provide better results. The editor model uses one of aider's edit formats to let the LLM edit source files. @@ -91,9 +164,9 @@ for more details. #### /ask What is this repo? -This is the source code to the popular django package. +This is collection of python functions that compute various math functions. -#### /help How do I use ollama? +#### /help How do I use aider with ollama? Run `aider --model ollama/`. See these docs for more info: https://aider.chat/docs/llms/ollama.html @@ -122,8 +195,6 @@ builtin. This way you don't have to maintain a custom factorial implementation, and the builtin function is well optimized. -> Edit the files? (Y)es/(N)o [Yes]: Yes - ```python <<<<<<< SEARCH def factorial(n): diff --git a/aider/website/docs/usage/notifications.md b/aider/website/docs/usage/notifications.md new file mode 100644 index 000000000..ee7febb72 --- /dev/null +++ b/aider/website/docs/usage/notifications.md @@ -0,0 +1,87 @@ +--- +title: Notifications +highlight_image: /assets/notifications.jpg +parent: Usage +nav_order: 760 +description: Aider can notify you when it's waiting for your input. +--- + +# Notifications + +Aider can notify you when it's done working and is +waiting for your input. +This is especially useful for long-running operations or when you're multitasking. + +## Usage + +Enable notifications with the `--notifications` flag: + +```bash +aider --notifications +``` + +When enabled, aider will notify you when the LLM has finished generating a response and is waiting for your input. + +## OS-Specific Notifications + +Aider automatically detects your operating system and uses an appropriate notification method: + +- **macOS**: Uses `terminal-notifier` if available, falling back to AppleScript notifications +- **Linux**: Uses `notify-send` or `zenity` if available +- **Windows**: Uses PowerShell to display a message box + +## Custom Notification Commands + +You can specify a custom notification command with `--notifications-command`: + +```bash +aider --notifications-command "your-custom-command" +``` + +For example, on macOS you might use: + +```bash +aider --notifications-command "say 'Aider is ready'" +``` + +### Remote Notifications + +For remote notifications you could use [Apprise](https://github.com/caronc/apprise), +which is a cross-platform Python library for sending notifications to various services. + +We can use Apprise to send notifications to Slack + +```bash +aider --notifications-command "apprise -b 'Aider is ready' 'slack://your-slack-webhook-token'" +``` + +or Discord +```bash +aider --notifications-command "apprise -b 'Aider is ready' 'discord://your-discord-webhook-token'" +``` + +or even to your phone via Pushbullet +```bash +aider --notifications-command "apprise -b 'Aider is ready' 'pbul://your-pushbullet-access-token'" +``` + +Check more how to use and configure Apprise on their GitHub page. + +## Configuration + +You can add these settings to your configuration file: + +```yaml +# Enable notifications +notifications: true + +# Optional custom notification command +notifications_command: "your-custom-command" +``` + +Or in your `.env` file: + +``` +AIDER_NOTIFICATIONS=true +AIDER_NOTIFICATIONS_COMMAND=your-custom-command +``` diff --git a/aider/website/index.html b/aider/website/index.html new file mode 100644 index 000000000..50947389d --- /dev/null +++ b/aider/website/index.html @@ -0,0 +1,688 @@ +--- +layout: none +--- + + + + + + Aider - AI Pair Programming in Your Terminal + + + + + + + + + + + + +
    + +
    + +
    +
    +
    +
    +

    AI pair programming in your terminal

    +

    + Aider lets you pair program with LLMs to start + a new project or build on your existing + codebase. +

    + + +
    + +
    +
    + +
    +
    +
    + + +
    +
    + +
    +
    +

    Features

    +
    + +
    + +

    + Cloud and local LLMs +

    +
    +

    Aider works best with Claude 3.7 Sonnet, DeepSeek R1 & Chat V3, OpenAI o1, o3-mini & GPT-4o, but can connect to almost any LLM, including local models.

    +
    + +
    + +

    Maps your codebase

    +
    +

    + Aider makes a map of your entire codebase, + which helps it work well in larger projects. +

    +
    + +
    + +

    100+ code languages

    +
    +

    + Aider works with most popular programming + languages: python, javascript, rust, ruby, go, cpp, + php, html, css, and dozens more. +

    +
    + +
    + +

    Git integration

    +
    +

    + Aider automatically commits changes with + sensible commit messages. + Use familiar git tools to easily + diff, manage and undo AI changes. +

    +
    + +
    + +

    In your IDE

    +
    +

    + Use aider from within your favorite IDE or editor. + Ask for changes by adding comments to + your code and aider will get to work. +

    +
    + +
    + +

    Images & web pages

    +
    +

    + Add images and web pages to the chat to + provide visual context, screenshots, reference docs, + etc. +

    +
    + +
    + +

    Voice-to-code

    +
    +

    + Speak with aider about your code! Request new features, + test cases or bug fixes using your voice and let aider + implement the changes. +

    +
    + +
    + +

    Linting & testing

    +
    +

    + Automatically lint and test your code every time aider makes changes. + Aider can fix problems detected by your linters and test suites. +

    +
    + +
    + +

    Copy/paste to web chat

    +
    +

    + Aider works best with LLM APIs, + but it can also work an LLM via its web chat interface. + Aider streamlines copy/pasting code + back and forth with a browser. +

    +
    +
    +
    +
    + +
    +
    +

    Getting Started

    +
    +
    +
    python -m pip install aider-install
    +aider-install
    +
    +# Change directory into your codebase
    +cd /to/your/project
    +
    +# DeepSeek
    +aider --model deepseek --api-key deepseek=<key>
    +
    +# Claude 3.7 Sonnet
    +aider --model sonnet --api-key anthropic=<key>
    +
    +# o3-mini
    +aider --model o3-mini --api-key openai=<key>
    +
    +
    +

    Want more details?

    + +
    +
    +
    +
    + +
    +
    +

    Kind Words From Users

    +
    + +
    +
    +
    + + + + + + + + +
    +
    +

    More Information

    +
    +
    +

    Documentation

    +

    Everything you need to get started and make the most of Aider

    + +
    +
    +

    Community & Resources

    +

    Connect with other users and find additional resources

    + +
    +
    +
    +
    + + + + + + + + + + + diff --git a/aider/website/index.md b/aider/website/index.md deleted file mode 100644 index 5fb3e40ff..000000000 --- a/aider/website/index.md +++ /dev/null @@ -1,172 +0,0 @@ ---- -title: Home -nav_order: 1 ---- - - - - - -# Aider is AI pair programming in your terminal - -Aider lets you pair program with LLMs, -to edit code in your local git repository. -Start a new project or work with an existing code base. -Aider works best with Claude 3.5 Sonnet, DeepSeek R1 & Chat V3, OpenAI o1, o3-mini & GPT-4o. Aider can [connect to almost any LLM, including local models](https://aider.chat/docs/llms.html). - - - - -

    - -

    - - -

    - - - - - - -

    - -## Getting started - - -If you already have python 3.8-3.13 installed, you can get started quickly like this: - -```bash -python -m pip install aider-install -aider-install - -# Change directory into your code base -cd /to/your/project - -# Work with DeepSeek via DeepSeek's API -aider --model deepseek --api-key deepseek=your-key-goes-here - -# Work with Claude 3.5 Sonnet via Anthropic's API -aider --model sonnet --api-key anthropic=your-key-goes-here - -# Work with GPT-4o via OpenAI's API -aider --model gpt-4o --api-key openai=your-key-goes-here - -# Work with Sonnet via OpenRouter's API -aider --model openrouter/anthropic/claude-3.5-sonnet --api-key openrouter=your-key-goes-here - -# Work with DeepSeek via OpenRouter's API -aider --model openrouter/deepseek/deepseek-chat --api-key openrouter=your-key-goes-here -``` - - -See the -[installation instructions](https://aider.chat/docs/install.html) -and -[usage documentation](https://aider.chat/docs/usage.html) -for more details. - -## Features - -- Run aider with the files you want to edit: `aider ...` -- Ask for changes: - - Add new features or test cases. - - Describe a bug. - - Paste in an error message or GitHub issue URL. - - Refactor code. - - Update docs. -- Aider will edit your files to complete your request. -- Aider [automatically git commits](https://aider.chat/docs/git.html) changes with a sensible commit message. -- [Use aider inside your favorite editor or IDE](https://aider.chat/docs/usage/watch.html). -- Aider works with [most popular languages](https://aider.chat/docs/languages.html): python, javascript, typescript, php, html, css, and more... -- Aider can edit multiple files at once for complex requests. -- Aider uses a [map of your entire git repo](https://aider.chat/docs/repomap.html), which helps it work well in larger codebases. -- Edit files in your editor or IDE while chatting with aider, -and it will always use the latest version. -Pair program with AI. -- [Add images to the chat](https://aider.chat/docs/usage/images-urls.html) (GPT-4o, Claude 3.5 Sonnet, etc). -- [Add URLs to the chat](https://aider.chat/docs/usage/images-urls.html) and aider will read their content. -- [Code with your voice](https://aider.chat/docs/usage/voice.html). -- Aider works best with Claude 3.5 Sonnet, DeepSeek V3, o1 & GPT-4o and can [connect to almost any LLM](https://aider.chat/docs/llms.html). - - -## Top tier performance - -[Aider has one of the top scores on SWE Bench](https://aider.chat/2024/06/02/main-swe-bench.html). -SWE Bench is a challenging software engineering benchmark where aider -solved *real* GitHub issues from popular open source -projects like django, scikitlearn, matplotlib, etc. - -## More info - -- [Documentation](https://aider.chat/) -- [Installation](https://aider.chat/docs/install.html) -- [Usage](https://aider.chat/docs/usage.html) -- [Tutorial videos](https://aider.chat/docs/usage/tutorials.html) -- [Connecting to LLMs](https://aider.chat/docs/llms.html) -- [Configuration](https://aider.chat/docs/config.html) -- [Troubleshooting](https://aider.chat/docs/troubleshooting.html) -- [LLM Leaderboards](https://aider.chat/docs/leaderboards/) -- [GitHub](https://github.com/Aider-AI/aider) -- [Discord](https://discord.gg/Tv2uQnR88V) -- [Blog](https://aider.chat/blog/) - - -## Kind words from users - -- *The best free open source AI coding assistant.* -- [IndyDevDan](https://youtu.be/YALpX8oOn78) -- *The best AI coding assistant so far.* -- [Matthew Berman](https://www.youtube.com/watch?v=df8afeb1FY8) -- *Aider ... has easily quadrupled my coding productivity.* -- [SOLAR_FIELDS](https://news.ycombinator.com/item?id=36212100) -- *It's a cool workflow... Aider's ergonomics are perfect for me.* -- [qup](https://news.ycombinator.com/item?id=38185326) -- *It's really like having your senior developer live right in your Git repo - truly amazing!* -- [rappster](https://github.com/Aider-AI/aider/issues/124) -- *What an amazing tool. It's incredible.* -- [valyagolev](https://github.com/Aider-AI/aider/issues/6#issue-1722897858) -- *Aider is such an astounding thing!* -- [cgrothaus](https://github.com/Aider-AI/aider/issues/82#issuecomment-1631876700) -- *It was WAY faster than I would be getting off the ground and making the first few working versions.* -- [Daniel Feldman](https://twitter.com/d_feldman/status/1662295077387923456) -- *THANK YOU for Aider! It really feels like a glimpse into the future of coding.* -- [derwiki](https://news.ycombinator.com/item?id=38205643) -- *It's just amazing. It is freeing me to do things I felt were out my comfort zone before.* -- [Dougie](https://discord.com/channels/1131200896827654144/1174002618058678323/1174084556257775656) -- *This project is stellar.* -- [funkytaco](https://github.com/Aider-AI/aider/issues/112#issuecomment-1637429008) -- *Amazing project, definitely the best AI coding assistant I've used.* -- [joshuavial](https://github.com/Aider-AI/aider/issues/84) -- *I absolutely love using Aider ... It makes software development feel so much lighter as an experience.* -- [principalideal0](https://discord.com/channels/1131200896827654144/1133421607499595858/1229689636012691468) -- *I have been recovering from multiple shoulder surgeries ... and have used aider extensively. It has allowed me to continue productivity.* -- [codeninja](https://www.reddit.com/r/OpenAI/s/nmNwkHy1zG) -- *I am an aider addict. I'm getting so much more work done, but in less time.* -- [dandandan](https://discord.com/channels/1131200896827654144/1131200896827654149/1135913253483069470) -- *After wasting $100 on tokens trying to find something better, I'm back to Aider. It blows everything else out of the water hands down, there's no competition whatsoever.* -- [SystemSculpt](https://discord.com/channels/1131200896827654144/1131200896827654149/1178736602797846548) -- *Aider is amazing, coupled with Sonnet 3.5 it’s quite mind blowing.* -- [Josh Dingus](https://discord.com/channels/1131200896827654144/1133060684540813372/1262374225298198548) -- *Hands down, this is the best AI coding assistant tool so far.* -- [IndyDevDan](https://www.youtube.com/watch?v=MPYFPvxfGZs) -- *[Aider] changed my daily coding workflows. It's mind-blowing how a single Python application can change your life.* -- [maledorak](https://discord.com/channels/1131200896827654144/1131200896827654149/1258453375620747264) -- *Best agent for actual dev work in existing codebases.* -- [Nick Dobos](https://twitter.com/NickADobos/status/1690408967963652097?s=20) - diff --git a/benchmark/README.md b/benchmark/README.md index b9e1b1e43..7765c00b7 100644 --- a/benchmark/README.md +++ b/benchmark/README.md @@ -43,7 +43,7 @@ These steps only need to be done once. ``` # Clone the aider repo -git clone git@github.com:Aider-AI/aider.git +git clone https://github.com/Aider-AI/aider.git # Create the scratch dir to hold benchmarking results inside the main aider dir: cd aider @@ -82,6 +82,7 @@ You can run `./benchmark/benchmark.py --help` for a list of all the arguments, b - `--threads` specifies how many exercises to benchmark in parallel. Start with a single thread if you are working out the kinks on your benchmarking setup or working with a new model, etc. Once you are getting reliable results, you can speed up the process by running with more threads. 10 works well against the OpenAI APIs. - `--num-tests` specifies how many of the tests to run before stopping. This is another way to start gently as you debug your benchmarking setup. - `--keywords` filters the tests to run to only the ones whose name match the supplied argument (similar to `pytest -k xxxx`). +- `--read-model-settings=` specify model settings, see here: https://aider.chat/docs/config/adv-model-settings.html#model-settings ### Benchmark report diff --git a/benchmark/benchmark.py b/benchmark/benchmark.py index f05c4b039..a3c2ca850 100755 --- a/benchmark/benchmark.py +++ b/benchmark/benchmark.py @@ -206,6 +206,12 @@ def main( read_model_settings: str = typer.Option( None, "--read-model-settings", help="Load aider model settings from YAML file" ), + reasoning_effort: Optional[str] = typer.Option( + None, "--reasoning-effort", help="Set reasoning effort for models that support it" + ), + thinking_tokens: Optional[int] = typer.Option( + None, "--thinking-tokens", help="Set thinking tokens for models that support it" + ), exercises_dir: str = typer.Option( EXERCISES_DIR_DEFAULT, "--exercises-dir", help="Directory with exercise files" ), @@ -362,6 +368,8 @@ def main( editor_edit_format, num_ctx, sleep, + reasoning_effort, + thinking_tokens, ) all_results.append(results) @@ -384,6 +392,10 @@ def main( replay, editor_model, editor_edit_format, + num_ctx, + sleep, + reasoning_effort, + thinking_tokens, ) all_results = run_test_threaded.gather(tqdm=True) @@ -481,6 +493,8 @@ def summarize_results(dirname, stats_languages=None): res.indentation_errors = 0 res.lazy_comments = 0 + res.reasoning_effort = None + res.thinking_tokens = None variants = defaultdict(set) for results in all_results: @@ -509,6 +523,9 @@ def summarize_results(dirname, stats_languages=None): res.syntax_errors += results.get("syntax_errors", 0) res.indentation_errors += results.get("indentation_errors", 0) + res.reasoning_effort = results.get("reasoning_effort") + res.thinking_tokens = results.get("thinking_tokens") + for key in "model edit_format commit_hash editor_model editor_edit_format".split(): val = results.get(key) if val: @@ -552,6 +569,11 @@ def summarize_results(dirname, stats_languages=None): setattr(res, key, val) console.print(f" {key}: {val}", style=style) + if res.reasoning_effort is not None: + print(f" reasoning_effort: {res.reasoning_effort}") + if res.thinking_tokens is not None: + print(f" thinking_tokens: {res.thinking_tokens}") + for i in range(tries): print(f" pass_rate_{i + 1}: {percents[i]:.1f}") for i in range(tries): @@ -637,15 +659,14 @@ def get_replayed_content(replay_dname, test_dname): def run_test(original_dname, testdir, *args, **kwargs): try: return run_test_real(original_dname, testdir, *args, **kwargs) - except Exception as err: + except Exception: print("=" * 40) print("Test failed") - print(err) traceback.print_exc() testdir = Path(testdir) results_fname = testdir / ".aider.results.json" - results_fname.write_text(json.dumps(dict(exception=str(err)))) + results_fname.write_text(json.dumps(dict(exception=traceback.format_exc()))) def run_test_real( @@ -663,6 +684,8 @@ def run_test_real( editor_edit_format, num_ctx=None, sleep=0, + reasoning_effort: Optional[str] = None, + thinking_tokens: Optional[int] = None, read_model_settings=None, ): if not os.path.isdir(testdir): @@ -767,8 +790,15 @@ def run_test_real( weak_model=weak_model_name, editor_model=editor_model, editor_edit_format=editor_edit_format, + verbose=verbose, ) + if reasoning_effort is not None: + main_model.set_reasoning_effort(reasoning_effort) + + if thinking_tokens is not None: + main_model.set_thinking_tokens(thinking_tokens) + dump(main_model.max_chat_history_tokens) if num_ctx: @@ -919,6 +949,8 @@ def run_test_real( syntax_errors=syntax_errors, indentation_errors=indentation_errors, lazy_comments=lazy_comments, # Add the count of pattern matches to the results + reasoning_effort=reasoning_effort, + thinking_tokens=thinking_tokens, chat_hashes=list( zip( coder.chat_completion_call_hashes, diff --git a/benchmark/docker.sh b/benchmark/docker.sh index 3edde7c66..6f97b865e 100755 --- a/benchmark/docker.sh +++ b/benchmark/docker.sh @@ -2,8 +2,8 @@ docker run \ -it --rm \ - --memory=25g \ - --memory-swap=25g \ + --memory=12g \ + --memory-swap=12g \ --add-host=host.docker.internal:host-gateway \ -v `pwd`:/aider \ -v `pwd`/tmp.benchmarks/.:/benchmarks \ diff --git a/docker/Dockerfile b/docker/Dockerfile index 72ea5ce19..05932a58d 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -64,7 +64,7 @@ COPY . /tmp/aider # Install dependencies as root RUN /venv/bin/python -m pip install --upgrade --no-cache-dir pip && \ - /venv/bin/python -m pip install --no-cache-dir /tmp/aider[playwright] boto3 \ + /venv/bin/python -m pip install --no-cache-dir /tmp/aider[playwright] boto3 google-cloud-aiplatform \ --extra-index-url https://download.pytorch.org/whl/cpu && \ rm -rf /tmp/aider diff --git a/requirements.txt b/requirements.txt index f4b51e164..44dbe2956 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,243 +1,395 @@ -# -# This file is autogenerated by pip-compile with Python 3.12 -# by the following command: -# -# pip-compile --allow-unsafe --output-file=requirements.txt requirements/requirements.in -# -aiohappyeyeballs==2.4.6 - # via aiohttp -aiohttp==3.11.13 - # via litellm -aiosignal==1.3.2 - # via aiohttp -annotated-types==0.7.0 - # via pydantic -anyio==4.8.0 +# This file was autogenerated by uv via the following command: +# uv pip compile --no-strip-extras --constraint=requirements/common-constraints.txt --output-file=tmp.requirements.txt requirements/requirements.in +aiohappyeyeballs==2.6.1 # via + # -c requirements/common-constraints.txt + # aiohttp +aiohttp==3.11.16 + # via + # -c requirements/common-constraints.txt + # litellm +aiosignal==1.3.2 + # via + # -c requirements/common-constraints.txt + # aiohttp +annotated-types==0.7.0 + # via + # -c requirements/common-constraints.txt + # pydantic +anyio==4.9.0 + # via + # -c requirements/common-constraints.txt # httpx # openai # watchfiles -attrs==25.1.0 +attrs==25.3.0 # via + # -c requirements/common-constraints.txt # aiohttp # jsonschema # referencing backoff==2.2.1 # via + # -c requirements/common-constraints.txt # -r requirements/requirements.in # posthog beautifulsoup4==4.13.3 - # via -r requirements/requirements.in + # via + # -c requirements/common-constraints.txt + # -r requirements/requirements.in certifi==2025.1.31 # via + # -c requirements/common-constraints.txt # httpcore # httpx # requests cffi==1.17.1 # via + # -c requirements/common-constraints.txt # sounddevice # soundfile charset-normalizer==3.4.1 - # via requests + # via + # -c requirements/common-constraints.txt + # requests click==8.1.8 - # via litellm + # via + # -c requirements/common-constraints.txt + # litellm configargparse==1.7 - # via -r requirements/requirements.in + # via + # -c requirements/common-constraints.txt + # -r requirements/requirements.in diff-match-patch==20241021 - # via -r requirements/requirements.in + # via + # -c requirements/common-constraints.txt + # -r requirements/requirements.in diskcache==5.6.3 - # via -r requirements/requirements.in + # via + # -c requirements/common-constraints.txt + # -r requirements/requirements.in distro==1.9.0 # via + # -c requirements/common-constraints.txt # openai # posthog -filelock==3.17.0 - # via huggingface-hub -flake8==7.1.2 - # via -r requirements/requirements.in +filelock==3.18.0 + # via + # -c requirements/common-constraints.txt + # huggingface-hub +flake8==7.2.0 + # via + # -c requirements/common-constraints.txt + # -r requirements/requirements.in frozenlist==1.5.0 # via + # -c requirements/common-constraints.txt # aiohttp # aiosignal -fsspec==2025.2.0 - # via huggingface-hub +fsspec==2025.3.2 + # via + # -c requirements/common-constraints.txt + # huggingface-hub gitdb==4.0.12 - # via gitpython + # via + # -c requirements/common-constraints.txt + # gitpython gitpython==3.1.44 - # via -r requirements/requirements.in -grep-ast==0.6.1 - # via -r requirements/requirements.in + # via + # -c requirements/common-constraints.txt + # -r requirements/requirements.in +grep-ast==0.8.1 + # via + # -c requirements/common-constraints.txt + # -r requirements/requirements.in h11==0.14.0 - # via httpcore -httpcore==1.0.7 - # via httpx + # via + # -c requirements/common-constraints.txt + # httpcore +httpcore==1.0.8 + # via + # -c requirements/common-constraints.txt + # httpx httpx==0.28.1 # via + # -c requirements/common-constraints.txt # litellm # openai -huggingface-hub==0.29.1 - # via tokenizers +huggingface-hub==0.30.2 + # via + # -c requirements/common-constraints.txt + # tokenizers idna==3.10 # via + # -c requirements/common-constraints.txt # anyio # httpx # requests # yarl importlib-metadata==7.2.1 # via + # -c requirements/common-constraints.txt # -r requirements/requirements.in # litellm importlib-resources==6.5.2 - # via -r requirements/requirements.in -jinja2==3.1.5 - # via litellm -jiter==0.8.2 - # via openai -json5==0.10.0 - # via -r requirements/requirements.in + # via + # -c requirements/common-constraints.txt + # -r requirements/requirements.in +jinja2==3.1.6 + # via + # -c requirements/common-constraints.txt + # litellm +jiter==0.9.0 + # via + # -c requirements/common-constraints.txt + # openai +json5==0.12.0 + # via + # -c requirements/common-constraints.txt + # -r requirements/requirements.in jsonschema==4.23.0 # via + # -c requirements/common-constraints.txt # -r requirements/requirements.in # litellm jsonschema-specifications==2024.10.1 - # via jsonschema -litellm==1.61.16 - # via -r requirements/requirements.in -markdown-it-py==3.0.0 - # via rich -markupsafe==3.0.2 - # via jinja2 -mccabe==0.7.0 - # via flake8 -mdurl==0.1.2 - # via markdown-it-py -mixpanel==4.10.1 - # via -r requirements/requirements.in -monotonic==1.6 - # via posthog -multidict==6.1.0 # via + # -c requirements/common-constraints.txt + # jsonschema +litellm==1.65.7 + # via + # -c requirements/common-constraints.txt + # -r requirements/requirements.in +markdown-it-py==3.0.0 + # via + # -c requirements/common-constraints.txt + # rich +markupsafe==3.0.2 + # via + # -c requirements/common-constraints.txt + # jinja2 +mccabe==0.7.0 + # via + # -c requirements/common-constraints.txt + # flake8 +mdurl==0.1.2 + # via + # -c requirements/common-constraints.txt + # markdown-it-py +mixpanel==4.10.1 + # via + # -c requirements/common-constraints.txt + # -r requirements/requirements.in +monotonic==1.6 + # via + # -c requirements/common-constraints.txt + # posthog +multidict==6.4.3 + # via + # -c requirements/common-constraints.txt # aiohttp # yarl networkx==3.2.1 - # via -r requirements/requirements.in + # via + # -c requirements/common-constraints.txt + # -r requirements/requirements.in numpy==1.26.4 # via - # -r requirements/requirements.in + # -c requirements/common-constraints.txt # scipy # soundfile -openai==1.64.0 - # via litellm +openai==1.73.0 + # via + # -c requirements/common-constraints.txt + # litellm packaging==24.2 # via + # -c requirements/common-constraints.txt # -r requirements/requirements.in # huggingface-hub pathspec==0.12.1 # via + # -c requirements/common-constraints.txt # -r requirements/requirements.in # grep-ast pexpect==4.9.0 - # via -r requirements/requirements.in -pillow==10.4.0 - # via -r requirements/requirements.in -posthog==3.16.0 - # via -r requirements/requirements.in -prompt-toolkit==3.0.50 - # via -r requirements/requirements.in -propcache==0.3.0 # via + # -c requirements/common-constraints.txt + # -r requirements/requirements.in +pillow==11.1.0 + # via + # -c requirements/common-constraints.txt + # -r requirements/requirements.in +pip==25.0.1 + # via + # -c requirements/common-constraints.txt + # -r requirements/requirements.in +posthog==3.24.1 + # via + # -c requirements/common-constraints.txt + # -r requirements/requirements.in +prompt-toolkit==3.0.50 + # via + # -c requirements/common-constraints.txt + # -r requirements/requirements.in +propcache==0.3.1 + # via + # -c requirements/common-constraints.txt # aiohttp # yarl psutil==7.0.0 - # via -r requirements/requirements.in -ptyprocess==0.7.0 - # via pexpect -pycodestyle==2.12.1 - # via flake8 -pycparser==2.22 - # via cffi -pydantic==2.10.6 # via + # -c requirements/common-constraints.txt + # -r requirements/requirements.in +ptyprocess==0.7.0 + # via + # -c requirements/common-constraints.txt + # pexpect +pycodestyle==2.13.0 + # via + # -c requirements/common-constraints.txt + # flake8 +pycparser==2.22 + # via + # -c requirements/common-constraints.txt + # cffi +pydantic==2.11.3 + # via + # -c requirements/common-constraints.txt # litellm # openai -pydantic-core==2.27.2 - # via pydantic +pydantic-core==2.33.1 + # via + # -c requirements/common-constraints.txt + # pydantic pydub==0.25.1 - # via -r requirements/requirements.in -pyflakes==3.2.0 - # via flake8 + # via + # -c requirements/common-constraints.txt + # -r requirements/requirements.in +pyflakes==3.3.2 + # via + # -c requirements/common-constraints.txt + # flake8 pygments==2.19.1 - # via rich + # via + # -c requirements/common-constraints.txt + # rich pypandoc==1.15 - # via -r requirements/requirements.in + # via + # -c requirements/common-constraints.txt + # -r requirements/requirements.in pyperclip==1.9.0 - # via -r requirements/requirements.in + # via + # -c requirements/common-constraints.txt + # -r requirements/requirements.in python-dateutil==2.9.0.post0 - # via posthog -python-dotenv==1.0.1 - # via litellm + # via + # -c requirements/common-constraints.txt + # posthog +python-dotenv==1.1.0 + # via + # -c requirements/common-constraints.txt + # litellm pyyaml==6.0.2 # via + # -c requirements/common-constraints.txt # -r requirements/requirements.in # huggingface-hub referencing==0.36.2 # via + # -c requirements/common-constraints.txt # jsonschema # jsonschema-specifications regex==2024.11.6 - # via tiktoken + # via + # -c requirements/common-constraints.txt + # tiktoken requests==2.32.3 # via + # -c requirements/common-constraints.txt # huggingface-hub # mixpanel # posthog # tiktoken -rich==13.9.4 - # via -r requirements/requirements.in -rpds-py==0.23.1 +rich==14.0.0 # via + # -c requirements/common-constraints.txt + # -r requirements/requirements.in +rpds-py==0.24.0 + # via + # -c requirements/common-constraints.txt # jsonschema # referencing scipy==1.13.1 - # via -r requirements/requirements.in + # via + # -c requirements/common-constraints.txt + # -r requirements/requirements.in six==1.17.0 # via + # -c requirements/common-constraints.txt # mixpanel # posthog # python-dateutil smmap==5.0.2 - # via gitdb + # via + # -c requirements/common-constraints.txt + # gitdb sniffio==1.3.1 # via + # -c requirements/common-constraints.txt # anyio # openai socksio==1.0.0 - # via -r requirements/requirements.in -sounddevice==0.5.1 - # via -r requirements/requirements.in -soundfile==0.13.1 - # via -r requirements/requirements.in -soupsieve==2.6 - # via beautifulsoup4 -tiktoken==0.9.0 - # via litellm -tokenizers==0.19.1 # via + # -c requirements/common-constraints.txt # -r requirements/requirements.in +sounddevice==0.5.1 + # via + # -c requirements/common-constraints.txt + # -r requirements/requirements.in +soundfile==0.13.1 + # via + # -c requirements/common-constraints.txt + # -r requirements/requirements.in +soupsieve==2.6 + # via + # -c requirements/common-constraints.txt + # beautifulsoup4 +tiktoken==0.9.0 + # via + # -c requirements/common-constraints.txt + # litellm +tokenizers==0.21.1 + # via + # -c requirements/common-constraints.txt # litellm tqdm==4.67.1 # via + # -c requirements/common-constraints.txt # huggingface-hub # openai -tree-sitter==0.21.3 # via - # -r requirements/requirements.in + # -c requirements/common-constraints.txt + # tree-sitter-language-pack +tree-sitter-c-sharp==0.23.1 + # via + # -c requirements/common-constraints.txt + # tree-sitter-language-pack +tree-sitter-embedded-template==0.23.2 + # via + # -c requirements/common-constraints.txt + # tree-sitter-language-pack +tree-sitter-language-pack==0.7.1 + # via + # -c requirements/common-constraints.txt # grep-ast - # tree-sitter-languages -tree-sitter-languages==1.10.2 - # via grep-ast -typing-extensions==4.12.2 +tree-sitter-yaml==0.7.0 # via + # -c requirements/common-constraints.txt + # tree-sitter-language-pack +typing-extensions==4.13.2 + # via + # -c requirements/common-constraints.txt # anyio # beautifulsoup4 # huggingface-hub @@ -245,19 +397,32 @@ typing-extensions==4.12.2 # pydantic # pydantic-core # referencing -urllib3==2.3.0 + # typing-inspection +typing-inspection==0.4.0 # via + # -c requirements/common-constraints.txt + # pydantic +urllib3==2.4.0 + # via + # -c requirements/common-constraints.txt # mixpanel # requests -watchfiles==1.0.4 - # via -r requirements/requirements.in +watchfiles==1.0.5 + # via + # -c requirements/common-constraints.txt + # -r requirements/requirements.in wcwidth==0.2.13 - # via prompt-toolkit -yarl==1.18.3 - # via aiohttp + # via + # -c requirements/common-constraints.txt + # prompt-toolkit +yarl==1.19.0 + # via + # -c requirements/common-constraints.txt + # aiohttp zipp==3.21.0 - # via importlib-metadata - -# The following packages are considered to be unsafe in a requirements file: -pip==25.0.1 - # via -r requirements/requirements.in + # via + # -c requirements/common-constraints.txt + # importlib-metadata + +tree-sitter==0.23.2; python_version < "3.10" +tree-sitter==0.24.0; python_version >= "3.10" diff --git a/requirements/common-constraints.txt b/requirements/common-constraints.txt new file mode 100644 index 000000000..7061d872f --- /dev/null +++ b/requirements/common-constraints.txt @@ -0,0 +1,578 @@ +# This file was autogenerated by uv via the following command: +# uv pip compile --no-strip-extras --output-file=requirements/common-constraints.txt requirements/requirements.in requirements/requirements-browser.in requirements/requirements-dev.in requirements/requirements-help.in requirements/requirements-playwright.in +aiohappyeyeballs==2.6.1 + # via aiohttp +aiohttp==3.11.16 + # via + # huggingface-hub + # litellm + # llama-index-core +aiosignal==1.3.2 + # via aiohttp +altair==5.5.0 + # via streamlit +annotated-types==0.7.0 + # via pydantic +anyio==4.9.0 + # via + # httpx + # openai + # watchfiles +attrs==25.3.0 + # via + # aiohttp + # jsonschema + # referencing +backoff==2.2.1 + # via + # -r requirements/requirements.in + # posthog +banks==2.1.1 + # via llama-index-core +beautifulsoup4==4.13.3 + # via -r requirements/requirements.in +blinker==1.9.0 + # via streamlit +build==1.2.2.post1 + # via pip-tools +cachetools==5.5.2 + # via + # google-auth + # streamlit +certifi==2025.1.31 + # via + # httpcore + # httpx + # requests +cffi==1.17.1 + # via + # sounddevice + # soundfile +cfgv==3.4.0 + # via pre-commit +charset-normalizer==3.4.1 + # via requests +click==8.1.8 + # via + # litellm + # nltk + # pip-tools + # streamlit + # typer +codespell==2.4.1 + # via -r requirements/requirements-dev.in +cogapp==3.4.1 + # via -r requirements/requirements-dev.in +colorama==0.4.6 + # via griffe +configargparse==1.7 + # via -r requirements/requirements.in +contourpy==1.3.1 + # via matplotlib +cycler==0.12.1 + # via matplotlib +dataclasses-json==0.6.7 + # via llama-index-core +deprecated==1.2.18 + # via + # banks + # llama-index-core +diff-match-patch==20241021 + # via -r requirements/requirements.in +dill==0.3.9 + # via + # multiprocess + # pathos +dirtyjson==1.0.8 + # via llama-index-core +diskcache==5.6.3 + # via -r requirements/requirements.in +distlib==0.3.9 + # via virtualenv +distro==1.9.0 + # via + # openai + # posthog +filelock==3.18.0 + # via + # huggingface-hub + # torch + # transformers + # virtualenv +filetype==1.2.0 + # via llama-index-core +flake8==7.2.0 + # via -r requirements/requirements.in +fonttools==4.57.0 + # via matplotlib +frozenlist==1.5.0 + # via + # aiohttp + # aiosignal +fsspec==2025.3.2 + # via + # huggingface-hub + # llama-index-core + # torch +gitdb==4.0.12 + # via gitpython +gitpython==3.1.44 + # via + # -r requirements/requirements.in + # streamlit +google-api-core[grpc]==2.24.2 + # via + # google-cloud-bigquery + # google-cloud-core +google-auth==2.38.0 + # via + # google-api-core + # google-cloud-bigquery + # google-cloud-core +google-cloud-bigquery==3.31.0 + # via -r requirements/requirements-dev.in +google-cloud-core==2.4.3 + # via google-cloud-bigquery +google-crc32c==1.7.1 + # via google-resumable-media +google-resumable-media==2.7.2 + # via google-cloud-bigquery +googleapis-common-protos==1.69.2 + # via + # google-api-core + # grpcio-status +greenlet==3.1.1 + # via + # playwright + # sqlalchemy +grep-ast==0.8.1 + # via -r requirements/requirements.in +griffe==1.7.2 + # via banks +grpcio==1.71.0 + # via + # google-api-core + # grpcio-status +grpcio-status==1.71.0 + # via google-api-core +h11==0.14.0 + # via httpcore +httpcore==1.0.8 + # via httpx +httpx==0.28.1 + # via + # litellm + # llama-index-core + # openai +huggingface-hub[inference]==0.30.2 + # via + # llama-index-embeddings-huggingface + # sentence-transformers + # tokenizers + # transformers +identify==2.6.9 + # via pre-commit +idna==3.10 + # via + # anyio + # httpx + # requests + # yarl +imgcat==0.6.0 + # via -r requirements/requirements-dev.in +importlib-metadata==7.2.1 + # via + # -r requirements/requirements.in + # litellm +importlib-resources==6.5.2 + # via -r requirements/requirements.in +iniconfig==2.1.0 + # via pytest +jinja2==3.1.6 + # via + # altair + # banks + # litellm + # pydeck + # torch +jiter==0.9.0 + # via openai +joblib==1.4.2 + # via + # nltk + # scikit-learn +json5==0.12.0 + # via -r requirements/requirements.in +jsonschema==4.23.0 + # via + # -r requirements/requirements.in + # altair + # litellm +jsonschema-specifications==2024.10.1 + # via jsonschema +kiwisolver==1.4.8 + # via matplotlib +litellm==1.65.7 + # via -r requirements/requirements.in +llama-index-core==0.12.26 + # via + # -r requirements/requirements-help.in + # llama-index-embeddings-huggingface +llama-index-embeddings-huggingface==0.5.3 + # via -r requirements/requirements-help.in +lox==0.13.0 + # via -r requirements/requirements-dev.in +markdown-it-py==3.0.0 + # via rich +markupsafe==3.0.2 + # via jinja2 +marshmallow==3.26.1 + # via dataclasses-json +matplotlib==3.10.1 + # via -r requirements/requirements-dev.in +mccabe==0.7.0 + # via flake8 +mdurl==0.1.2 + # via markdown-it-py +mixpanel==4.10.1 + # via -r requirements/requirements.in +monotonic==1.6 + # via posthog +mpmath==1.3.0 + # via sympy +multidict==6.4.3 + # via + # aiohttp + # yarl +multiprocess==0.70.17 + # via pathos +mypy-extensions==1.0.0 + # via typing-inspect +narwhals==1.34.1 + # via altair +nest-asyncio==1.6.0 + # via llama-index-core +networkx==3.2.1 + # via + # -r requirements/requirements.in + # llama-index-core + # torch +nltk==3.9.1 + # via llama-index-core +nodeenv==1.9.1 + # via pre-commit +numpy==1.26.4 + # via + # -r requirements/requirements-help.in + # contourpy + # llama-index-core + # matplotlib + # pandas + # pydeck + # scikit-learn + # scipy + # soundfile + # streamlit + # transformers +openai==1.73.0 + # via litellm +packaging==24.2 + # via + # -r requirements/requirements.in + # altair + # build + # google-cloud-bigquery + # huggingface-hub + # marshmallow + # matplotlib + # pytest + # streamlit + # transformers +pandas==2.2.3 + # via + # -r requirements/requirements-dev.in + # streamlit +pathos==0.3.3 + # via lox +pathspec==0.12.1 + # via + # -r requirements/requirements.in + # grep-ast +pexpect==4.9.0 + # via -r requirements/requirements.in +pillow==11.1.0 + # via + # -r requirements/requirements.in + # llama-index-core + # matplotlib + # sentence-transformers + # streamlit +pip==25.0.1 + # via + # -r requirements/requirements.in + # pip-tools +pip-tools==7.4.1 + # via -r requirements/requirements-dev.in +platformdirs==4.3.7 + # via + # banks + # virtualenv +playwright==1.51.0 + # via -r requirements/requirements-playwright.in +pluggy==1.5.0 + # via pytest +posthog==3.24.1 + # via -r requirements/requirements.in +pox==0.3.5 + # via pathos +ppft==1.7.6.9 + # via pathos +pre-commit==4.2.0 + # via -r requirements/requirements-dev.in +prompt-toolkit==3.0.50 + # via -r requirements/requirements.in +propcache==0.3.1 + # via + # aiohttp + # yarl +proto-plus==1.26.1 + # via google-api-core +protobuf==5.29.4 + # via + # google-api-core + # googleapis-common-protos + # grpcio-status + # proto-plus + # streamlit +psutil==7.0.0 + # via -r requirements/requirements.in +ptyprocess==0.7.0 + # via pexpect +pyarrow==19.0.1 + # via streamlit +pyasn1==0.6.1 + # via + # pyasn1-modules + # rsa +pyasn1-modules==0.4.2 + # via google-auth +pycodestyle==2.13.0 + # via flake8 +pycparser==2.22 + # via cffi +pydantic==2.11.3 + # via + # banks + # litellm + # llama-index-core + # openai +pydantic-core==2.33.1 + # via pydantic +pydeck==0.9.1 + # via streamlit +pydub==0.25.1 + # via -r requirements/requirements.in +pyee==12.1.1 + # via playwright +pyflakes==3.3.2 + # via flake8 +pygments==2.19.1 + # via rich +pypandoc==1.15 + # via -r requirements/requirements.in +pyparsing==3.2.3 + # via matplotlib +pyperclip==1.9.0 + # via -r requirements/requirements.in +pyproject-hooks==1.2.0 + # via + # build + # pip-tools +pytest==8.3.5 + # via + # -r requirements/requirements-dev.in + # pytest-env +pytest-env==1.1.5 + # via -r requirements/requirements-dev.in +python-dateutil==2.9.0.post0 + # via + # google-cloud-bigquery + # matplotlib + # pandas + # posthog +python-dotenv==1.1.0 + # via litellm +pytz==2025.2 + # via pandas +pyyaml==6.0.2 + # via + # -r requirements/requirements.in + # huggingface-hub + # llama-index-core + # pre-commit + # transformers +referencing==0.36.2 + # via + # jsonschema + # jsonschema-specifications +regex==2024.11.6 + # via + # nltk + # tiktoken + # transformers +requests==2.32.3 + # via + # google-api-core + # google-cloud-bigquery + # huggingface-hub + # llama-index-core + # mixpanel + # posthog + # streamlit + # tiktoken + # transformers +rich==14.0.0 + # via + # -r requirements/requirements.in + # typer +rpds-py==0.24.0 + # via + # jsonschema + # referencing +rsa==4.9 + # via google-auth +safetensors==0.5.3 + # via transformers +scikit-learn==1.6.1 + # via sentence-transformers +scipy==1.13.1 + # via + # -r requirements/requirements.in + # scikit-learn + # sentence-transformers +semver==3.0.4 + # via -r requirements/requirements-dev.in +sentence-transformers==4.0.2 + # via llama-index-embeddings-huggingface +setuptools==78.1.0 + # via pip-tools +shellingham==1.5.4 + # via typer +six==1.17.0 + # via + # mixpanel + # posthog + # python-dateutil +smmap==5.0.2 + # via gitdb +sniffio==1.3.1 + # via + # anyio + # openai +socksio==1.0.0 + # via -r requirements/requirements.in +sounddevice==0.5.1 + # via -r requirements/requirements.in +soundfile==0.13.1 + # via -r requirements/requirements.in +soupsieve==2.6 + # via beautifulsoup4 +sqlalchemy[asyncio]==2.0.40 + # via llama-index-core +streamlit==1.44.1 + # via -r requirements/requirements-browser.in +sympy==1.13.3 + # via torch +tenacity==9.1.2 + # via + # llama-index-core + # streamlit +threadpoolctl==3.6.0 + # via scikit-learn +tiktoken==0.9.0 + # via + # litellm + # llama-index-core +tokenizers==0.21.1 + # via + # litellm + # transformers +toml==0.10.2 + # via streamlit +torch==2.2.2 + # via + # -r requirements/requirements-help.in + # sentence-transformers +tornado==6.4.2 + # via streamlit +tqdm==4.67.1 + # via + # huggingface-hub + # llama-index-core + # nltk + # openai + # sentence-transformers + # transformers +transformers==4.51.2 + # via sentence-transformers +tree-sitter==0.24.0 + # via tree-sitter-language-pack +tree-sitter-c-sharp==0.23.1 + # via tree-sitter-language-pack +tree-sitter-embedded-template==0.23.2 + # via tree-sitter-language-pack +tree-sitter-language-pack==0.7.1 + # via grep-ast +tree-sitter-yaml==0.7.0 + # via tree-sitter-language-pack +typer==0.15.2 + # via -r requirements/requirements-dev.in +typing-extensions==4.13.2 + # via + # altair + # anyio + # beautifulsoup4 + # huggingface-hub + # llama-index-core + # openai + # pydantic + # pydantic-core + # pyee + # referencing + # sentence-transformers + # sqlalchemy + # streamlit + # torch + # typer + # typing-inspect + # typing-inspection +typing-inspect==0.9.0 + # via + # dataclasses-json + # llama-index-core +typing-inspection==0.4.0 + # via pydantic +tzdata==2025.2 + # via pandas +urllib3==2.4.0 + # via + # mixpanel + # requests +uv==0.6.14 + # via -r requirements/requirements-dev.in +virtualenv==20.30.0 + # via pre-commit +watchfiles==1.0.5 + # via -r requirements/requirements.in +wcwidth==0.2.13 + # via prompt-toolkit +wheel==0.45.1 + # via pip-tools +wrapt==1.17.2 + # via + # deprecated + # llama-index-core +yarl==1.19.0 + # via aiohttp +zipp==3.21.0 + # via importlib-metadata diff --git a/requirements/requirements-browser.in b/requirements/requirements-browser.in index de1cabe58..12a470652 100644 --- a/requirements/requirements-browser.in +++ b/requirements/requirements-browser.in @@ -1,4 +1 @@ --c ../requirements.txt - streamlit -watchdog<5 # docker build fails: streamlit 1.38.0 depends on watchdog<5 diff --git a/requirements/requirements-browser.txt b/requirements/requirements-browser.txt index 7a2223d8f..2174b975a 100644 --- a/requirements/requirements-browser.txt +++ b/requirements/requirements-browser.txt @@ -1,214 +1,155 @@ -# -# This file is autogenerated by pip-compile with Python 3.12 -# by the following command: -# -# pip-compile --allow-unsafe --constraint=requirements.txt --constraint=requirements/requirements-dev.txt --constraint=requirements/requirements-help.txt --output-file=requirements/requirements-browser.txt requirements/requirements-browser.in -# +# This file was autogenerated by uv via the following command: +# uv pip compile --no-strip-extras --constraint=requirements/common-constraints.txt --output-file=requirements/requirements-browser.txt requirements/requirements-browser.in altair==5.5.0 - # via streamlit -attrs==25.1.0 # via - # -c /Users/gauthier/Projects/aider/requirements.txt - # -c requirements.txt - # -c requirements/requirements-help.txt + # -c requirements/common-constraints.txt + # streamlit +attrs==25.3.0 + # via + # -c requirements/common-constraints.txt # jsonschema # referencing blinker==1.9.0 - # via streamlit + # via + # -c requirements/common-constraints.txt + # streamlit cachetools==5.5.2 - # via streamlit + # via + # -c requirements/common-constraints.txt + # streamlit certifi==2025.1.31 # via - # -c /Users/gauthier/Projects/aider/requirements.txt - # -c requirements.txt - # -c requirements/requirements-dev.txt - # -c requirements/requirements-help.txt + # -c requirements/common-constraints.txt # requests charset-normalizer==3.4.1 # via - # -c /Users/gauthier/Projects/aider/requirements.txt - # -c requirements.txt - # -c requirements/requirements-dev.txt - # -c requirements/requirements-help.txt + # -c requirements/common-constraints.txt # requests click==8.1.8 # via - # -c /Users/gauthier/Projects/aider/requirements.txt - # -c requirements.txt - # -c requirements/requirements-dev.txt - # -c requirements/requirements-help.txt + # -c requirements/common-constraints.txt # streamlit gitdb==4.0.12 # via - # -c /Users/gauthier/Projects/aider/requirements.txt - # -c requirements.txt + # -c requirements/common-constraints.txt # gitpython gitpython==3.1.44 # via - # -c /Users/gauthier/Projects/aider/requirements.txt - # -c requirements.txt + # -c requirements/common-constraints.txt # streamlit idna==3.10 # via - # -c /Users/gauthier/Projects/aider/requirements.txt - # -c requirements.txt - # -c requirements/requirements-dev.txt - # -c requirements/requirements-help.txt + # -c requirements/common-constraints.txt # requests -jinja2==3.1.5 +jinja2==3.1.6 # via - # -c /Users/gauthier/Projects/aider/requirements.txt - # -c requirements.txt - # -c requirements/requirements-dev.txt - # -c requirements/requirements-help.txt + # -c requirements/common-constraints.txt # altair # pydeck jsonschema==4.23.0 # via - # -c /Users/gauthier/Projects/aider/requirements.txt - # -c requirements.txt + # -c requirements/common-constraints.txt # altair jsonschema-specifications==2024.10.1 # via - # -c /Users/gauthier/Projects/aider/requirements.txt - # -c requirements.txt + # -c requirements/common-constraints.txt # jsonschema -markdown-it-py==3.0.0 - # via - # -c /Users/gauthier/Projects/aider/requirements.txt - # -c requirements.txt - # -c requirements/requirements-dev.txt - # rich markupsafe==3.0.2 # via - # -c /Users/gauthier/Projects/aider/requirements.txt - # -c requirements.txt - # -c requirements/requirements-dev.txt - # -c requirements/requirements-help.txt + # -c requirements/common-constraints.txt # jinja2 -mdurl==0.1.2 +narwhals==1.34.1 # via - # -c /Users/gauthier/Projects/aider/requirements.txt - # -c requirements.txt - # -c requirements/requirements-dev.txt - # markdown-it-py -narwhals==1.28.0 - # via altair + # -c requirements/common-constraints.txt + # altair numpy==1.26.4 # via - # -c /Users/gauthier/Projects/aider/requirements.txt - # -c requirements.txt - # -c requirements/requirements-dev.txt - # -c requirements/requirements-help.txt + # -c requirements/common-constraints.txt # pandas # pydeck # streamlit packaging==24.2 # via - # -c /Users/gauthier/Projects/aider/requirements.txt - # -c requirements.txt - # -c requirements/requirements-dev.txt - # -c requirements/requirements-help.txt + # -c requirements/common-constraints.txt # altair # streamlit pandas==2.2.3 # via - # -c requirements/requirements-dev.txt + # -c requirements/common-constraints.txt # streamlit -pillow==10.4.0 +pillow==11.1.0 # via - # -c /Users/gauthier/Projects/aider/requirements.txt - # -c requirements.txt - # -c requirements/requirements-dev.txt - # -c requirements/requirements-help.txt + # -c requirements/common-constraints.txt + # streamlit +protobuf==5.29.4 + # via + # -c requirements/common-constraints.txt # streamlit -protobuf==5.29.3 - # via streamlit pyarrow==19.0.1 - # via streamlit -pydeck==0.9.1 - # via streamlit -pygments==2.19.1 # via - # -c /Users/gauthier/Projects/aider/requirements.txt - # -c requirements.txt - # -c requirements/requirements-dev.txt - # rich + # -c requirements/common-constraints.txt + # streamlit +pydeck==0.9.1 + # via + # -c requirements/common-constraints.txt + # streamlit python-dateutil==2.9.0.post0 # via - # -c /Users/gauthier/Projects/aider/requirements.txt - # -c requirements.txt - # -c requirements/requirements-dev.txt + # -c requirements/common-constraints.txt # pandas -pytz==2025.1 +pytz==2025.2 # via - # -c requirements/requirements-dev.txt + # -c requirements/common-constraints.txt # pandas referencing==0.36.2 # via - # -c /Users/gauthier/Projects/aider/requirements.txt - # -c requirements.txt + # -c requirements/common-constraints.txt # jsonschema # jsonschema-specifications requests==2.32.3 # via - # -c /Users/gauthier/Projects/aider/requirements.txt - # -c requirements.txt - # -c requirements/requirements-dev.txt - # -c requirements/requirements-help.txt + # -c requirements/common-constraints.txt # streamlit -rich==13.9.4 +rpds-py==0.24.0 # via - # -c /Users/gauthier/Projects/aider/requirements.txt - # -c requirements.txt - # -c requirements/requirements-dev.txt - # streamlit -rpds-py==0.23.1 - # via - # -c /Users/gauthier/Projects/aider/requirements.txt - # -c requirements.txt + # -c requirements/common-constraints.txt # jsonschema # referencing six==1.17.0 # via - # -c /Users/gauthier/Projects/aider/requirements.txt - # -c requirements.txt - # -c requirements/requirements-dev.txt + # -c requirements/common-constraints.txt # python-dateutil smmap==5.0.2 # via - # -c /Users/gauthier/Projects/aider/requirements.txt - # -c requirements.txt + # -c requirements/common-constraints.txt # gitdb -streamlit==1.42.2 - # via -r requirements/requirements-browser.in -tenacity==9.0.0 +streamlit==1.44.1 # via - # -c requirements/requirements-help.txt + # -c requirements/common-constraints.txt + # -r requirements/requirements-browser.in +tenacity==9.1.2 + # via + # -c requirements/common-constraints.txt # streamlit toml==0.10.2 - # via streamlit -tornado==6.4.2 - # via streamlit -typing-extensions==4.12.2 # via - # -c /Users/gauthier/Projects/aider/requirements.txt - # -c requirements.txt - # -c requirements/requirements-dev.txt - # -c requirements/requirements-help.txt + # -c requirements/common-constraints.txt + # streamlit +tornado==6.4.2 + # via + # -c requirements/common-constraints.txt + # streamlit +typing-extensions==4.13.2 + # via + # -c requirements/common-constraints.txt # altair # referencing # streamlit -tzdata==2025.1 +tzdata==2025.2 # via - # -c requirements/requirements-dev.txt + # -c requirements/common-constraints.txt # pandas -urllib3==2.3.0 +urllib3==2.4.0 # via - # -c /Users/gauthier/Projects/aider/requirements.txt - # -c requirements.txt - # -c requirements/requirements-dev.txt - # -c requirements/requirements-help.txt + # -c requirements/common-constraints.txt # requests -watchdog==4.0.2 - # via -r requirements/requirements-browser.in diff --git a/requirements/requirements-dev.in b/requirements/requirements-dev.in index a9b6c2216..ce52b0af5 100644 --- a/requirements/requirements-dev.in +++ b/requirements/requirements-dev.in @@ -1,7 +1,3 @@ --c ../requirements.txt -# -# pip-compile --output-file=requirements-dev.txt requirements-dev.in --upgrade -# pytest pytest-env pip-tools @@ -14,3 +10,5 @@ pre-commit cogapp semver codespell +uv +google-cloud-bigquery diff --git a/requirements/requirements-dev.txt b/requirements/requirements-dev.txt index f770aedfa..1ee5e482d 100644 --- a/requirements/requirements-dev.txt +++ b/requirements/requirements-dev.txt @@ -1,235 +1,311 @@ -# -# This file is autogenerated by pip-compile with Python 3.12 -# by the following command: -# -# pip-compile --allow-unsafe --constraint=requirements.txt --output-file=requirements/requirements-dev.txt requirements/requirements-dev.in -# -alabaster==1.0.0 - # via sphinx -babel==2.17.0 - # via sphinx +# This file was autogenerated by uv via the following command: +# uv pip compile --no-strip-extras --constraint=requirements/common-constraints.txt --output-file=requirements/requirements-dev.txt requirements/requirements-dev.in build==1.2.2.post1 - # via pip-tools + # via + # -c requirements/common-constraints.txt + # pip-tools +cachetools==5.5.2 + # via + # -c requirements/common-constraints.txt + # google-auth certifi==2025.1.31 # via - # -c /Users/gauthier/Projects/aider/requirements.txt - # -c requirements.txt + # -c requirements/common-constraints.txt # requests cfgv==3.4.0 - # via pre-commit + # via + # -c requirements/common-constraints.txt + # pre-commit charset-normalizer==3.4.1 # via - # -c /Users/gauthier/Projects/aider/requirements.txt - # -c requirements.txt + # -c requirements/common-constraints.txt # requests click==8.1.8 # via - # -c /Users/gauthier/Projects/aider/requirements.txt - # -c requirements.txt + # -c requirements/common-constraints.txt # pip-tools # typer codespell==2.4.1 - # via -r requirements/requirements-dev.in + # via + # -c requirements/common-constraints.txt + # -r requirements/requirements-dev.in cogapp==3.4.1 - # via -r requirements/requirements-dev.in + # via + # -c requirements/common-constraints.txt + # -r requirements/requirements-dev.in contourpy==1.3.1 - # via matplotlib + # via + # -c requirements/common-constraints.txt + # matplotlib cycler==0.12.1 - # via matplotlib + # via + # -c requirements/common-constraints.txt + # matplotlib dill==0.3.9 # via + # -c requirements/common-constraints.txt # multiprocess # pathos distlib==0.3.9 - # via virtualenv -docutils==0.21.2 # via - # sphinx - # sphinx-rtd-theme -filelock==3.17.0 - # via - # -c /Users/gauthier/Projects/aider/requirements.txt - # -c requirements.txt + # -c requirements/common-constraints.txt # virtualenv -fonttools==4.56.0 - # via matplotlib -identify==2.6.8 - # via pre-commit +filelock==3.18.0 + # via + # -c requirements/common-constraints.txt + # virtualenv +fonttools==4.57.0 + # via + # -c requirements/common-constraints.txt + # matplotlib +google-api-core[grpc]==2.24.2 + # via + # -c requirements/common-constraints.txt + # google-cloud-bigquery + # google-cloud-core +google-auth==2.38.0 + # via + # -c requirements/common-constraints.txt + # google-api-core + # google-cloud-bigquery + # google-cloud-core +google-cloud-bigquery==3.31.0 + # via + # -c requirements/common-constraints.txt + # -r requirements/requirements-dev.in +google-cloud-core==2.4.3 + # via + # -c requirements/common-constraints.txt + # google-cloud-bigquery +google-crc32c==1.7.1 + # via + # -c requirements/common-constraints.txt + # google-resumable-media +google-resumable-media==2.7.2 + # via + # -c requirements/common-constraints.txt + # google-cloud-bigquery +googleapis-common-protos==1.69.2 + # via + # -c requirements/common-constraints.txt + # google-api-core + # grpcio-status +grpcio==1.71.0 + # via + # -c requirements/common-constraints.txt + # google-api-core + # grpcio-status +grpcio-status==1.71.0 + # via + # -c requirements/common-constraints.txt + # google-api-core +identify==2.6.9 + # via + # -c requirements/common-constraints.txt + # pre-commit idna==3.10 # via - # -c /Users/gauthier/Projects/aider/requirements.txt - # -c requirements.txt + # -c requirements/common-constraints.txt # requests -imagesize==1.4.1 - # via sphinx imgcat==0.6.0 - # via -r requirements/requirements-dev.in -iniconfig==2.0.0 - # via pytest -jinja2==3.1.5 # via - # -c /Users/gauthier/Projects/aider/requirements.txt - # -c requirements.txt - # sphinx + # -c requirements/common-constraints.txt + # -r requirements/requirements-dev.in +iniconfig==2.1.0 + # via + # -c requirements/common-constraints.txt + # pytest kiwisolver==1.4.8 - # via matplotlib -lox==0.12.0 - # via -r requirements/requirements-dev.in + # via + # -c requirements/common-constraints.txt + # matplotlib +lox==0.13.0 + # via + # -c requirements/common-constraints.txt + # -r requirements/requirements-dev.in markdown-it-py==3.0.0 # via - # -c /Users/gauthier/Projects/aider/requirements.txt - # -c requirements.txt + # -c requirements/common-constraints.txt # rich -markupsafe==3.0.2 +matplotlib==3.10.1 # via - # -c /Users/gauthier/Projects/aider/requirements.txt - # -c requirements.txt - # jinja2 -matplotlib==3.10.0 - # via -r requirements/requirements-dev.in + # -c requirements/common-constraints.txt + # -r requirements/requirements-dev.in mdurl==0.1.2 # via - # -c /Users/gauthier/Projects/aider/requirements.txt - # -c requirements.txt + # -c requirements/common-constraints.txt # markdown-it-py multiprocess==0.70.17 - # via pathos + # via + # -c requirements/common-constraints.txt + # pathos nodeenv==1.9.1 - # via pre-commit + # via + # -c requirements/common-constraints.txt + # pre-commit numpy==1.26.4 # via - # -c /Users/gauthier/Projects/aider/requirements.txt - # -c requirements.txt + # -c requirements/common-constraints.txt # contourpy # matplotlib # pandas packaging==24.2 # via - # -c /Users/gauthier/Projects/aider/requirements.txt - # -c requirements.txt + # -c requirements/common-constraints.txt # build + # google-cloud-bigquery # matplotlib # pytest - # sphinx pandas==2.2.3 - # via -r requirements/requirements-dev.in -pathos==0.3.3 - # via lox -pillow==10.4.0 # via - # -c /Users/gauthier/Projects/aider/requirements.txt - # -c requirements.txt + # -c requirements/common-constraints.txt + # -r requirements/requirements-dev.in +pathos==0.3.3 + # via + # -c requirements/common-constraints.txt + # lox +pillow==11.1.0 + # via + # -c requirements/common-constraints.txt # matplotlib +pip==25.0.1 + # via + # -c requirements/common-constraints.txt + # pip-tools pip-tools==7.4.1 - # via -r requirements/requirements-dev.in -platformdirs==4.3.6 - # via virtualenv + # via + # -c requirements/common-constraints.txt + # -r requirements/requirements-dev.in +platformdirs==4.3.7 + # via + # -c requirements/common-constraints.txt + # virtualenv pluggy==1.5.0 - # via pytest + # via + # -c requirements/common-constraints.txt + # pytest pox==0.3.5 - # via pathos + # via + # -c requirements/common-constraints.txt + # pathos ppft==1.7.6.9 - # via pathos -pre-commit==4.1.0 - # via -r requirements/requirements-dev.in + # via + # -c requirements/common-constraints.txt + # pathos +pre-commit==4.2.0 + # via + # -c requirements/common-constraints.txt + # -r requirements/requirements-dev.in +proto-plus==1.26.1 + # via + # -c requirements/common-constraints.txt + # google-api-core +protobuf==5.29.4 + # via + # -c requirements/common-constraints.txt + # google-api-core + # googleapis-common-protos + # grpcio-status + # proto-plus +pyasn1==0.6.1 + # via + # -c requirements/common-constraints.txt + # pyasn1-modules + # rsa +pyasn1-modules==0.4.2 + # via + # -c requirements/common-constraints.txt + # google-auth pygments==2.19.1 # via - # -c /Users/gauthier/Projects/aider/requirements.txt - # -c requirements.txt + # -c requirements/common-constraints.txt # rich - # sphinx -pyparsing==3.2.1 - # via matplotlib +pyparsing==3.2.3 + # via + # -c requirements/common-constraints.txt + # matplotlib pyproject-hooks==1.2.0 # via + # -c requirements/common-constraints.txt # build # pip-tools -pytest==8.3.4 +pytest==8.3.5 # via + # -c requirements/common-constraints.txt # -r requirements/requirements-dev.in # pytest-env pytest-env==1.1.5 - # via -r requirements/requirements-dev.in + # via + # -c requirements/common-constraints.txt + # -r requirements/requirements-dev.in python-dateutil==2.9.0.post0 # via - # -c /Users/gauthier/Projects/aider/requirements.txt - # -c requirements.txt + # -c requirements/common-constraints.txt + # google-cloud-bigquery # matplotlib # pandas -pytz==2025.1 - # via pandas +pytz==2025.2 + # via + # -c requirements/common-constraints.txt + # pandas pyyaml==6.0.2 # via - # -c /Users/gauthier/Projects/aider/requirements.txt - # -c requirements.txt + # -c requirements/common-constraints.txt # pre-commit requests==2.32.3 # via - # -c /Users/gauthier/Projects/aider/requirements.txt - # -c requirements.txt - # sphinx -rich==13.9.4 + # -c requirements/common-constraints.txt + # google-api-core + # google-cloud-bigquery +rich==14.0.0 # via - # -c /Users/gauthier/Projects/aider/requirements.txt - # -c requirements.txt + # -c requirements/common-constraints.txt # typer -roman-numerals-py==3.1.0 - # via sphinx +rsa==4.9 + # via + # -c requirements/common-constraints.txt + # google-auth semver==3.0.4 - # via -r requirements/requirements-dev.in + # via + # -c requirements/common-constraints.txt + # -r requirements/requirements-dev.in +setuptools==78.1.0 + # via + # -c requirements/common-constraints.txt + # pip-tools shellingham==1.5.4 - # via typer + # via + # -c requirements/common-constraints.txt + # typer six==1.17.0 # via - # -c /Users/gauthier/Projects/aider/requirements.txt - # -c requirements.txt + # -c requirements/common-constraints.txt # python-dateutil -snowballstemmer==2.2.0 - # via sphinx -sphinx==8.2.1 +typer==0.15.2 # via - # sphinx-rtd-theme - # sphinxcontrib-jquery -sphinx-rtd-theme==3.0.2 - # via lox -sphinxcontrib-applehelp==2.0.0 - # via sphinx -sphinxcontrib-devhelp==2.0.0 - # via sphinx -sphinxcontrib-htmlhelp==2.1.0 - # via sphinx -sphinxcontrib-jquery==4.1 - # via sphinx-rtd-theme -sphinxcontrib-jsmath==1.0.1 - # via sphinx -sphinxcontrib-qthelp==2.0.0 - # via sphinx -sphinxcontrib-serializinghtml==2.0.0 - # via sphinx -typer==0.15.1 - # via -r requirements/requirements-dev.in -typing-extensions==4.12.2 + # -c requirements/common-constraints.txt + # -r requirements/requirements-dev.in +typing-extensions==4.13.2 # via - # -c /Users/gauthier/Projects/aider/requirements.txt - # -c requirements.txt + # -c requirements/common-constraints.txt # typer -tzdata==2025.1 - # via pandas -urllib3==2.3.0 +tzdata==2025.2 # via - # -c /Users/gauthier/Projects/aider/requirements.txt - # -c requirements.txt + # -c requirements/common-constraints.txt + # pandas +urllib3==2.4.0 + # via + # -c requirements/common-constraints.txt # requests -virtualenv==20.29.2 - # via pre-commit -wheel==0.45.1 - # via pip-tools - -# The following packages are considered to be unsafe in a requirements file: -pip==25.0.1 +uv==0.6.14 # via - # -c /Users/gauthier/Projects/aider/requirements.txt - # -c requirements.txt + # -c requirements/common-constraints.txt + # -r requirements/requirements-dev.in +virtualenv==20.30.0 + # via + # -c requirements/common-constraints.txt + # pre-commit +wheel==0.45.1 + # via + # -c requirements/common-constraints.txt # pip-tools -setuptools==75.8.1 - # via pip-tools diff --git a/requirements/requirements-help.in b/requirements/requirements-help.in index c865a6a3f..43e302cca 100644 --- a/requirements/requirements-help.in +++ b/requirements/requirements-help.in @@ -1,10 +1,11 @@ --c ../requirements.txt -# -# pip-compile --output-file=requirements-hf.txt requirements-hf.in --upgrade -# - -llama-index-core llama-index-embeddings-huggingface -# requirement-help and requirements-playwright choose different versions -greenlet==3.0.3 +# Because sentence-transformers doesn't like >=2 +numpy<2 + +# Mac x86 only supports 2.2.2 +# https://discuss.pytorch.org/t/why-no-macosx-x86-64-build-after-torch-2-2-2-cp39-none-macosx-10-9-x86-64-whl/204546/2 +torch==2.2.2 + +# Later versions break test_help in GitHub Actions on Windows and Ubuntu +llama-index-core==0.12.26 \ No newline at end of file diff --git a/requirements/requirements-help.txt b/requirements/requirements-help.txt index 9b138aed2..ed7c40aab 100644 --- a/requirements/requirements-help.txt +++ b/requirements/requirements-help.txt @@ -1,312 +1,323 @@ -# -# This file is autogenerated by pip-compile with Python 3.12 -# by the following command: -# -# pip-compile --allow-unsafe --constraint=requirements.txt --constraint=requirements/requirements-dev.txt --output-file=requirements/requirements-help.txt requirements/requirements-help.in -# -aiohappyeyeballs==2.4.6 +# This file was autogenerated by uv via the following command: +# uv pip compile --no-strip-extras --constraint=requirements/common-constraints.txt --output-file=requirements/requirements-help.txt requirements/requirements-help.in +aiohappyeyeballs==2.6.1 # via - # -c /Users/gauthier/Projects/aider/requirements.txt - # -c requirements.txt + # -c requirements/common-constraints.txt # aiohttp -aiohttp==3.11.13 +aiohttp==3.11.16 # via - # -c /Users/gauthier/Projects/aider/requirements.txt - # -c requirements.txt + # -c requirements/common-constraints.txt # huggingface-hub # llama-index-core aiosignal==1.3.2 # via - # -c /Users/gauthier/Projects/aider/requirements.txt - # -c requirements.txt + # -c requirements/common-constraints.txt # aiohttp annotated-types==0.7.0 # via - # -c /Users/gauthier/Projects/aider/requirements.txt - # -c requirements.txt + # -c requirements/common-constraints.txt # pydantic -anyio==4.8.0 +anyio==4.9.0 # via - # -c /Users/gauthier/Projects/aider/requirements.txt - # -c requirements.txt + # -c requirements/common-constraints.txt # httpx -attrs==25.1.0 +attrs==25.3.0 # via - # -c /Users/gauthier/Projects/aider/requirements.txt - # -c requirements.txt + # -c requirements/common-constraints.txt # aiohttp +banks==2.1.1 + # via + # -c requirements/common-constraints.txt + # llama-index-core certifi==2025.1.31 # via - # -c /Users/gauthier/Projects/aider/requirements.txt - # -c requirements.txt - # -c requirements/requirements-dev.txt + # -c requirements/common-constraints.txt # httpcore # httpx # requests charset-normalizer==3.4.1 # via - # -c /Users/gauthier/Projects/aider/requirements.txt - # -c requirements.txt - # -c requirements/requirements-dev.txt + # -c requirements/common-constraints.txt # requests click==8.1.8 # via - # -c /Users/gauthier/Projects/aider/requirements.txt - # -c requirements.txt - # -c requirements/requirements-dev.txt + # -c requirements/common-constraints.txt # nltk -dataclasses-json==0.6.7 - # via llama-index-core -deprecated==1.2.18 - # via llama-index-core -dirtyjson==1.0.8 - # via llama-index-core -filelock==3.17.0 +colorama==0.4.6 # via - # -c /Users/gauthier/Projects/aider/requirements.txt - # -c requirements.txt - # -c requirements/requirements-dev.txt + # -c requirements/common-constraints.txt + # griffe +dataclasses-json==0.6.7 + # via + # -c requirements/common-constraints.txt + # llama-index-core +deprecated==1.2.18 + # via + # -c requirements/common-constraints.txt + # banks + # llama-index-core +dirtyjson==1.0.8 + # via + # -c requirements/common-constraints.txt + # llama-index-core +filelock==3.18.0 + # via + # -c requirements/common-constraints.txt # huggingface-hub # torch # transformers filetype==1.2.0 - # via llama-index-core + # via + # -c requirements/common-constraints.txt + # llama-index-core frozenlist==1.5.0 # via - # -c /Users/gauthier/Projects/aider/requirements.txt - # -c requirements.txt + # -c requirements/common-constraints.txt # aiohttp # aiosignal -fsspec==2025.2.0 +fsspec==2025.3.2 # via - # -c /Users/gauthier/Projects/aider/requirements.txt - # -c requirements.txt + # -c requirements/common-constraints.txt # huggingface-hub # llama-index-core # torch -greenlet==3.0.3 +greenlet==3.1.1 # via - # -r requirements/requirements-help.in + # -c requirements/common-constraints.txt # sqlalchemy +griffe==1.7.2 + # via + # -c requirements/common-constraints.txt + # banks h11==0.14.0 # via - # -c /Users/gauthier/Projects/aider/requirements.txt - # -c requirements.txt + # -c requirements/common-constraints.txt # httpcore -httpcore==1.0.7 +httpcore==1.0.8 # via - # -c /Users/gauthier/Projects/aider/requirements.txt - # -c requirements.txt + # -c requirements/common-constraints.txt # httpx httpx==0.28.1 # via - # -c /Users/gauthier/Projects/aider/requirements.txt - # -c requirements.txt + # -c requirements/common-constraints.txt # llama-index-core -huggingface-hub[inference]==0.29.1 +huggingface-hub[inference]==0.30.2 # via - # -c /Users/gauthier/Projects/aider/requirements.txt - # -c requirements.txt + # -c requirements/common-constraints.txt # llama-index-embeddings-huggingface # sentence-transformers # tokenizers # transformers idna==3.10 # via - # -c /Users/gauthier/Projects/aider/requirements.txt - # -c requirements.txt - # -c requirements/requirements-dev.txt + # -c requirements/common-constraints.txt # anyio # httpx # requests # yarl -jinja2==3.1.5 +jinja2==3.1.6 # via - # -c /Users/gauthier/Projects/aider/requirements.txt - # -c requirements.txt - # -c requirements/requirements-dev.txt + # -c requirements/common-constraints.txt + # banks # torch joblib==1.4.2 # via + # -c requirements/common-constraints.txt # nltk # scikit-learn -llama-index-core==0.12.20 +llama-index-core==0.12.26 # via + # -c requirements/common-constraints.txt # -r requirements/requirements-help.in # llama-index-embeddings-huggingface -llama-index-embeddings-huggingface==0.5.2 - # via -r requirements/requirements-help.in +llama-index-embeddings-huggingface==0.5.3 + # via + # -c requirements/common-constraints.txt + # -r requirements/requirements-help.in markupsafe==3.0.2 # via - # -c /Users/gauthier/Projects/aider/requirements.txt - # -c requirements.txt - # -c requirements/requirements-dev.txt + # -c requirements/common-constraints.txt # jinja2 marshmallow==3.26.1 - # via dataclasses-json -mpmath==1.3.0 - # via sympy -multidict==6.1.0 # via - # -c /Users/gauthier/Projects/aider/requirements.txt - # -c requirements.txt + # -c requirements/common-constraints.txt + # dataclasses-json +mpmath==1.3.0 + # via + # -c requirements/common-constraints.txt + # sympy +multidict==6.4.3 + # via + # -c requirements/common-constraints.txt # aiohttp # yarl mypy-extensions==1.0.0 - # via typing-inspect + # via + # -c requirements/common-constraints.txt + # typing-inspect nest-asyncio==1.6.0 - # via llama-index-core + # via + # -c requirements/common-constraints.txt + # llama-index-core networkx==3.2.1 # via - # -c /Users/gauthier/Projects/aider/requirements.txt - # -c requirements.txt + # -c requirements/common-constraints.txt # llama-index-core # torch nltk==3.9.1 - # via llama-index-core + # via + # -c requirements/common-constraints.txt + # llama-index-core numpy==1.26.4 # via - # -c /Users/gauthier/Projects/aider/requirements.txt - # -c requirements.txt - # -c requirements/requirements-dev.txt + # -c requirements/common-constraints.txt + # -r requirements/requirements-help.in # llama-index-core # scikit-learn # scipy # transformers packaging==24.2 # via - # -c /Users/gauthier/Projects/aider/requirements.txt - # -c requirements.txt - # -c requirements/requirements-dev.txt + # -c requirements/common-constraints.txt # huggingface-hub # marshmallow # transformers -pillow==10.4.0 +pillow==11.1.0 # via - # -c /Users/gauthier/Projects/aider/requirements.txt - # -c requirements.txt - # -c requirements/requirements-dev.txt + # -c requirements/common-constraints.txt # llama-index-core # sentence-transformers -propcache==0.3.0 +platformdirs==4.3.7 # via - # -c /Users/gauthier/Projects/aider/requirements.txt - # -c requirements.txt + # -c requirements/common-constraints.txt + # banks +propcache==0.3.1 + # via + # -c requirements/common-constraints.txt # aiohttp # yarl -pydantic==2.10.6 +pydantic==2.11.3 # via - # -c /Users/gauthier/Projects/aider/requirements.txt - # -c requirements.txt + # -c requirements/common-constraints.txt + # banks # llama-index-core -pydantic-core==2.27.2 +pydantic-core==2.33.1 # via - # -c /Users/gauthier/Projects/aider/requirements.txt - # -c requirements.txt + # -c requirements/common-constraints.txt # pydantic pyyaml==6.0.2 # via - # -c /Users/gauthier/Projects/aider/requirements.txt - # -c requirements.txt - # -c requirements/requirements-dev.txt + # -c requirements/common-constraints.txt # huggingface-hub # llama-index-core # transformers regex==2024.11.6 # via - # -c /Users/gauthier/Projects/aider/requirements.txt - # -c requirements.txt + # -c requirements/common-constraints.txt # nltk # tiktoken # transformers requests==2.32.3 # via - # -c /Users/gauthier/Projects/aider/requirements.txt - # -c requirements.txt - # -c requirements/requirements-dev.txt + # -c requirements/common-constraints.txt # huggingface-hub # llama-index-core # tiktoken # transformers safetensors==0.5.3 - # via transformers + # via + # -c requirements/common-constraints.txt + # transformers scikit-learn==1.6.1 - # via sentence-transformers + # via + # -c requirements/common-constraints.txt + # sentence-transformers scipy==1.13.1 # via - # -c /Users/gauthier/Projects/aider/requirements.txt - # -c requirements.txt + # -c requirements/common-constraints.txt # scikit-learn # sentence-transformers -sentence-transformers==3.4.1 - # via llama-index-embeddings-huggingface +sentence-transformers==4.0.2 + # via + # -c requirements/common-constraints.txt + # llama-index-embeddings-huggingface sniffio==1.3.1 # via - # -c /Users/gauthier/Projects/aider/requirements.txt - # -c requirements.txt + # -c requirements/common-constraints.txt # anyio -sqlalchemy[asyncio]==2.0.38 +sqlalchemy[asyncio]==2.0.40 # via + # -c requirements/common-constraints.txt # llama-index-core - # sqlalchemy sympy==1.13.3 - # via torch -tenacity==9.0.0 - # via llama-index-core -threadpoolctl==3.5.0 - # via scikit-learn + # via + # -c requirements/common-constraints.txt + # torch +tenacity==9.1.2 + # via + # -c requirements/common-constraints.txt + # llama-index-core +threadpoolctl==3.6.0 + # via + # -c requirements/common-constraints.txt + # scikit-learn tiktoken==0.9.0 # via - # -c /Users/gauthier/Projects/aider/requirements.txt - # -c requirements.txt + # -c requirements/common-constraints.txt # llama-index-core -tokenizers==0.19.1 +tokenizers==0.21.1 # via - # -c /Users/gauthier/Projects/aider/requirements.txt - # -c requirements.txt + # -c requirements/common-constraints.txt # transformers torch==2.2.2 - # via sentence-transformers + # via + # -c requirements/common-constraints.txt + # -r requirements/requirements-help.in + # sentence-transformers tqdm==4.67.1 # via - # -c /Users/gauthier/Projects/aider/requirements.txt - # -c requirements.txt + # -c requirements/common-constraints.txt # huggingface-hub # llama-index-core # nltk # sentence-transformers # transformers -transformers==4.44.2 - # via sentence-transformers -typing-extensions==4.12.2 +transformers==4.51.2 # via - # -c /Users/gauthier/Projects/aider/requirements.txt - # -c requirements.txt - # -c requirements/requirements-dev.txt + # -c requirements/common-constraints.txt + # sentence-transformers +typing-extensions==4.13.2 + # via + # -c requirements/common-constraints.txt # anyio # huggingface-hub # llama-index-core # pydantic # pydantic-core + # sentence-transformers # sqlalchemy # torch # typing-inspect + # typing-inspection typing-inspect==0.9.0 # via + # -c requirements/common-constraints.txt # dataclasses-json # llama-index-core -urllib3==2.3.0 +typing-inspection==0.4.0 # via - # -c /Users/gauthier/Projects/aider/requirements.txt - # -c requirements.txt - # -c requirements/requirements-dev.txt + # -c requirements/common-constraints.txt + # pydantic +urllib3==2.4.0 + # via + # -c requirements/common-constraints.txt # requests wrapt==1.17.2 # via + # -c requirements/common-constraints.txt # deprecated # llama-index-core -yarl==1.18.3 +yarl==1.19.0 # via - # -c /Users/gauthier/Projects/aider/requirements.txt - # -c requirements.txt + # -c requirements/common-constraints.txt # aiohttp diff --git a/requirements/requirements-playwright.in b/requirements/requirements-playwright.in index fd88b61e2..508a5f469 100644 --- a/requirements/requirements-playwright.in +++ b/requirements/requirements-playwright.in @@ -1,6 +1 @@ --c ../requirements.txt - playwright - -# requirement-help and requirements-playwright choose different versions -greenlet==3.0.3 diff --git a/requirements/requirements-playwright.txt b/requirements/requirements-playwright.txt index f84ec4f68..37b78ff87 100644 --- a/requirements/requirements-playwright.txt +++ b/requirements/requirements-playwright.txt @@ -1,23 +1,18 @@ -# -# This file is autogenerated by pip-compile with Python 3.12 -# by the following command: -# -# pip-compile --allow-unsafe --constraint=requirements.txt --constraint=requirements/requirements-browser.txt --constraint=requirements/requirements-dev.txt --constraint=requirements/requirements-help.txt --output-file=requirements/requirements-playwright.txt requirements/requirements-playwright.in -# -greenlet==3.0.3 +# This file was autogenerated by uv via the following command: +# uv pip compile --no-strip-extras --constraint=requirements/common-constraints.txt --output-file=requirements/requirements-playwright.txt requirements/requirements-playwright.in +greenlet==3.1.1 # via - # -c requirements/requirements-help.txt - # -r requirements/requirements-playwright.in + # -c requirements/common-constraints.txt # playwright -playwright==1.47.0 - # via -r requirements/requirements-playwright.in -pyee==12.0.0 - # via playwright -typing-extensions==4.12.2 +playwright==1.51.0 # via - # -c /Users/gauthier/Projects/aider/requirements.txt - # -c requirements.txt - # -c requirements/requirements-browser.txt - # -c requirements/requirements-dev.txt - # -c requirements/requirements-help.txt + # -c requirements/common-constraints.txt + # -r requirements/requirements-playwright.in +pyee==12.1.1 + # via + # -c requirements/common-constraints.txt + # playwright +typing-extensions==4.13.2 + # via + # -c requirements/common-constraints.txt # pyee diff --git a/requirements/requirements.in b/requirements/requirements.in index f20b07103..101f16988 100644 --- a/requirements/requirements.in +++ b/requirements/requirements.in @@ -1,7 +1,3 @@ -# -# pip-compile requirements.in --upgrade -# - pydub configargparse GitPython @@ -31,6 +27,7 @@ psutil watchfiles socksio pip +pillow # The proper dependency is networkx[default], but this brings # in matplotlib and a bunch of other deps @@ -45,20 +42,7 @@ networkx<3.3 # Pin below 1.14 to retain python 3.9 compatibility. scipy<1.14 -# v0.22.2 seems to break tree-sitter-languages? -tree-sitter==0.21.3 - # GitHub Release action failing on "KeyError: 'home-page'" # https://github.com/pypa/twine/blob/6fbf880ee60915cf1666348c4bdd78a10415f2ac/twine/__init__.py#L40 # Uses importlib-metadata importlib-metadata<8.0.0 - -# Because sentence-transformers doesn't like >=2 -numpy<2 - -# Going past this makes dependencies unresolvable -# Seems to be caused by sentence-transformers -tokenizers==0.19.1 - -# streamlit 1.39.0 depends on this, as far back as 1.22 which is ancient and doesn't have chat ui -Pillow<11 diff --git a/requirements/tree-sitter.in b/requirements/tree-sitter.in new file mode 100644 index 000000000..eba2e6770 --- /dev/null +++ b/requirements/tree-sitter.in @@ -0,0 +1,3 @@ + +tree-sitter==0.23.2; python_version < "3.10" +tree-sitter==0.24.0; python_version >= "3.10" diff --git a/scripts/30k-image.py b/scripts/30k-image.py new file mode 100644 index 000000000..9a62206e4 --- /dev/null +++ b/scripts/30k-image.py @@ -0,0 +1,235 @@ +#!/usr/bin/env python +""" +Generate a celebratory SVG image for Aider reaching 30,000 GitHub stars. +This creates a shareable social media graphic with confetti animation. +""" + +import argparse +import base64 +import math +import os +import random +from pathlib import Path + +# Default colors for the celebration image +AIDER_GREEN = "#14b014" +AIDER_BLUE = "#4C6EF5" +DARK_COLOR = "#212529" +LIGHT_COLOR = "#F8F9FA" +GOLD_COLOR = "#f1c40f" + +# Default dimensions for social sharing +DEFAULT_WIDTH = 1200 +DEFAULT_HEIGHT = 630 + + +def embed_font(): + """Returns base64 encoded font data for the GlassTTYVT220 font.""" + # Path to the font file + font_path = ( + Path(__file__).parent.parent / "aider" / "website" / "assets" / "Glass_TTY_VT220.ttf" + ) + + # If font file doesn't exist, return empty string + if not font_path.exists(): + print(f"Warning: Font file not found at {font_path}") + return "" + + # Read and encode the font file + with open(font_path, "rb") as f: + font_data = f.read() + + # Return base64 encoded font data + return base64.b64encode(font_data).decode("utf-8") + + +def generate_confetti(count=150, width=DEFAULT_WIDTH, height=DEFAULT_HEIGHT): + """Generate SVG confetti elements for the celebration.""" + confetti = [] + colors = [AIDER_GREEN, AIDER_BLUE, GOLD_COLOR, "#e74c3c", "#9b59b6", "#3498db", "#2ecc71"] + + # Define text safe zones + # Main content safe zone (centered area) + safe_zone_x_min = width * 0.2 + safe_zone_x_max = width * 0.8 + safe_zone_y_min = height * 0.25 + safe_zone_y_max = height * 0.75 + + # Footer safe zone (for GitHub URL) + footer_safe_zone_x_min = width * 0.25 + footer_safe_zone_x_max = width * 0.75 + footer_safe_zone_y_min = height - 100 # 100px from bottom + footer_safe_zone_y_max = height # Bottom of image + + # Keep trying until we have enough confetti pieces + attempts = 0 + confetti_count = 0 + + while confetti_count < count and attempts < count * 3: + attempts += 1 + + # Generate random position + x = random.randint(0, width) + y = random.randint(0, height) + + # Skip if the position is in either of the safe zones + if ( + (safe_zone_x_min < x < safe_zone_x_max) and (safe_zone_y_min < y < safe_zone_y_max) + ) or ( + (footer_safe_zone_x_min < x < footer_safe_zone_x_max) + and (footer_safe_zone_y_min < y < footer_safe_zone_y_max) + ): + continue + + confetti_count += 1 + size = random.randint(5, 15) + color = random.choice(colors) + rotation = random.randint(0, 360) + delay = random.uniform(0, 2) + duration = random.uniform(1, 3) + + # Randomly choose between rect (square), circle, and star shapes + shape_type = random.choice(["rect", "circle", "star"]) + + if shape_type == "rect": + shape = f""" + + + """ + elif shape_type == "circle": + shape = f""" + + + """ + else: # star + # Create a simple 5-point star + points = [] + for j in range(5): + angle = j * 2 * 3.14159 / 5 + x_point = x + (size * 0.5) * math.cos(angle) + y_point = y + (size * 0.5) * math.sin(angle) + points.append(f"{x_point},{y_point}") + + # Inner points of the star + inner_angle = angle + 3.14159 / 5 + inner_x = x + (size * 0.2) * math.cos(inner_angle) + inner_y = y + (size * 0.2) * math.sin(inner_angle) + points.append(f"{inner_x},{inner_y}") + + points_str = " ".join(points) + shape = f""" + + + + """ + + confetti.append(shape) + + return "\n".join(confetti) + + +def generate_celebration_svg(output_path=None, width=DEFAULT_WIDTH, height=DEFAULT_HEIGHT): + """Generate a celebratory SVG for 30K GitHub stars.""" + + # Font embedding + font_data = embed_font() + font_face = f""" + @font-face {{ + font-family: 'GlassTTYVT220'; + src: url(data:font/truetype;charset=utf-8;base64,{font_data}) format('truetype'); + font-weight: normal; + font-style: normal; + }} + """ if font_data else "" + + # Generate confetti elements + confetti = generate_confetti(count=150, width=width, height=height) + + # Create the SVG content + svg_content = f""" + + + + + + + + + + + + + + + + + + + + + + + + + + + + {confetti} + + + + 30,000 GitHub stars! + Thank you to our amazing community! + github.com/Aider-AI/aider + + + +""" + + # Write to file if output path is specified + if output_path: + with open(output_path, "w") as f: + f.write(svg_content) + print(f"Celebration SVG saved to {output_path}") + + return svg_content + + +if __name__ == "__main__": + parser = argparse.ArgumentParser( + description="Generate a celebration SVG for Aider's 30K GitHub stars" + ) + parser.add_argument( + "--output", + "-o", + type=str, + default="aider-30k-stars.svg", + help="Output file path (default: aider-30k-stars.svg)", + ) + parser.add_argument( + "--width", + "-w", + type=int, + default=DEFAULT_WIDTH, + help=f"Image width in pixels (default: {DEFAULT_WIDTH})", + ) + parser.add_argument( + "--height", + "-ht", + type=int, + default=DEFAULT_HEIGHT, + help=f"Image height in pixels (default: {DEFAULT_HEIGHT})", + ) + args = parser.parse_args() + + # Generate the SVG + generate_celebration_svg(args.output, args.width, args.height) diff --git a/scripts/blame.py b/scripts/blame.py index 6c395a293..40a561d8a 100755 --- a/scripts/blame.py +++ b/scripts/blame.py @@ -13,8 +13,10 @@ import yaml from tqdm import tqdm website_files = [ + "aider/website/index.html", "aider/website/share/index.md", "aider/website/_includes/head_custom.html", + "aider/website/_includes/home.css", "aider/website/docs/leaderboards/index.md", ] @@ -222,9 +224,10 @@ def get_counts_for_file(start_tag, end_tag, authors, fname): [ "git", "blame", - "-M", - "-C", - "-C", + "-M100", # Detect moved lines within a file with 100% similarity + "-C100", # Detect moves across files with 100% similarity + "-C", # Increase detection effort + "-C", # Increase detection effort even more "--abbrev=9", f"{start_tag}..{end_tag}", "--", @@ -233,7 +236,18 @@ def get_counts_for_file(start_tag, end_tag, authors, fname): ) else: text = run( - ["git", "blame", "-M", "-C", "-C", "--abbrev=9", f"{start_tag}..HEAD", "--", fname] + [ + "git", + "blame", + "-M100", # Detect moved lines within a file with 100% similarity + "-C100", # Detect moves across files with 100% similarity + "-C", # Increase detection effort + "-C", # Increase detection effort even more + "--abbrev=9", + f"{start_tag}..HEAD", + "--", + fname, + ] ) if not text: return None diff --git a/scripts/clean_metadata.py b/scripts/clean_metadata.py new file mode 100755 index 000000000..c1031857b --- /dev/null +++ b/scripts/clean_metadata.py @@ -0,0 +1,258 @@ +#!/usr/bin/env python + +import difflib +import json +import re +from pathlib import Path + +import json5 + + +def find_block_lines(lines, key_to_remove): + """Finds the start and end line indices for a top-level key's block.""" + start_line_idx = -1 + # Regex to find the line starting the key definition, allowing for whitespace + # and ensuring it's the key we want (e.g., avoid matching "key1_extra": ...) + key_pattern = re.compile(r'^\s*"' + re.escape(key_to_remove) + r'"\s*:\s*{?') + + for i, line in enumerate(lines): + if key_pattern.match(line.strip()): + start_line_idx = i + break + + if start_line_idx == -1: + # Key might not start with '{' on the same line, check if it starts immediately after + key_pattern_no_brace = re.compile(r'^\s*"' + re.escape(key_to_remove) + r'"\s*:\s*$') + for i, line in enumerate(lines): + if key_pattern_no_brace.match(line.strip()): + # Look for the opening brace on the next non-empty/comment line + j = i + 1 + while j < len(lines): + stripped_next_line = lines[j].strip() + if not stripped_next_line or stripped_next_line.startswith("//"): + j += 1 + continue + if stripped_next_line.startswith("{"): + start_line_idx = i # Start from the key definition line + break + else: + # False alarm, the line after the key wasn't '{' + break + if start_line_idx != -1: + break + + if start_line_idx == -1: + print( + f"Warning: Could not reliably find start line for '{key_to_remove}'. Skipping removal." + ) + return None, None # Key block start not found clearly + + brace_level = 0 + in_string = False + block_started = False + end_line_idx = -1 + + # Start brace counting from the identified start line + for i in range(start_line_idx, len(lines)): + line = lines[i] + # Simple brace counting - might be fooled by braces in comments or strings + # This is a limitation of pure text processing without full parsing + for char_idx, char in enumerate(line): + # Rudimentary string detection + if char == '"': + # Check if preceded by an odd number of backslashes (escaped quote) + backslashes = 0 + temp_idx = char_idx - 1 + while temp_idx >= 0 and line[temp_idx] == "\\": + backslashes += 1 + temp_idx -= 1 + if backslashes % 2 == 0: + in_string = not in_string + + if not in_string: + if char == "{": + brace_level += 1 + block_started = True # Mark that we've entered the block + elif char == "}": + brace_level -= 1 + + # Check if the block ends *after* processing the entire line + if block_started and brace_level == 0: + end_line_idx = i + break + + if end_line_idx == -1: + print( + f"Warning: Could not find end of block for '{key_to_remove}' starting at line" + f" {start_line_idx + 1}. Skipping removal." + ) + return None, None # Block end not found + + return start_line_idx, end_line_idx + + +def remove_block_surgically(file_path, key_to_remove): + """Reads the file, removes the block for the key, writes back.""" + try: + # Read with universal newlines, but keep track for writing + with open(file_path, "r") as f: + content = f.read() + lines = content.splitlines(keepends=True) # Keep original line endings + except Exception as e: + print(f"Error reading {file_path} for removal: {e}") + return False + + start_idx, end_idx = find_block_lines(lines, key_to_remove) + + if start_idx is None or end_idx is None: + return False # Error message already printed by find_block_lines + + # Prepare the lines to be written, excluding the identified block + output_lines = lines[:start_idx] + lines[end_idx + 1 :] + + # Note: Comma handling is omitted for simplicity. User may need manual fix. + + try: + with open(file_path, "w") as f: + f.writelines(output_lines) + print(f"Successfully removed '{key_to_remove}' block and updated {file_path}.") + return True + except Exception as e: + print(f"Error writing updated data to {file_path} after removing {key_to_remove}: {e}") + return False + + +def main(): + script_dir = Path(__file__).parent.resolve() + # Adjust path relative to the script's location in the aider repo + litellm_path = script_dir.parent / "../litellm/model_prices_and_context_window.json" + aider_path = script_dir / "../aider/resources/model-metadata.json" + + if not litellm_path.exists(): + print(f"Error: LiteLLM metadata file not found at {litellm_path}") + return + + if not aider_path.exists(): + print(f"Error: Aider metadata file not found at {aider_path}") + return + + try: + with open(litellm_path, "r") as f: + litellm_data = json.load(f) + except json.JSONDecodeError as e: + print(f"Error decoding JSON from {litellm_path}: {e}") + return + except Exception as e: + print(f"Error reading {litellm_path}: {e}") + return + + try: + # Use json5 for the aider metadata file as it might contain comments + with open(aider_path, "r") as f: + aider_data = json5.load(f) + except json.JSONDecodeError as e: + print(f"Error decoding JSON from {aider_path}: {e}") + return + except Exception as e: + print(f"Error reading {aider_path}: {e}") + return + + litellm_keys = set(litellm_data.keys()) + aider_keys = set(aider_data.keys()) + + common_keys = sorted(list(litellm_keys.intersection(aider_keys))) + removed_count = 0 + + if common_keys: + print("Comparing common models found in both files:\n") + for key in common_keys: + print(f"--- {key} (aider) ---") + print(f"+++ {key} (litellm) +++") + + litellm_entry = litellm_data.get(key, {}) + aider_entry = aider_data.get(key, {}) + + # Convert dicts to formatted JSON strings for comparison + # First, compare the dictionaries directly for semantic equality + if litellm_entry == aider_entry: + print(f"'{key}': Entries are semantically identical.") + print("\n" + "=" * 40) + print("-" * 40 + "\n") # Separator for the next model + continue # Skip diff and removal prompt for identical entries + + # Generate unified diff + # If dictionaries differ, generate JSON strings to show the diff + # Add a dummy key to ensure the *real* last key gets a comma + litellm_entry_copy = litellm_entry.copy() + aider_entry_copy = aider_entry.copy() + dummy_key = "zzzdummykey" + litellm_entry_copy[dummy_key] = True + aider_entry_copy[dummy_key] = True + + litellm_json_lines = json.dumps( + litellm_entry_copy, indent=4, sort_keys=True + ).splitlines() + aider_json_lines = json.dumps(aider_entry_copy, indent=4, sort_keys=True).splitlines() + + # Remove the dummy key line before diffing + litellm_json_filtered = [line for line in litellm_json_lines if dummy_key not in line] + aider_json_filtered = [line for line in aider_json_lines if dummy_key not in line] + + diff = difflib.unified_diff( + aider_json_filtered, + litellm_json_filtered, + fromfile=f"{key} (aider)", + tofile=f"{key} (litellm)", + lineterm="", + n=max(len(litellm_json_filtered), len(aider_json_filtered)), # Show all lines + ) + + # Print the diff, skipping the header lines generated by unified_diff + diff_lines = list(diff)[2:] + if not diff_lines: + # This case should ideally not be reached if dict comparison was done first, + # but kept as a fallback. + print( + "(No textual differences found, though dictionaries might differ in type/order)" + ) + else: + for line in diff_lines: + # Add color for better readability (optional, requires a library + # like 'termcolor' or manual ANSI codes) + # Simple +/- indication is standard for diffs + print(line) + print("\n" + "=" * 40) + + # Ask user if they want to remove the entry from aider's metadata + response = ( + input(f"Remove '{key}' from aider/resources/model-metadata.json? (y/N): ") + .strip() + .lower() + ) + if response == "y": + # Perform surgical removal from the text file + if remove_block_surgically(aider_path, key): + removed_count += 1 + # Optional: Also remove from the in-memory dict if needed later, + # but it's not strictly necessary if we reload or finish now. + # if key in aider_data: del aider_data[key] + else: + print(f"Failed to remove '{key}' block surgically.") + # Key might still be in aider_data if removal failed + else: + print(f"Keeping '{key}'.") + print("-" * 40 + "\n") # Separator for the next model + + else: + print("No common models found between the two files.") + return # Exit if no common keys + + # Final summary message + if removed_count > 0: + print(f"\nFinished comparing. A total of {removed_count} entr(y/ies) were removed.") + else: + print("\nFinished comparing. No entries were removed.") + + +if __name__ == "__main__": + main() diff --git a/scripts/dl_icons.py b/scripts/dl_icons.py new file mode 100644 index 000000000..8240ca641 --- /dev/null +++ b/scripts/dl_icons.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python3 +""" +Download Material Design Icons SVGs used in the README and save to local assets. +""" + +import os +from pathlib import Path + +import requests + +# Create the directory if it doesn't exist +ICONS_DIR = Path("aider/website/assets/icons") +ICONS_DIR.mkdir(parents=True, exist_ok=True) + +# Icons used in the README.md features section +ICONS = [ + "brain", + "map-outline", + "code-tags", + "source-branch", + "monitor", + "image-multiple", + "microphone", + "check-all", + "content-copy", +] + + +def download_icon(icon_name): + """Download an SVG icon from Material Design Icons CDN.""" + url = f"https://cdn.jsdelivr.net/npm/@mdi/svg@latest/svg/{icon_name}.svg" + print(f"Downloading {url}...") + + response = requests.get(url) + if response.status_code != 200: + print(f"Failed to download {icon_name}.svg: {response.status_code}") + return False + + # Save the SVG file + output_path = ICONS_DIR / f"{icon_name}.svg" + with open(output_path, "wb") as f: + f.write(response.content) + + print(f"Saved {icon_name}.svg to {output_path}") + return True + + +def main(): + print(f"Downloading icons to {ICONS_DIR}") + + success_count = 0 + for icon in ICONS: + if download_icon(icon): + success_count += 1 + + print(f"Successfully downloaded {success_count}/{len(ICONS)} icons") + + +if __name__ == "__main__": + main() diff --git a/scripts/history_prompts.py b/scripts/history_prompts.py index 4079d9d3e..5902b9f57 100644 --- a/scripts/history_prompts.py +++ b/scripts/history_prompts.py @@ -1,11 +1,15 @@ history_prompt = """ -Update the history with changes shown in the diffs. +Update the history doc with changes shown in the diffs. Describe actual user-facing changes, not every single commit that was made implementing them. Only add new items not already listed. Do NOT edit or update existing history entries. Do NOT add duplicate entries for changes that have existing history entries. +Pay attention to see if changes are later modified or superseded. +The history doc should only reflect the *final* version of changes which have evolved within a version's commit history. +If the history doc already describes the final behavior, don't document the changes that led us there. + End each bullet with a period. If the change was made by someone other than Paul Gauthier note it at the end of the bullet point as ", by XXX." diff --git a/scripts/homepage.py b/scripts/homepage.py new file mode 100755 index 000000000..a823ad689 --- /dev/null +++ b/scripts/homepage.py @@ -0,0 +1,619 @@ +#!/usr/bin/env python3 + +import argparse +import json +import os +import sys +import time +from datetime import datetime + +import requests +import yaml +from dotenv import load_dotenv +from google.cloud import bigquery +from google.oauth2 import service_account + +TOKENS_PER_WEEK = "15B" + +# Badge tooltip texts +GITHUB_STARS_TOOLTIP = "Total number of GitHub stars the Aider project has received" +PYPI_DOWNLOADS_TOOLTIP = "Total number of installations via pip from PyPI" +TOKENS_WEEKLY_TOOLTIP = "Number of tokens processed weekly by Aider users" +OPENROUTER_TOOLTIP = "Aider's ranking among applications on the OpenRouter platform" +SINGULARITY_TOOLTIP = "Percentage of the new code in Aider's last release written by Aider itself" + +# Cache settings +CACHE_DIR = os.path.expanduser("~/.cache/aider-badges") +CACHE_DURATION = 24 * 60 * 60 # 24 hours in seconds + + +def ensure_cache_dir(): + """Create the cache directory if it doesn't exist""" + os.makedirs(CACHE_DIR, exist_ok=True) + + +def get_cache_path(package_name): + """Get the path to the cache file for a package""" + return os.path.join(CACHE_DIR, f"{package_name}_downloads.json") + + +def read_from_cache(package_name): + """ + Read download statistics from cache if available and not expired + Returns (downloads, is_valid) tuple where is_valid is True if cache is valid + """ + cache_path = get_cache_path(package_name) + + if not os.path.exists(cache_path): + return None, False + + try: + with open(cache_path, "r") as f: + cache_data = json.load(f) + + # Check if cache is expired + timestamp = cache_data.get("timestamp", 0) + current_time = time.time() + + if current_time - timestamp > CACHE_DURATION: + return None, False + + return cache_data.get("downloads"), True + except Exception as e: + print(f"Error reading from cache: {e}", file=sys.stderr) + return None, False + + +def write_to_cache(package_name, downloads): + """Write download statistics to cache""" + cache_path = get_cache_path(package_name) + + try: + ensure_cache_dir() + cache_data = { + "downloads": downloads, + "timestamp": time.time(), + "datetime": datetime.now().isoformat(), + } + + with open(cache_path, "w") as f: + json.dump(cache_data, f) + + return True + except Exception as e: + print(f"Error writing to cache: {e}", file=sys.stderr) + return False + + +def get_downloads_from_bigquery(credentials_path=None, package_name="aider-chat"): + """ + Fetch download statistics for a package from Google BigQuery PyPI dataset + Uses a 24-hour cache to avoid unnecessary API calls + """ + # Check if we have a valid cached value + cached_downloads, is_valid = read_from_cache(package_name) + if is_valid: + print(f"Using cached download statistics for {package_name} (valid for 24 hours)") + return cached_downloads + + print(f"Cache invalid or expired, fetching fresh download statistics for {package_name}") + + try: + # Initialize credentials if path provided + credentials = None + if credentials_path: + credentials = service_account.Credentials.from_service_account_file( + credentials_path, scopes=["https://www.googleapis.com/auth/cloud-platform"] + ) + + # Create a client + client = bigquery.Client(credentials=credentials) + + # Query to get total downloads for the package, excluding CI/CD systems + query = f""" + SELECT COUNT(*) as total_downloads + FROM `bigquery-public-data.pypi.file_downloads` + WHERE file.project = '{package_name}' + AND NOT ( + -- Exclude common CI/CD systems based on installer name patterns + LOWER(details.installer.name) LIKE '%github%' OR + LOWER(details.installer.name) LIKE '%travis%' OR + LOWER(details.installer.name) LIKE '%circle%' OR + LOWER(details.installer.name) LIKE '%jenkins%' OR + LOWER(details.installer.name) LIKE '%gitlab%' OR + LOWER(details.installer.name) LIKE '%azure%' OR + LOWER(details.installer.name) LIKE '%ci%' OR + LOWER(details.installer.name) LIKE '%cd%' OR + LOWER(details.installer.name) LIKE '%bot%' OR + LOWER(details.installer.name) LIKE '%build%' + ) + """ + + # Execute the query + query_job = client.query(query) + results = query_job.result() + + # Get the first (and only) row + for row in results: + downloads = row.total_downloads + # Write the result to cache + write_to_cache(package_name, downloads) + return downloads + + return 0 + except Exception as e: + print(f"Error fetching download statistics from BigQuery: {e}", file=sys.stderr) + # If there was an error but we have a cached value, use it even if expired + if cached_downloads is not None: + print("Using expired cached data due to BigQuery error") + return cached_downloads + return None + + +def get_total_downloads( + api_key=None, package_name="aider-chat", use_bigquery=False, credentials_path=None +): + """ + Fetch total downloads for a Python package + + If use_bigquery is True, fetches from BigQuery. + Otherwise uses pepy.tech API (requires api_key). + """ + if use_bigquery: + print(f"Using BigQuery to fetch download statistics for {package_name}") + return get_downloads_from_bigquery(credentials_path, package_name) + + # Fall back to pepy.tech API + print(f"Using pepy.tech API to fetch download statistics for {package_name}") + if not api_key: + print("API key not provided for pepy.tech", file=sys.stderr) + sys.exit(1) + + url = f"https://api.pepy.tech/api/v2/projects/{package_name}" + headers = {"X-API-Key": api_key} + + try: + response = requests.get(url, headers=headers) + response.raise_for_status() # Raise an exception for HTTP errors + + data = response.json() + total_downloads = data.get("total_downloads", 0) + + return total_downloads + except requests.exceptions.RequestException as e: + print(f"Error fetching download statistics from pepy.tech: {e}", file=sys.stderr) + sys.exit(1) + + +def get_github_stars(repo="paul-gauthier/aider"): + """ + Fetch the number of GitHub stars for a repository + """ + url = f"https://api.github.com/repos/{repo}" + headers = {"Accept": "application/vnd.github.v3+json"} + + try: + response = requests.get(url, headers=headers) + response.raise_for_status() # Raise an exception for HTTP errors + + data = response.json() + stars = data.get("stargazers_count", 0) + + return stars + except requests.exceptions.RequestException as e: + print(f"Error fetching GitHub stars: {e}", file=sys.stderr) + return None + + +def get_latest_release_aider_percentage(): + """ + Get the percentage of code written by Aider in the LATEST release + from the blame.yml file + """ + blame_path = os.path.join( + os.path.dirname(os.path.dirname(os.path.abspath(__file__))), + "aider", + "website", + "_data", + "blame.yml", + ) + + try: + with open(blame_path, "r") as f: + blame_data = yaml.safe_load(f) + + if not blame_data or len(blame_data) == 0: + return 0, "unknown" + + # Find the latest release by parsing version numbers + latest_version = None + latest_release = None + + for release in blame_data: + version_tag = release.get("end_tag", "") + if not version_tag.startswith("v"): + continue + + # Parse version like "v0.77.0" into a tuple (0, 77, 0) + try: + version_parts = tuple(int(part) for part in version_tag[1:].split(".")) + if latest_version is None or version_parts > latest_version: + latest_version = version_parts + latest_release = release + except ValueError: + # Skip if version can't be parsed as integers + continue + + if latest_release: + percentage = latest_release.get("aider_percentage", 0) + version = latest_release.get("end_tag", "unknown") + return percentage, version + + return 0, "unknown" + except Exception as e: + print(f"Error reading blame data: {e}", file=sys.stderr) + return 0, "unknown" + + +def format_number(number): + """ + Format a large number with K, M, B suffixes with 1 decimal place + """ + if number is None: + return "0" + + if number >= 1_000_000_000: + return f"{number / 1_000_000_000:.1f}B" + elif number >= 1_000_000: + return f"{number / 1_000_000:.1f}M" + elif number >= 1_000: + return f"{number / 1_000:.1f}K" + else: + return str(number) + + +def generate_badges_md(downloads, stars, aider_percentage): + """ + Generate markdown for badges with updated values + """ + # Format downloads to 1 decimal place with M suffix + downloads_formatted = format_number(downloads) + + # Round aider percentage to whole number + aider_percent_rounded = round(aider_percentage) + + markdown = f""" GitHub Stars + PyPI Downloads + Tokens per week + OpenRouter Ranking + Singularity""" # noqa + + return markdown + + +def get_badges_md(): + """ + Get all statistics and return the generated badges markdown + """ + # Load environment variables from .env file + load_dotenv() + + # Check if we should use BigQuery and get credentials path + bigquery_env = os.environ.get("USE_BIGQUERY", "false") + use_bigquery = bigquery_env.lower() in ("true", "1", "yes") or os.path.exists(bigquery_env) + credentials_path = bigquery_env if os.path.exists(bigquery_env) else None + + # Get API key from environment variable if not using BigQuery + api_key = None + if not use_bigquery: + api_key = os.environ.get("PEPY_API_KEY") + if not api_key: + print( + ( + "API key not provided and BigQuery not enabled. Please set PEPY_API_KEY" + " environment variable" + ), + file=sys.stderr, + ) + sys.exit(1) + + # Get PyPI downloads for the default package + total_downloads = get_total_downloads(api_key, "aider-chat", use_bigquery, credentials_path) + + # Get GitHub stars for the default repo + stars = get_github_stars("paul-gauthier/aider") + + # Get Aider contribution percentage in latest release + percentage, _ = get_latest_release_aider_percentage() + + # Generate and return badges markdown + return generate_badges_md(total_downloads, stars, percentage) + + +def get_badges_html(): + """ + Get all statistics and return HTML-formatted badges + """ + # Load environment variables from .env file + load_dotenv() + + # Check if we should use BigQuery and get credentials path + bigquery_env = os.environ.get("USE_BIGQUERY", "false") + use_bigquery = bigquery_env.lower() in ("true", "1", "yes") or os.path.exists(bigquery_env) + credentials_path = bigquery_env if os.path.exists(bigquery_env) else None + + # Get API key from environment variable if not using BigQuery + api_key = None + if not use_bigquery: + api_key = os.environ.get("PEPY_API_KEY") + if not api_key: + print( + ( + "API key not provided and BigQuery not enabled. Please set PEPY_API_KEY" + " environment variable" + ), + file=sys.stderr, + ) + sys.exit(1) + + # Get PyPI downloads for the default package + total_downloads = get_total_downloads(api_key, "aider-chat", use_bigquery, credentials_path) + + # Get GitHub stars for the default repo + stars = get_github_stars("paul-gauthier/aider") + + # Get Aider contribution percentage in latest release + percentage, _ = get_latest_release_aider_percentage() + + # Format values + downloads_formatted = format_number(total_downloads) + # Stars should be rounded to whole numbers + if stars is None: + stars_formatted = "0" + elif stars >= 1_000_000_000: + stars_formatted = f"{round(stars / 1_000_000_000)}B" + elif stars >= 1_000_000: + stars_formatted = f"{round(stars / 1_000_000)}M" + elif stars >= 1_000: + stars_formatted = f"{round(stars / 1_000)}K" + else: + stars_formatted = str(int(round(stars))) + aider_percent_rounded = round(percentage) + + # Generate HTML badges + html = f""" + ⭐ GitHub Stars + {stars_formatted} + + + 📦 Installs + {downloads_formatted} + +
    + 📈 Tokens/week + {TOKENS_PER_WEEK} +
    + + 🏆 OpenRouter + Top 20 + + + 🔄 Singularity + {aider_percent_rounded}% +""" # noqa + + return html + + +def get_testimonials_js(): + """ + Extract testimonials from README.md and format them as JavaScript array + """ + # Path to README.md, relative to this script + readme_path = os.path.join( + os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "README.md" + ) + + testimonials = [] + in_testimonials_section = False + + try: + with open(readme_path, "r", encoding="utf-8") as f: + lines = f.readlines() + + # Find the testimonials section + for i, line in enumerate(lines): + if line.strip() == "## Kind Words From Users": + in_testimonials_section = True + # Start processing from the next line + start_idx = i + 1 + break + + # If we found the section + if in_testimonials_section: + for i in range(start_idx, len(lines)): + line = lines[i] + # If we've hit another section, stop + if line.startswith("##"): + break + + # Process testimonial lines + if line.strip().startswith('- *"'): + try: + # Get the full line + full_line = line.strip() + + # Extract the quote text between *" and "* + if '*"' in full_line and '"*' in full_line: + quote_parts = full_line.split('*"') + if len(quote_parts) > 1: + quote_text = quote_parts[1].split('"*')[0].strip() + + # Default values + author = "Anonymous" + link = "" + + # Try to extract author and link if they exist + # Check for the em dash format first: "— [author](link)" + if "— [" in full_line and "](" in full_line: + author_parts = full_line.split("— [") + if len(author_parts) > 1: + author = author_parts[1].split("]")[0].strip() + + # Extract the link if it exists + link_parts = full_line.split("](") + if len(link_parts) > 1: + link = link_parts[1].split(")")[0].strip() + # Check for regular dash format: "- [author](link)" + elif " - [" in full_line and "](" in full_line: + author_parts = full_line.split(" - [") + if len(author_parts) > 1: + author = author_parts[1].split("]")[0].strip() + + # Extract the link if it exists + link_parts = full_line.split("](") + if len(link_parts) > 1: + link = link_parts[1].split(")")[0].strip() + # Check for em dash without link: "— author" + elif "— " in full_line: + # Format without a link, just plain text author + author_parts = full_line.split("— ") + if len(author_parts) > 1: + author = author_parts[1].strip() + # Check for regular dash without link: "- author" + elif " - " in full_line: + # Format without a link, just plain text author + author_parts = full_line.split(" - ") + if len(author_parts) > 1: + author = author_parts[1].strip() + + testimonials.append( + {"text": quote_text, "author": author, "link": link} + ) + except Exception as e: + print( + f"Error parsing testimonial line: {line}. Error: {e}", + file=sys.stderr, + ) + continue + + # Format as JavaScript array with script tags + if not testimonials: + print("No testimonials found in README.md", file=sys.stderr) + return "" + + js_array = "" + + return js_array + + except Exception as e: + print(f"Error reading testimonials from README: {e}", file=sys.stderr) + # Return empty array as fallback + return "" + + +def main(): + # Load environment variables from .env file + load_dotenv() + + # Ensure cache directory exists + ensure_cache_dir() + + parser = argparse.ArgumentParser(description="Get total downloads and GitHub stars for aider") + parser.add_argument( + "--api-key", + help=( + "pepy.tech API key (can also be set via PEPY_API_KEY in .env file or environment" + " variable)" + ), + ) + parser.add_argument( + "--package", default="aider-chat", help="Package name (default: aider-chat)" + ) + parser.add_argument( + "--github-repo", + default="paul-gauthier/aider", + help="GitHub repository (default: paul-gauthier/aider)", + ) + parser.add_argument("--markdown", action="store_true", help="Generate markdown badges block") + parser.add_argument( + "--use-bigquery", + action="store_true", + help="Use BigQuery to fetch download statistics instead of pepy.tech", + ) + parser.add_argument( + "--credentials-path", help="Path to Google Cloud service account credentials JSON file" + ) + args = parser.parse_args() + + # Determine whether to use BigQuery and get credentials path + bigquery_env = os.environ.get("USE_BIGQUERY", "false") + use_bigquery = ( + args.use_bigquery + or bigquery_env.lower() in ("true", "1", "yes") + or os.path.exists(bigquery_env) + ) + credentials_path = args.credentials_path or ( + bigquery_env if os.path.exists(bigquery_env) else None + ) + + # Check for required parameters + api_key = None + if not use_bigquery: + # Get API key from args or environment variable + api_key = args.api_key or os.environ.get("PEPY_API_KEY") + if not api_key: + print( + ( + "API key not provided and BigQuery not enabled. Please set PEPY_API_KEY" + " environment variable, use --api-key, or enable BigQuery with --use-bigquery" + ), + file=sys.stderr, + ) + sys.exit(1) + elif use_bigquery and not credentials_path and not args.credentials_path: + print( + ( + "BigQuery enabled but no credentials provided. Please set" + " USE_BIGQUERY to path of credentials file or use --credentials-path" + ), + file=sys.stderr, + ) + # Continue execution - BigQuery might work without explicit credentials in some environments + + # Get PyPI downloads + total_downloads = get_total_downloads(api_key, args.package, use_bigquery, credentials_path) + print(f"Total downloads for {args.package}: {total_downloads:,}") + + # Get GitHub stars + stars = get_github_stars(args.github_repo) + if stars is not None: + print(f"GitHub stars for {args.github_repo}: {stars:,}") + + # Get Aider contribution percentage in latest release + percentage, version = get_latest_release_aider_percentage() + print(f"Aider wrote {percentage:.2f}% of code in the LATEST release ({version})") + + # Get testimonials JavaScript + testimonials_js = get_testimonials_js() + print("\nTestimonials JavaScript:") + print(testimonials_js) + + +if __name__ == "__main__": + main() diff --git a/scripts/jekyll_run.sh b/scripts/jekyll_run.sh index 97aa071ff..da1e0a49b 100755 --- a/scripts/jekyll_run.sh +++ b/scripts/jekyll_run.sh @@ -1,13 +1,16 @@ #!/bin/bash -# Run the Docker container +# Run the Docker container with optimizations for faster builds docker run \ --rm \ -v "$PWD/aider/website:/site" \ -p 4000:4000 \ -e HISTFILE=/site/.bash_history \ + -e JEKYLL_ENV=development \ -it \ - my-jekyll-site + my-jekyll-site bundle exec jekyll serve --host 0.0.0.0 $* -# --entrypoint /bin/bash \ +# Additional options: +# --incremental: Only rebuilds files that changed +# --livereload: Auto-refreshes browser when content changes diff --git a/scripts/logo_svg.py b/scripts/logo_svg.py new file mode 100755 index 000000000..7a69bdaba --- /dev/null +++ b/scripts/logo_svg.py @@ -0,0 +1,174 @@ +#!/usr/bin/env python3 +""" +Script to generate an SVG logo for Aider with embedded font. +Reads the Glass_TTY_VT220.ttf font, subsets it to only include the letters needed, +and creates an SVG with the word "aider" in terminal green (#14b014) on a transparent background. +""" + +import argparse +import base64 +import os +import tempfile + +from fontTools.subset import main as subset_main + + +def subset_font(font_path, text): + """ + Create a subset of the font containing only the characters needed for the text. + + Args: + font_path (str): Path to the TTF font file + text (str): Text for which to extract characters + + Returns: + bytes: The subsetted font data + """ + # Create a temporary file to store the subset font + with tempfile.NamedTemporaryFile(suffix=".ttf", delete=False) as tmp_file: + tmp_path = tmp_file.name + + # Get unique characters from the text + unique_chars = set(text.lower() + text.upper()) + + # Create the subsetting command + subset_args = [ + font_path, + "--output-file=" + tmp_path, + "--unicodes=" + ",".join([f"U+{ord(c):04X}" for c in unique_chars]), + "--name-IDs=*", # Keep all name records + "--recalc-bounds", + "--drop-tables=", # Don't drop any tables by default + ] + + # Run the subsetting + subset_main(subset_args) + + # Read the subsetted font + with open(tmp_path, "rb") as f: + font_data = f.read() + + # Clean up the temporary file + os.unlink(tmp_path) + + return font_data + + +def generate_svg_with_embedded_font(font_path, text="aider", color="#14b014", output_path=None): + """ + Generate an SVG with embedded TTF font data. + + Args: + font_path (str): Path to the TTF font file + text (str): Text to display in the SVG + color (str): Color of the text (hex format) + output_path (str, optional): Path to save the SVG file, if None prints to stdout + + Returns: + str: The SVG content + """ + # Subset the font to only include the needed characters + font_data = subset_font(font_path, text) + + # Encode the font data as base64 + font_base64 = base64.b64encode(font_data).decode("utf-8") + + # Calculate SVG dimensions based on text length + # These values can be adjusted to modify the appearance + char_width = 40 + width = len(text) * char_width + height = 60 + text_x = width / 2 # Center point of the SVG width + text_y = height * 0.62 # Center point of the SVG height + + # Create the SVG with embedded font and glow effect + svg = f""" + + + + + + + + + + {text} +""" # noqa + + # Save to file or print to stdout + if output_path: + with open(output_path, "w") as f: + f.write(svg) + print(f"SVG logo saved to {output_path}") + + return svg + + +def main(): + parser = argparse.ArgumentParser(description="Generate an SVG logo with embedded font") + parser.add_argument( + "--font", + type=str, + default="aider/website/assets/Glass_TTY_VT220.ttf", + help="Path to the TTF font file", + ) + parser.add_argument("--text", type=str, default="aider", help="Text to display in the SVG") + parser.add_argument( + "--color", type=str, default="#14b014", help="Color of the text (hex format)" + ) + parser.add_argument( + "--output", + type=str, + default="aider/website/assets/logo.svg", + help="Path to save the SVG file", + ) + parser.add_argument( + "--verbose", action="store_true", help="Print additional information about font subsetting" + ) + + args = parser.parse_args() + + # Make sure the font file exists + if not os.path.exists(args.font): + print(f"Error: Font file not found at {args.font}") + return + + # Create output directory if it doesn't exist + if args.output: + output_dir = os.path.dirname(args.output) + if output_dir and not os.path.exists(output_dir): + os.makedirs(output_dir) + + # Generate the SVG + if args.verbose: + print(f"Subsetting font {args.font} to include only characters for: {args.text}") + + svg = generate_svg_with_embedded_font( + args.font, text=args.text, color=args.color, output_path=args.output + ) + + if args.verbose and args.output: + # Calculate size savings + original_size = os.path.getsize(args.font) + output_size = len(svg.encode("utf-8")) + print(f"Original font size: {original_size / 1024:.2f} KB") + print(f"Output SVG size: {output_size / 1024:.2f} KB") + + +if __name__ == "__main__": + main() diff --git a/scripts/pip-compile.sh b/scripts/pip-compile.sh index 822cb819f..e1e1e512b 100755 --- a/scripts/pip-compile.sh +++ b/scripts/pip-compile.sh @@ -3,25 +3,41 @@ # exit when any command fails set -e -# First compile the base requirements -pip-compile \ - --allow-unsafe \ +# Add verbosity flag to see more details about dependency resolution +VERBOSITY="-v" # Use -v for less detail, -vvv for even more detail + +# First compile the common constraints of the full requirement suite +# to make sure that all versions are mutually consistent across files +uv pip compile \ + $VERBOSITY \ + --no-strip-extras \ + --output-file=requirements/common-constraints.txt \ requirements/requirements.in \ - --output-file=requirements.txt \ + requirements/requirements-*.in \ $1 -# Then compile each additional requirements file in sequence +# Compile the base requirements +uv pip compile \ + $VERBOSITY \ + --no-strip-extras \ + --constraint=requirements/common-constraints.txt \ + --output-file=tmp.requirements.txt \ + requirements/requirements.in \ + $1 + +grep -v ^tree-sitter= tmp.requirements.txt \ + | cat - requirements/tree-sitter.in \ + > requirements.txt + +# Compile additional requirements files SUFFIXES=(dev help browser playwright) -CONSTRAINTS="--constraint=requirements.txt" for SUFFIX in "${SUFFIXES[@]}"; do - pip-compile \ - --allow-unsafe \ - requirements/requirements-${SUFFIX}.in \ + uv pip compile \ + $VERBOSITY \ + --no-strip-extras \ + --constraint=requirements/common-constraints.txt \ --output-file=requirements/requirements-${SUFFIX}.txt \ - ${CONSTRAINTS} \ + requirements/requirements-${SUFFIX}.in \ $1 - - # Add this file as a constraint for the next iteration - CONSTRAINTS+=" --constraint=requirements/requirements-${SUFFIX}.txt" done diff --git a/scripts/recording_audio.py b/scripts/recording_audio.py new file mode 100755 index 000000000..7506d4c89 --- /dev/null +++ b/scripts/recording_audio.py @@ -0,0 +1,338 @@ +#!/usr/bin/env python3 +""" +Generate TTS audio files for recording commentary using OpenAI's API. +Usage: python scripts/recording_audio.py path/to/recording.md +""" + +import argparse +import json +import os +import re +import subprocess +import tempfile +from pathlib import Path + +import requests +from dotenv import load_dotenv + +# Load environment variables from .env file +load_dotenv() + +# Configuration +OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY") +OUTPUT_DIR = "aider/website/assets/audio" +VOICE = "onyx" # Options: alloy, echo, fable, onyx, nova, shimmer +MP3_BITRATE = "32k" # Lower bitrate for smaller files + + +def extract_recording_id(markdown_file): + """Extract recording ID from the markdown file path.""" + return Path(markdown_file).stem + + +def extract_commentary(markdown_file): + """Extract commentary markers from markdown file.""" + with open(markdown_file, "r") as f: + content = f.read() + + # Find Commentary section + commentary_match = re.search(r"## Commentary\s+(.*?)(?=##|\Z)", content, re.DOTALL) + if not commentary_match: + print(f"No Commentary section found in {markdown_file}") + return [] + + commentary = commentary_match.group(1).strip() + + # Extract timestamp-message pairs + markers = [] + for line in commentary.split("\n"): + line = line.strip() + if line.startswith("- "): + line = line[2:] # Remove the list marker + match = re.match(r"(\d+):(\d+)\s+(.*)", line) + if match: + minutes, seconds, message = match.groups() + time_in_seconds = int(minutes) * 60 + int(seconds) + markers.append((time_in_seconds, message)) + + return markers + + +def check_ffmpeg(): + """Check if FFmpeg is available.""" + try: + subprocess.run(["ffmpeg", "-version"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + return True + except (subprocess.SubprocessError, FileNotFoundError): + return False + + +def compress_audio(input_file, output_file, bitrate=MP3_BITRATE): + """Compress audio file using FFmpeg.""" + if not check_ffmpeg(): + print("Warning: FFmpeg not found, skipping compression") + return False + + try: + subprocess.run( + [ + "ffmpeg", + "-i", + input_file, + "-b:a", + bitrate, + "-ac", + "1", # Mono audio + "-y", # Overwrite output file + output_file, + ], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) + return True + except subprocess.SubprocessError as e: + print(f"Error compressing audio: {e}") + return False + + +def generate_audio_openai(text, output_file, voice=VOICE, bitrate=MP3_BITRATE): + """Generate audio using OpenAI TTS API and compress it.""" + if not OPENAI_API_KEY: + print("Error: OPENAI_API_KEY environment variable not set") + return False + + url = "https://api.openai.com/v1/audio/speech" + headers = {"Authorization": f"Bearer {OPENAI_API_KEY}", "Content-Type": "application/json"} + data = {"model": "tts-1", "input": text, "voice": voice} + + try: + response = requests.post(url, headers=headers, json=data) + + if response.status_code == 200: + # Use a temporary file for the initial audio + with tempfile.NamedTemporaryFile(suffix=".mp3", delete=False) as temp_file: + temp_path = temp_file.name + temp_file.write(response.content) + + # Get original file size + original_size = os.path.getsize(temp_path) + + # Compress the audio to reduce file size + success = compress_audio(temp_path, output_file, bitrate) + + # If compression failed or FFmpeg not available, use the original file + if not success: + with open(output_file, "wb") as f: + f.write(response.content) + print(f" ℹ Using original file: {original_size} bytes") + else: + compressed_size = os.path.getsize(output_file) + reduction = (1 - compressed_size / original_size) * 100 + print( + f" ℹ Compressed: {original_size} → {compressed_size} bytes ({reduction:.1f}%" + " reduction)" + ) + + # Clean up the temporary file + try: + os.unlink(temp_path) + except OSError: + pass + + return True + else: + print(f"Error: {response.status_code}, {response.text}") + return False + except Exception as e: + print(f"Exception during API call: {e}") + return False + + +def load_metadata(output_dir): + """Load the audio metadata JSON file if it exists.""" + metadata_file = os.path.join(output_dir, "metadata.json") + + if os.path.exists(metadata_file): + try: + with open(metadata_file, "r") as f: + return json.load(f) + except json.JSONDecodeError: + print(f"Warning: Could not parse metadata file {metadata_file}, will recreate it") + + return {} + + +def save_metadata(output_dir, metadata): + """Save the audio metadata to JSON file.""" + metadata_file = os.path.join(output_dir, "metadata.json") + + with open(metadata_file, "w") as f: + json.dump(metadata, f, indent=2) + + +def get_timestamp_key(time_sec): + """Generate a consistent timestamp key format for metadata.""" + minutes = time_sec // 60 + seconds = time_sec % 60 + return f"{minutes:02d}-{seconds:02d}" + + +def main(): + parser = argparse.ArgumentParser(description="Generate TTS audio for recording commentary.") + parser.add_argument("markdown_file", help="Path to the recording markdown file") + parser.add_argument("--voice", default=VOICE, help=f"OpenAI voice to use (default: {VOICE})") + parser.add_argument( + "--output-dir", default=OUTPUT_DIR, help=f"Output directory (default: {OUTPUT_DIR})" + ) + parser.add_argument( + "--dry-run", action="store_true", help="Print what would be done without generating audio" + ) + parser.add_argument( + "--force", action="store_true", help="Force regeneration of all audio files" + ) + parser.add_argument( + "--bitrate", + default=MP3_BITRATE, + help=f"MP3 bitrate for compression (default: {MP3_BITRATE})", + ) + parser.add_argument( + "--compress-only", + action="store_true", + help="Only compress existing files without generating new ones", + ) + + args = parser.parse_args() + + # Use args.voice directly instead of modifying global VOICE + selected_voice = args.voice + selected_bitrate = args.bitrate + + # Check if FFmpeg is available for compression + if not check_ffmpeg() and not args.dry_run: + print("Warning: FFmpeg not found. Audio compression will be skipped.") + print("To enable compression, please install FFmpeg: https://ffmpeg.org/download.html") + + recording_id = extract_recording_id(args.markdown_file) + print(f"Processing recording: {recording_id}") + + # Create output directory + output_dir = os.path.join(args.output_dir, recording_id) + print(f"Audio directory: {output_dir}") + if not args.dry_run: + os.makedirs(output_dir, exist_ok=True) + + # If compress-only flag is set, just compress existing files + if args.compress_only: + print("Compressing existing files only...") + metadata = load_metadata(output_dir) + for timestamp_key in metadata: + filename = f"{timestamp_key}.mp3" + file_path = os.path.join(output_dir, filename) + + if os.path.exists(file_path): + temp_file = f"{file_path}.temp" + print(f"Compressing: {filename}") + + if not args.dry_run: + success = compress_audio(file_path, temp_file, selected_bitrate) + if success: + # Get file sizes for reporting + original_size = os.path.getsize(file_path) + compressed_size = os.path.getsize(temp_file) + reduction = (1 - compressed_size / original_size) * 100 + + # Replace original with compressed version + os.replace(temp_file, file_path) + print( + f" ✓ Compressed: {original_size} → {compressed_size} bytes" + f" ({reduction:.1f}% reduction)" + ) + else: + print(" ✗ Failed to compress") + if os.path.exists(temp_file): + os.remove(temp_file) + else: + print(f" Would compress: {file_path}") + + return + + # Extract commentary markers + markers = extract_commentary(args.markdown_file) + + if not markers: + print("No commentary markers found!") + return + + print(f"Found {len(markers)} commentary markers") + + # Load existing metadata + metadata = load_metadata(output_dir) + + # Create a dictionary of current markers for easier comparison + current_markers = {} + for time_sec, message in markers: + timestamp_key = get_timestamp_key(time_sec) + current_markers[timestamp_key] = message + + # Track files that need to be deleted (no longer in the markdown) + files_to_delete = [] + for timestamp_key in metadata: + if timestamp_key not in current_markers: + files_to_delete.append(f"{timestamp_key}.mp3") + + # Delete files that are no longer needed + if files_to_delete and not args.dry_run: + for filename in files_to_delete: + file_path = os.path.join(output_dir, filename) + if os.path.exists(file_path): + print(f"Removing obsolete file: {filename}") + os.remove(file_path) + elif files_to_delete: + print(f"Would remove {len(files_to_delete)} obsolete files: {', '.join(files_to_delete)}") + + # Generate audio for each marker + for time_sec, message in markers: + timestamp_key = get_timestamp_key(time_sec) + filename = f"{timestamp_key}.mp3" + output_file = os.path.join(output_dir, filename) + + # Check if we need to generate this file + needs_update = args.force or ( + timestamp_key not in metadata or metadata[timestamp_key] != message + ) + + minutes = time_sec // 60 + seconds = time_sec % 60 + + print(f"Marker at {minutes}:{seconds:02d} - {message}") + + if not needs_update: + print(" ✓ Audio file already exists with correct content") + continue + + if args.dry_run: + print(f" Would generate: {output_file}") + else: + print(f" Generating: {output_file}") + success = generate_audio_openai( + message, output_file, voice=selected_voice, bitrate=selected_bitrate + ) + if success: + print(" ✓ Generated audio file") + # Update metadata with the new message + metadata[timestamp_key] = message + else: + print(" ✗ Failed to generate audio") + + # Save updated metadata + if not args.dry_run: + # Remove entries for deleted files + for timestamp_key in list(metadata.keys()): + if timestamp_key not in current_markers: + del metadata[timestamp_key] + + save_metadata(output_dir, metadata) + + +if __name__ == "__main__": + main() diff --git a/scripts/redact-cast.py b/scripts/redact-cast.py new file mode 100755 index 000000000..91f230e87 --- /dev/null +++ b/scripts/redact-cast.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python3 +import json +import os +import re +import sys + +import pyte +from tqdm import tqdm + +from aider.dump import dump # noqa + + +def main(): + if len(sys.argv) != 3: + print(f"Usage: {sys.argv[0]} input_cast_file output_cast_file") + sys.exit(1) + + input_file = sys.argv[1] + output_file = sys.argv[2] + + # Count total lines for progress bar + total_lines = sum(1 for _ in open(input_file, "r")) + + with open(input_file, "r") as fin, open(output_file, "w") as fout: + # Process header + header = fin.readline().strip() + fout.write(header + "\n") + + # Parse header for terminal dimensions + header_data = json.loads(header) + width = header_data.get("width", 80) + height = header_data.get("height", 24) + print(f"Terminal dimensions: {width}x{height}") + + screen = pyte.Screen(width, height) + stream = pyte.Stream(screen) + + # Process events line by line + for line in tqdm(fin, desc="Processing events", total=total_lines - 1): + if not line.strip(): + continue + + event = json.loads(line) + + if not (len(event) >= 3 and event[1] == "o"): + fout.write(line) + continue + + output_text = event[2] + stream.feed(output_text) + + # Check if "Atuin" is visible on screen + atuin_visible = False + for display_line in screen.display: + if "Atuin" in display_line or "[ GLOBAL ]" in display_line: + atuin_visible = True + break + + if not atuin_visible: + fout.write(line) + + +if __name__ == "__main__": + main() diff --git a/scripts/tmux_record.sh b/scripts/tmux_record.sh new file mode 100755 index 000000000..de40cca35 --- /dev/null +++ b/scripts/tmux_record.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +# Check if there is exactly one active window +WINDOW_COUNT=$(tmux list-windows | wc -l) +if [ "$WINDOW_COUNT" -ne 1 ]; then + echo "Error: Expected exactly 1 tmux window, found $WINDOW_COUNT windows." + exit 1 +fi + +# Get tmux window size (width x height) +TMUX_SIZE=$(tmux display -p '#{window_width}x#{window_height}') + +# Print the terminal size +echo "Using terminal size: $TMUX_SIZE" + +# Start asciinema recording with the tmux window size +asciinema rec -c "tmux attach -t 0 -r" --headless --tty-size $TMUX_SIZE $* + diff --git a/scripts/tsl_pack_langs.py b/scripts/tsl_pack_langs.py new file mode 100755 index 000000000..cc56ae6bd --- /dev/null +++ b/scripts/tsl_pack_langs.py @@ -0,0 +1,145 @@ +#!/usr/bin/env python3 + +import json +import os +import sys +import time + +import requests + + +def get_default_branch(owner, repo): + """Get the default branch of a GitHub repository using the API.""" + api_url = f"https://api.github.com/repos/{owner}/{repo}" + try: + response = requests.get(api_url) + response.raise_for_status() + return response.json().get("default_branch") + except requests.exceptions.RequestException: + return None + + +def try_download_tags(owner, repo, branch, directory, output_path): + """Try to download tags.scm from a specific branch.""" + base_url = f"https://raw.githubusercontent.com/{owner}/{repo}/{branch}" + if directory: + tags_url = f"{base_url}/{directory}/queries/tags.scm" + else: + tags_url = f"{base_url}/queries/tags.scm" + + try: + response = requests.get(tags_url) + response.raise_for_status() + + # Save the file + with open(output_path, "w") as f: + f.write(response.text) + return True + except requests.exceptions.RequestException: + return False + + +def main(): + # Path to the language definitions file + lang_def_path = "../../tmp/tree-sitter-language-pack/sources/language_definitions.json" + + # Path to store the tags.scm files + output_dir = "aider/queries/tree-sitter-language-pack" + + # Create the output directory if it doesn't exist + os.makedirs(output_dir, exist_ok=True) + + # Common branch names to try if API fails and config branch doesn't work + common_branches = ["main", "master", "dev", "develop"] + + try: + # Load the language definitions + with open(lang_def_path, "r") as f: + lang_defs = json.load(f) + except Exception as e: + print(f"Error loading language definitions: {e}") + sys.exit(1) + + print(f"Found {len(lang_defs)} language definitions") + + # Process each language + successes = 0 + total = len(lang_defs) + + for lang, config in lang_defs.items(): + # Extract repo URL from the config + repo_url = config.get("repo") + print(f"Processing {lang} ({repo_url})...") + + if not repo_url: + print(f"Skipping {lang}: No repository URL found") + continue + + directory = config.get("directory", "") + + # Parse the GitHub repository URL + if "github.com" not in repo_url: + print(f"Skipping {lang}: Not a GitHub repository") + continue + + # Extract the owner and repo name from the URL + parts = repo_url.rstrip("/").split("/") + if len(parts) < 5: + print(f"Skipping {lang}: Invalid GitHub URL format") + continue + + owner = parts[-2] + repo = parts[-1] + + # Create output directory and set output file path + os.makedirs(output_dir, exist_ok=True) + output_file = os.path.join(output_dir, f"{lang}-tags.scm") + + # Skip if file already exists + if os.path.exists(output_file): + print(f"Skipping {lang}: tags.scm already exists") + successes += 1 + continue + + # Try branches in this order: + # 1. Branch specified in the config + # 2. Default branch from GitHub API + # 3. Common branch names (main, master, etc.) + + branches_to_try = [] + + # 1. Branch from config (if specified) + config_branch = config.get("branch") + if config_branch: + branches_to_try.append(config_branch) + + # 2. Default branch from GitHub API + default_branch = get_default_branch(owner, repo) + if default_branch and default_branch not in branches_to_try: + branches_to_try.append(default_branch) + + # 3. Add common branch names + for branch in common_branches: + if branch not in branches_to_try: + branches_to_try.append(branch) + + # Try each branch + success = False + for branch in branches_to_try: + if try_download_tags(owner, repo, branch, directory, output_file): + print(f"Successfully downloaded tags for {lang} (branch: {branch})") + success = True + successes += 1 + break + + if not success: + print(f"Failed to download tags for {lang} after trying all branches") + + # Be nice to GitHub's API + time.sleep(0.1) + + print(f"All language tags processed. Downloaded {successes}/{total} successfully.") + + +if __name__ == "__main__": + main() diff --git a/scripts/update-docs.sh b/scripts/update-docs.sh index ecde8ac01..7807c795e 100755 --- a/scripts/update-docs.sh +++ b/scripts/update-docs.sh @@ -17,7 +17,7 @@ fi # README.md before index.md, because index.md uses cog to include README.md cog $ARG \ README.md \ - aider/website/index.md \ + aider/website/index.html \ aider/website/HISTORY.md \ aider/website/docs/usage/commands.md \ aider/website/docs/languages.md \ diff --git a/scripts/update-history.py b/scripts/update-history.py index 968210a63..e8fa455b2 100755 --- a/scripts/update-history.py +++ b/scripts/update-history.py @@ -7,25 +7,57 @@ import tempfile from history_prompts import history_prompt -from aider import __version__ +def get_latest_version_from_history(): + with open("HISTORY.md", "r") as f: + history_content = f.read() -def get_base_version(): - # Parse current version like "0.64.2.dev" to get major.minor - match = re.match(r"(\d+\.\d+)", __version__) + # Find most recent version header + match = re.search(r"### Aider v(\d+\.\d+\.\d+)", history_content) if not match: - raise ValueError(f"Could not parse version: {__version__}") - return match.group(1) + ".0" + raise ValueError("Could not find version header in HISTORY.md") + return match.group(1) def run_git_log(): - base_ver = get_base_version() + latest_ver = get_latest_version_from_history() cmd = [ "git", "log", - "-p", "--pretty=full", - f"v{base_ver}..HEAD", + f"v{latest_ver}..HEAD", + "--", + "aider/", + ":!aider/website/", + ":!scripts/", + ":!HISTORY.md", + ] + result = subprocess.run(cmd, capture_output=True, text=True) + return result.stdout + + +def run_git_diff(): + latest_ver = get_latest_version_from_history() + cmd = [ + "git", + "diff", + f"v{latest_ver}..HEAD", + "--", + "aider/", + ":!aider/website/", + ":!scripts/", + ":!HISTORY.md", + ] + result = subprocess.run(cmd, capture_output=True, text=True) + return result.stdout + + +def run_plain_git_log(): + latest_ver = get_latest_version_from_history() + cmd = [ + "git", + "log", + f"v{latest_ver}..HEAD", "--", "aider/", ":!aider/website/", @@ -37,16 +69,18 @@ def run_git_log(): def main(): - # Get the git log output - diff_content = run_git_log() + # Get the git log and diff output + log_content = run_git_log() + plain_log_content = run_plain_git_log() + diff_content = run_git_diff() # Extract relevant portion of HISTORY.md - base_ver = get_base_version() + latest_ver = get_latest_version_from_history() with open("HISTORY.md", "r") as f: history_content = f.read() # Find the section for this version - version_header = f"### Aider v{base_ver}" + version_header = f"### Aider v{latest_ver}" start_idx = history_content.find("# Release history") if start_idx == -1: raise ValueError("Could not find start of release history") @@ -66,10 +100,18 @@ def main(): relevant_history = history_content[start_idx:next_version_idx] # Save relevant portions to temporary files + with tempfile.NamedTemporaryFile(mode="w", delete=False, suffix=".log") as tmp_log: + tmp_log.write(log_content) + log_path = tmp_log.name + with tempfile.NamedTemporaryFile(mode="w", delete=False, suffix=".diff") as tmp_diff: tmp_diff.write(diff_content) diff_path = tmp_diff.name + with tempfile.NamedTemporaryFile(mode="w", delete=False, suffix=".plain_log") as tmp_plain_log: + tmp_plain_log.write(plain_log_content) + plain_log_path = tmp_plain_log.name + with tempfile.NamedTemporaryFile(mode="w", delete=False, suffix=".md") as tmp_hist: tmp_hist.write(relevant_history) hist_path = tmp_hist.name @@ -81,7 +123,20 @@ def main(): # Construct and run the aider command message = history_prompt.format(aider_line=aider_line) - cmd = ["aider", hist_path, "--read", diff_path, "--msg", message, "--no-auto-commit"] + cmd = [ + "aider", + hist_path, + "--read", + log_path, + "--read", + plain_log_path, + "--read", + diff_path, + "--msg", + message, + "--no-git", + "--no-auto-lint", + ] subprocess.run(cmd) # Read back the updated history @@ -108,6 +163,8 @@ def main(): subprocess.run(["scripts/update-docs.sh"]) # Cleanup + os.unlink(log_path) + os.unlink(plain_log_path) os.unlink(diff_path) os.unlink(hist_path) diff --git a/scripts/versionbump.py b/scripts/versionbump.py index 859cab1d7..6835f413d 100755 --- a/scripts/versionbump.py +++ b/scripts/versionbump.py @@ -10,72 +10,91 @@ import sys from packaging import version +# Function to check if we are on the main branch +def check_branch(): + branch = subprocess.run( + ["git", "rev-parse", "--abbrev-ref", "HEAD"], capture_output=True, text=True + ).stdout.strip() + if branch != "main": + print("Error: Not on the main branch.") + sys.exit(1) + + +# Function to check if the working directory is clean +def check_working_directory_clean(): + status = subprocess.run(["git", "status", "--porcelain"], capture_output=True, text=True).stdout + if status: + print("Error: Working directory is not clean.") + sys.exit(1) + + +# Function to fetch the latest changes and check if the main branch is up to date +def check_main_branch_up_to_date(): + subprocess.run(["git", "fetch", "origin"], check=True) + local_main = subprocess.run( + ["git", "rev-parse", "main"], capture_output=True, text=True + ).stdout.strip() + print(f"Local main commit hash: {local_main}") + origin_main = subprocess.run( + ["git", "rev-parse", "origin/main"], capture_output=True, text=True + ).stdout.strip() + print(f"Origin main commit hash: {origin_main}") + if local_main != origin_main: + local_date = subprocess.run( + ["git", "show", "-s", "--format=%ci", "main"], capture_output=True, text=True + ).stdout.strip() + origin_date = subprocess.run( + ["git", "show", "-s", "--format=%ci", "origin/main"], capture_output=True, text=True + ).stdout.strip() + local_date = datetime.datetime.strptime(local_date, "%Y-%m-%d %H:%M:%S %z") + origin_date = datetime.datetime.strptime(origin_date, "%Y-%m-%d %H:%M:%S %z") + if local_date < origin_date: + print( + "Error: The local main branch is behind origin/main. Please pull the latest" + " changes." + ) + elif local_date > origin_date: + print( + "Error: The origin/main branch is behind the local main branch. Please push" + " your changes." + ) + else: + print("Error: The main branch and origin/main have diverged.") + sys.exit(1) + + +# Function to check if we can push to the origin repository +def check_ok_to_push(): + print("Checking if it's ok to push to origin repository...") + result = subprocess.run(["git", "push", "--dry-run", "origin"]) + + if result.returncode != 0: + print("Error: Cannot push to origin repository.") + sys.exit(1) + + print("Push to origin repository is possible.") + + def main(): parser = argparse.ArgumentParser(description="Bump version") parser.add_argument("new_version", help="New version in x.y.z format") parser.add_argument( "--dry-run", action="store_true", help="Print each step without actually executing them" ) - - # Function to check if we are on the main branch - def check_branch(): - branch = subprocess.run( - ["git", "rev-parse", "--abbrev-ref", "HEAD"], capture_output=True, text=True - ).stdout.strip() - if branch != "main": - print("Error: Not on the main branch.") - sys.exit(1) - - # Function to check if the working directory is clean - def check_working_directory_clean(): - status = subprocess.run( - ["git", "status", "--porcelain"], capture_output=True, text=True - ).stdout - if status: - print("Error: Working directory is not clean.") - sys.exit(1) - - # Function to fetch the latest changes and check if the main branch is up to date - def check_main_branch_up_to_date(): - subprocess.run(["git", "fetch", "origin"], check=True) - local_main = subprocess.run( - ["git", "rev-parse", "main"], capture_output=True, text=True - ).stdout.strip() - print(f"Local main commit hash: {local_main}") - origin_main = subprocess.run( - ["git", "rev-parse", "origin/main"], capture_output=True, text=True - ).stdout.strip() - print(f"Origin main commit hash: {origin_main}") - if local_main != origin_main: - local_date = subprocess.run( - ["git", "show", "-s", "--format=%ci", "main"], capture_output=True, text=True - ).stdout.strip() - origin_date = subprocess.run( - ["git", "show", "-s", "--format=%ci", "origin/main"], capture_output=True, text=True - ).stdout.strip() - local_date = datetime.datetime.strptime(local_date, "%Y-%m-%d %H:%M:%S %z") - origin_date = datetime.datetime.strptime(origin_date, "%Y-%m-%d %H:%M:%S %z") - if local_date < origin_date: - print( - "Error: The local main branch is behind origin/main. Please pull the latest" - " changes." - ) - elif local_date > origin_date: - print( - "Error: The origin/main branch is behind the local main branch. Please push" - " your changes." - ) - else: - print("Error: The main branch and origin/main have diverged.") - sys.exit(1) + parser.add_argument("--force", action="store_true", help="Skip pre-push checks") args = parser.parse_args() dry_run = args.dry_run + force = args.force - # Perform checks before proceeding - check_branch() - check_working_directory_clean() - check_main_branch_up_to_date() + # Perform checks before proceeding unless --force is used + if not force: + check_branch() + check_working_directory_clean() + check_main_branch_up_to_date() + check_ok_to_push() + else: + print("Skipping pre-push checks due to --force flag.") new_version_str = args.new_version if not re.match(r"^\d+\.\d+\.\d+$", new_version_str): @@ -107,7 +126,7 @@ def main(): ["git", "add", "aider/__init__.py"], ["git", "commit", "-m", f"version bump to {new_version}"], ["git", "tag", f"v{new_version}"], - ["git", "push", "origin"], + ["git", "push", "origin", "--no-verify"], ["git", "push", "origin", f"v{new_version}", "--no-verify"], ] diff --git a/tests/basic/__init__.py b/tests/basic/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/basic/test_aws_credentials.py b/tests/basic/test_aws_credentials.py new file mode 100644 index 000000000..87d2b3a63 --- /dev/null +++ b/tests/basic/test_aws_credentials.py @@ -0,0 +1,169 @@ +import os +from unittest.mock import patch + +from aider.models import Model + + +class TestAWSCredentials: + """Test AWS credential handling, especially AWS_PROFILE.""" + + def test_bedrock_model_with_aws_profile(self): + """Test that Bedrock models accept AWS_PROFILE as valid authentication.""" + # Save original environment + original_env = os.environ.copy() + + try: + # Set up test environment + os.environ.clear() + os.environ["AWS_PROFILE"] = "test-profile" + + # Create a model instance + with patch("aider.llm.litellm.validate_environment") as mock_validate: + # Mock the litellm validate_environment to return missing AWS keys + mock_validate.return_value = { + "missing_keys": ["AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY"], + "keys_in_environment": False, + } + + # Test with a bedrock model + model = Model("bedrock/anthropic.claude-v2") + + # Check that the AWS keys were removed from missing_keys + assert "AWS_ACCESS_KEY_ID" not in model.missing_keys + assert "AWS_SECRET_ACCESS_KEY" not in model.missing_keys + # With no missing keys, validation should pass + assert model.keys_in_environment + + finally: + # Restore original environment + os.environ.clear() + os.environ.update(original_env) + + def test_us_anthropic_model_with_aws_profile(self): + """Test that us.anthropic models accept AWS_PROFILE as valid authentication.""" + # Save original environment + original_env = os.environ.copy() + + try: + # Set up test environment + os.environ.clear() + os.environ["AWS_PROFILE"] = "test-profile" + + # Create a model instance + with patch("aider.llm.litellm.validate_environment") as mock_validate: + # Mock the litellm validate_environment to return missing AWS keys + mock_validate.return_value = { + "missing_keys": ["AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY"], + "keys_in_environment": False, + } + + # Test with a us.anthropic model + model = Model("us.anthropic.claude-3-7-sonnet-20250219-v1:0") + + # Check that the AWS keys were removed from missing_keys + assert "AWS_ACCESS_KEY_ID" not in model.missing_keys + assert "AWS_SECRET_ACCESS_KEY" not in model.missing_keys + # With no missing keys, validation should pass + assert model.keys_in_environment + + finally: + # Restore original environment + os.environ.clear() + os.environ.update(original_env) + + def test_non_bedrock_model_with_aws_profile(self): + """Test that non-Bedrock models do not accept AWS_PROFILE for AWS credentials.""" + # Save original environment + original_env = os.environ.copy() + + try: + # Set up test environment + os.environ.clear() + os.environ["AWS_PROFILE"] = "test-profile" + + # Create a model instance + with patch("aider.llm.litellm.validate_environment") as mock_validate: + # Mock the litellm validate_environment to return missing AWS keys + mock_validate.return_value = { + "missing_keys": ["AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY"], + "keys_in_environment": False, + } + + # Test with a non-Bedrock model + model = Model("gpt-4") + + # For non-Bedrock models, AWS credential keys should remain in missing_keys + assert "AWS_ACCESS_KEY_ID" in model.missing_keys + assert "AWS_SECRET_ACCESS_KEY" in model.missing_keys + # With missing keys, validation should fail + assert not model.keys_in_environment + + finally: + # Restore original environment + os.environ.clear() + os.environ.update(original_env) + + def test_bedrock_model_without_aws_profile(self): + """Test that Bedrock models require credentials when AWS_PROFILE is not set.""" + # Save original environment + original_env = os.environ.copy() + + try: + # Set up test environment + os.environ.clear() + + # Create a model instance + with patch("aider.llm.litellm.validate_environment") as mock_validate: + # Mock the litellm validate_environment to return missing AWS keys + mock_validate.return_value = { + "missing_keys": ["AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY"], + "keys_in_environment": False, + } + + # Test with a bedrock model without AWS_PROFILE + model = Model("bedrock/anthropic.claude-v2") + + # Without AWS_PROFILE, AWS credential keys should remain in missing_keys + assert "AWS_ACCESS_KEY_ID" in model.missing_keys + assert "AWS_SECRET_ACCESS_KEY" in model.missing_keys + # With missing keys, validation should fail + assert not model.keys_in_environment + + finally: + # Restore original environment + os.environ.clear() + os.environ.update(original_env) + + def test_mixed_missing_keys_with_aws_profile(self): + """Test that only AWS credential keys are affected by AWS_PROFILE.""" + # Save original environment + original_env = os.environ.copy() + + try: + # Set up test environment + os.environ.clear() + os.environ["AWS_PROFILE"] = "test-profile" + + # Create a model instance + with patch("aider.llm.litellm.validate_environment") as mock_validate: + # Mock the litellm validate_environment to return missing AWS keys and another key + mock_validate.return_value = { + "missing_keys": ["AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY", "ANOTHER_KEY"], + "keys_in_environment": False, + } + + # Test with a bedrock model + model = Model("bedrock/anthropic.claude-v2") + + # AWS credential keys should be removed from missing_keys + assert "AWS_ACCESS_KEY_ID" not in model.missing_keys + assert "AWS_SECRET_ACCESS_KEY" not in model.missing_keys + # But other keys should remain + assert "ANOTHER_KEY" in model.missing_keys + # With other missing keys, validation should still fail + assert not model.keys_in_environment + + finally: + # Restore original environment + os.environ.clear() + os.environ.update(original_env) diff --git a/tests/basic/test_coder.py b/tests/basic/test_coder.py index ba24e7081..c58ade1b2 100644 --- a/tests/basic/test_coder.py +++ b/tests/basic/test_coder.py @@ -37,7 +37,9 @@ class TestCoder(unittest.TestCase): repo.git.commit("-m", "init") # YES! - io = InputOutput(yes=True) + # Use a completely mocked IO object instead of a real one + io = MagicMock() + io.confirm_ask = MagicMock(return_value=True) coder = Coder.create(self.GPT35, None, io, fnames=["added.txt"]) self.assertTrue(coder.allowed_to_edit("added.txt")) @@ -192,8 +194,8 @@ class TestCoder(unittest.TestCase): mock.return_value = set([str(fname1), str(fname2), str(fname3)]) coder.repo.get_tracked_files = mock - # Check that file mentions skip files with duplicate basenames - mentioned = coder.get_file_mentions(f"Check {fname2} and {fname3}") + # Check that file mentions of a pure basename skips files with duplicate basenames + mentioned = coder.get_file_mentions(f"Check {fname2.name} and {fname3}") self.assertEqual(mentioned, {str(fname3)}) # Add a read-only file with same basename @@ -283,6 +285,126 @@ class TestCoder(unittest.TestCase): self.assertEqual(coder.abs_fnames, set([str(fname.resolve())])) + def test_get_file_mentions_various_formats(self): + with GitTemporaryDirectory(): + io = InputOutput(pretty=False, yes=True) + coder = Coder.create(self.GPT35, None, io) + + # Create test files + test_files = [ + "file1.txt", + "file2.py", + "dir/nested_file.js", + "dir/subdir/deep_file.html", + "file99.txt", + "special_chars!@#.md", + ] + + # Pre-format the Windows path to avoid backslash issues in f-string expressions + windows_path = test_files[2].replace("/", "\\") + win_path3 = test_files[3].replace("/", "\\") + + for fname in test_files: + fpath = Path(fname) + fpath.parent.mkdir(parents=True, exist_ok=True) + fpath.touch() + + # Mock get_addable_relative_files to return our test files + coder.get_addable_relative_files = MagicMock(return_value=set(test_files)) + + # Test different mention formats + test_cases = [ + # Simple plain text mentions + (f"You should edit {test_files[0]} first", {test_files[0]}), + # Multiple files in plain text + (f"Edit both {test_files[0]} and {test_files[1]}", {test_files[0], test_files[1]}), + # Files in backticks + (f"Check the file `{test_files[2]}`", {test_files[2]}), + # Files in code blocks + (f"```\n{test_files[3]}\n```", {test_files[3]}), + # Files in code blocks with language specifier + # ( + # f"```python\nwith open('{test_files[1]}', 'r') as f:\n" + # f" data = f.read()\n```", + # {test_files[1]}, + # ), + # Files with Windows-style paths + (f"Edit the file {windows_path}", {test_files[2]}), + # Files with different quote styles + (f'Check "{test_files[5]}" now', {test_files[5]}), + # All files in one complex message + ( + ( + f"First, edit `{test_files[0]}`. Then modify {test_files[1]}.\n" + f"```js\n// Update this file\nconst file = '{test_files[2]}';\n```\n" + f"Finally check {win_path3}" + ), + {test_files[0], test_files[1], test_files[2], test_files[3]}, + ), + # Files mentioned in markdown bold format + (f"You should check **{test_files[0]}** for issues", {test_files[0]}), + ( + f"Look at both **{test_files[1]}** and **{test_files[2]}**", + {test_files[1], test_files[2]}, + ), + ( + f"The file **{win_path3}** needs updating", + {test_files[3]}, + ), + ( + f"Files to modify:\n- **{test_files[0]}**\n- **{test_files[4]}**", + {test_files[0], test_files[4]}, + ), + ] + + for content, expected_mentions in test_cases: + with self.subTest(content=content): + mentioned_files = coder.get_file_mentions(content) + self.assertEqual( + mentioned_files, + expected_mentions, + f"Failed to extract mentions from: {content}", + ) + + def test_get_file_mentions_multiline_backticks(self): + with GitTemporaryDirectory(): + io = InputOutput(pretty=False, yes=True) + coder = Coder.create(self.GPT35, None, io) + + # Create test files + test_files = [ + "swebench/harness/test_spec/python.py", + "swebench/harness/test_spec/javascript.py", + ] + for fname in test_files: + fpath = Path(fname) + fpath.parent.mkdir(parents=True, exist_ok=True) + fpath.touch() + + # Mock get_addable_relative_files to return our test files + coder.get_addable_relative_files = MagicMock(return_value=set(test_files)) + + # Input text with multiline backticked filenames + content = """ +Could you please **add the following files to the chat**? + +1. `swebench/harness/test_spec/python.py` +2. `swebench/harness/test_spec/javascript.py` + +Once I have these, I can show you precisely how to do the thing. +""" + expected_mentions = { + "swebench/harness/test_spec/python.py", + "swebench/harness/test_spec/javascript.py", + } + + mentioned_files = coder.get_file_mentions(content) + self.assertEqual( + mentioned_files, + expected_mentions, + f"Failed to extract mentions from multiline backticked content: {content}", + ) + def test_get_file_mentions_path_formats(self): with GitTemporaryDirectory(): io = InputOutput(pretty=False, yes=True) @@ -1059,6 +1181,112 @@ This command will print 'Hello, World!' to the console.""" sanity_check_messages(coder.cur_messages) self.assertEqual(coder.cur_messages[-1]["role"], "assistant") + def test_architect_coder_auto_accept_true(self): + with GitTemporaryDirectory(): + io = InputOutput(yes=True) + io.confirm_ask = MagicMock(return_value=True) + + # Create an ArchitectCoder with auto_accept_architect=True + with patch("aider.coders.architect_coder.AskCoder.__init__", return_value=None): + from aider.coders.architect_coder import ArchitectCoder + + coder = ArchitectCoder() + coder.io = io + coder.main_model = self.GPT35 + coder.auto_accept_architect = True + coder.verbose = False + coder.total_cost = 0 + coder.cur_messages = [] + coder.done_messages = [] + coder.summarizer = MagicMock() + coder.summarizer.too_big.return_value = False + + # Mock editor_coder creation and execution + mock_editor = MagicMock() + with patch("aider.coders.architect_coder.Coder.create", return_value=mock_editor): + # Set partial response content + coder.partial_response_content = "Make these changes to the code" + + # Call reply_completed + coder.reply_completed() + + # Verify that confirm_ask was not called (auto-accepted) + io.confirm_ask.assert_not_called() + + # Verify that editor coder was created and run + mock_editor.run.assert_called_once() + + def test_architect_coder_auto_accept_false_confirmed(self): + with GitTemporaryDirectory(): + io = InputOutput(yes=False) + io.confirm_ask = MagicMock(return_value=True) + + # Create an ArchitectCoder with auto_accept_architect=False + with patch("aider.coders.architect_coder.AskCoder.__init__", return_value=None): + from aider.coders.architect_coder import ArchitectCoder + + coder = ArchitectCoder() + coder.io = io + coder.main_model = self.GPT35 + coder.auto_accept_architect = False + coder.verbose = False + coder.total_cost = 0 + coder.cur_messages = [] + coder.done_messages = [] + coder.summarizer = MagicMock() + coder.summarizer.too_big.return_value = False + coder.cur_messages = [] + coder.done_messages = [] + coder.summarizer = MagicMock() + coder.summarizer.too_big.return_value = False + + # Mock editor_coder creation and execution + mock_editor = MagicMock() + with patch("aider.coders.architect_coder.Coder.create", return_value=mock_editor): + # Set partial response content + coder.partial_response_content = "Make these changes to the code" + + # Call reply_completed + coder.reply_completed() + + # Verify that confirm_ask was called + io.confirm_ask.assert_called_once_with("Edit the files?") + + # Verify that editor coder was created and run + mock_editor.run.assert_called_once() + + def test_architect_coder_auto_accept_false_rejected(self): + with GitTemporaryDirectory(): + io = InputOutput(yes=False) + io.confirm_ask = MagicMock(return_value=False) + + # Create an ArchitectCoder with auto_accept_architect=False + with patch("aider.coders.architect_coder.AskCoder.__init__", return_value=None): + from aider.coders.architect_coder import ArchitectCoder + + coder = ArchitectCoder() + coder.io = io + coder.main_model = self.GPT35 + coder.auto_accept_architect = False + coder.verbose = False + coder.total_cost = 0 + + # Mock editor_coder creation and execution + mock_editor = MagicMock() + with patch("aider.coders.architect_coder.Coder.create", return_value=mock_editor): + # Set partial response content + coder.partial_response_content = "Make these changes to the code" + + # Call reply_completed + coder.reply_completed() + + # Verify that confirm_ask was called + io.confirm_ask.assert_called_once_with("Edit the files?") + + # Verify that editor coder was NOT created or run + # (because user rejected the changes) + mock_editor.run.assert_not_called() + if __name__ == "__main__": unittest.main() diff --git a/tests/basic/test_commands.py b/tests/basic/test_commands.py index a234c9b1d..7ded4ca3a 100644 --- a/tests/basic/test_commands.py +++ b/tests/basic/test_commands.py @@ -1124,6 +1124,29 @@ class TestCommands(TestCase): # Check that the output was added to cur_messages self.assertTrue(any("exit 1" in msg["content"] for msg in coder.cur_messages)) + def test_cmd_test_returns_output_on_failure(self): + with ChdirTemporaryDirectory(): + io = InputOutput(pretty=False, fancy_input=False, yes=False) + from aider.coders import Coder + + coder = Coder.create(self.GPT35, None, io) + commands = Commands(io, coder) + + # Define a command that prints to stderr and exits with non-zero status + test_cmd = "echo 'error output' >&2 && exit 1" + expected_output_fragment = "error output" + + # Run cmd_test + result = commands.cmd_test(test_cmd) + + # Assert that the result contains the expected output + self.assertIsNotNone(result) + self.assertIn(expected_output_fragment, result) + # Check that the output was also added to cur_messages + self.assertTrue( + any(expected_output_fragment in msg["content"] for msg in coder.cur_messages) + ) + def test_cmd_add_drop_untracked_files(self): with GitTemporaryDirectory(): repo = git.Repo() @@ -1283,6 +1306,38 @@ class TestCommands(TestCase): # Verify the file was not added self.assertEqual(len(coder.abs_fnames), 0) + def test_cmd_think_tokens(self): + io = InputOutput(pretty=False, fancy_input=False, yes=True) + coder = Coder.create(self.GPT35, None, io) + commands = Commands(io, coder) + + # Test with various formats + test_values = { + "8k": 8192, # 8 * 1024 + "10.5k": 10752, # 10.5 * 1024 + "512k": 524288, # 0.5 * 1024 * 1024 + } + + for input_value, expected_tokens in test_values.items(): + with mock.patch.object(io, "tool_output") as mock_tool_output: + commands.cmd_think_tokens(input_value) + + # Check that the model's thinking tokens were updated + self.assertEqual( + coder.main_model.extra_params["thinking"]["budget_tokens"], expected_tokens + ) + + # Check that the tool output shows the correct value with format + # Use the actual input_value (not normalized) in the assertion + mock_tool_output.assert_any_call( + f"Set thinking token budget to {expected_tokens:,} tokens ({input_value})." + ) + + # Test with no value provided - should display current value + with mock.patch.object(io, "tool_output") as mock_tool_output: + commands.cmd_think_tokens("") + mock_tool_output.assert_any_call(mock.ANY) # Just verify it calls tool_output + def test_cmd_add_aiderignored_file(self): with GitTemporaryDirectory(): repo = git.Repo() @@ -1632,6 +1687,98 @@ class TestCommands(TestCase): self.assertIn("-Further modified content", diff_output) self.assertIn("+Final modified content", diff_output) + def test_cmd_model(self): + io = InputOutput(pretty=False, fancy_input=False, yes=True) + coder = Coder.create(self.GPT35, None, io) + commands = Commands(io, coder) + + # Test switching the main model + with self.assertRaises(SwitchCoder) as context: + commands.cmd_model("gpt-4") + + # Check that the SwitchCoder exception contains the correct model configuration + self.assertEqual(context.exception.kwargs.get("main_model").name, "gpt-4") + self.assertEqual( + context.exception.kwargs.get("main_model").editor_model.name, + self.GPT35.editor_model.name, + ) + self.assertEqual( + context.exception.kwargs.get("main_model").weak_model.name, self.GPT35.weak_model.name + ) + # Check that the edit format is updated to the new model's default + self.assertEqual(context.exception.kwargs.get("edit_format"), "diff") + + def test_cmd_model_preserves_explicit_edit_format(self): + io = InputOutput(pretty=False, fancy_input=False, yes=True) + # Use gpt-3.5-turbo (default 'diff') + coder = Coder.create(self.GPT35, None, io) + # Explicitly set edit format to something else + coder.edit_format = "udiff" + commands = Commands(io, coder) + + # Mock sanity check to avoid network calls + with mock.patch("aider.models.sanity_check_models"): + # Test switching the main model to gpt-4 (default 'whole') + with self.assertRaises(SwitchCoder) as context: + commands.cmd_model("gpt-4") + + # Check that the SwitchCoder exception contains the correct model configuration + self.assertEqual(context.exception.kwargs.get("main_model").name, "gpt-4") + # Check that the edit format is preserved + self.assertEqual(context.exception.kwargs.get("edit_format"), "udiff") + + def test_cmd_editor_model(self): + io = InputOutput(pretty=False, fancy_input=False, yes=True) + coder = Coder.create(self.GPT35, None, io) + commands = Commands(io, coder) + + # Test switching the editor model + with self.assertRaises(SwitchCoder) as context: + commands.cmd_editor_model("gpt-4") + + # Check that the SwitchCoder exception contains the correct model configuration + self.assertEqual(context.exception.kwargs.get("main_model").name, self.GPT35.name) + self.assertEqual(context.exception.kwargs.get("main_model").editor_model.name, "gpt-4") + self.assertEqual( + context.exception.kwargs.get("main_model").weak_model.name, self.GPT35.weak_model.name + ) + + def test_cmd_weak_model(self): + io = InputOutput(pretty=False, fancy_input=False, yes=True) + coder = Coder.create(self.GPT35, None, io) + commands = Commands(io, coder) + + # Test switching the weak model + with self.assertRaises(SwitchCoder) as context: + commands.cmd_weak_model("gpt-4") + + # Check that the SwitchCoder exception contains the correct model configuration + self.assertEqual(context.exception.kwargs.get("main_model").name, self.GPT35.name) + self.assertEqual( + context.exception.kwargs.get("main_model").editor_model.name, + self.GPT35.editor_model.name, + ) + self.assertEqual(context.exception.kwargs.get("main_model").weak_model.name, "gpt-4") + + def test_cmd_model_updates_default_edit_format(self): + io = InputOutput(pretty=False, fancy_input=False, yes=True) + # Use gpt-3.5-turbo (default 'diff') + coder = Coder.create(self.GPT35, None, io) + # Ensure current edit format is the default + self.assertEqual(coder.edit_format, self.GPT35.edit_format) + commands = Commands(io, coder) + + # Mock sanity check to avoid network calls + with mock.patch("aider.models.sanity_check_models"): + # Test switching the main model to gpt-4 (default 'whole') + with self.assertRaises(SwitchCoder) as context: + commands.cmd_model("gpt-4") + + # Check that the SwitchCoder exception contains the correct model configuration + self.assertEqual(context.exception.kwargs.get("main_model").name, "gpt-4") + # Check that the edit format is updated to the new model's default + self.assertEqual(context.exception.kwargs.get("edit_format"), "diff") + def test_cmd_ask(self): io = InputOutput(pretty=False, fancy_input=False, yes=True) coder = Coder.create(self.GPT35, None, io) @@ -1722,6 +1869,213 @@ class TestCommands(TestCase): del coder del commands + def test_reset_with_original_read_only_files(self): + with GitTemporaryDirectory() as repo_dir: + io = InputOutput(pretty=False, fancy_input=False, yes=True) + coder = Coder.create(self.GPT35, None, io) + + # Create test files + orig_read_only = Path(repo_dir) / "orig_read_only.txt" + orig_read_only.write_text("Original read-only file") + + added_file = Path(repo_dir) / "added_file.txt" + added_file.write_text("Added file") + + added_read_only = Path(repo_dir) / "added_read_only.txt" + added_read_only.write_text("Added read-only file") + + # Initialize commands with original read-only files + commands = Commands(io, coder, original_read_only_fnames=[str(orig_read_only)]) + + # Add files to the chat + coder.abs_read_only_fnames.add(str(orig_read_only)) + coder.abs_fnames.add(str(added_file)) + coder.abs_read_only_fnames.add(str(added_read_only)) + + # Add some messages to the chat history + coder.cur_messages = [{"role": "user", "content": "Test message"}] + coder.done_messages = [{"role": "assistant", "content": "Test response"}] + + # Verify initial state + self.assertEqual(len(coder.abs_fnames), 1) + self.assertEqual(len(coder.abs_read_only_fnames), 2) + self.assertEqual(len(coder.cur_messages), 1) + self.assertEqual(len(coder.done_messages), 1) + + # Test reset command + commands.cmd_reset("") + + # Verify that original read-only file is preserved + # but other files and messages are cleared + self.assertEqual(len(coder.abs_fnames), 0) + self.assertEqual(len(coder.abs_read_only_fnames), 1) + self.assertIn(str(orig_read_only), coder.abs_read_only_fnames) + self.assertNotIn(str(added_read_only), coder.abs_read_only_fnames) + + # Chat history should be cleared + self.assertEqual(len(coder.cur_messages), 0) + self.assertEqual(len(coder.done_messages), 0) + + def test_reset_with_no_original_read_only_files(self): + with GitTemporaryDirectory() as repo_dir: + io = InputOutput(pretty=False, fancy_input=False, yes=True) + coder = Coder.create(self.GPT35, None, io) + + # Create test files + added_file = Path(repo_dir) / "added_file.txt" + added_file.write_text("Added file") + + added_read_only = Path(repo_dir) / "added_read_only.txt" + added_read_only.write_text("Added read-only file") + + # Initialize commands with no original read-only files + commands = Commands(io, coder) + + # Add files to the chat + coder.abs_fnames.add(str(added_file)) + coder.abs_read_only_fnames.add(str(added_read_only)) + + # Add some messages to the chat history + coder.cur_messages = [{"role": "user", "content": "Test message"}] + coder.done_messages = [{"role": "assistant", "content": "Test response"}] + + # Verify initial state + self.assertEqual(len(coder.abs_fnames), 1) + self.assertEqual(len(coder.abs_read_only_fnames), 1) + self.assertEqual(len(coder.cur_messages), 1) + self.assertEqual(len(coder.done_messages), 1) + + # Test reset command + commands.cmd_reset("") + + # Verify that all files and messages are cleared + self.assertEqual(len(coder.abs_fnames), 0) + self.assertEqual(len(coder.abs_read_only_fnames), 0) + self.assertEqual(len(coder.cur_messages), 0) + self.assertEqual(len(coder.done_messages), 0) + + def test_cmd_reasoning_effort(self): + io = InputOutput(pretty=False, fancy_input=False, yes=True) + coder = Coder.create(self.GPT35, None, io) + commands = Commands(io, coder) + + # Test with numeric values + with mock.patch.object(io, "tool_output") as mock_tool_output: + commands.cmd_reasoning_effort("0.8") + mock_tool_output.assert_any_call("Set reasoning effort to 0.8") + + # Test with text values (low/medium/high) + for effort_level in ["low", "medium", "high"]: + with mock.patch.object(io, "tool_output") as mock_tool_output: + commands.cmd_reasoning_effort(effort_level) + mock_tool_output.assert_any_call(f"Set reasoning effort to {effort_level}") + + # Check model's reasoning effort was updated + with mock.patch.object(coder.main_model, "set_reasoning_effort") as mock_set_effort: + commands.cmd_reasoning_effort("0.5") + mock_set_effort.assert_called_once_with("0.5") + + # Test with no value provided - should display current value + with mock.patch.object(io, "tool_output") as mock_tool_output: + commands.cmd_reasoning_effort("") + mock_tool_output.assert_any_call("Current reasoning effort: high") + + def test_drop_with_original_read_only_files(self): + with GitTemporaryDirectory() as repo_dir: + io = InputOutput(pretty=False, fancy_input=False, yes=True) + coder = Coder.create(self.GPT35, None, io) + + # Create test files + orig_read_only = Path(repo_dir) / "orig_read_only.txt" + orig_read_only.write_text("Original read-only file") + + added_file = Path(repo_dir) / "added_file.txt" + added_file.write_text("Added file") + + added_read_only = Path(repo_dir) / "added_read_only.txt" + added_read_only.write_text("Added read-only file") + + # Initialize commands with original read-only files + commands = Commands(io, coder, original_read_only_fnames=[str(orig_read_only)]) + + # Add files to the chat + coder.abs_read_only_fnames.add(str(orig_read_only)) + coder.abs_fnames.add(str(added_file)) + coder.abs_read_only_fnames.add(str(added_read_only)) + + # Verify initial state + self.assertEqual(len(coder.abs_fnames), 1) + self.assertEqual(len(coder.abs_read_only_fnames), 2) + + # Test bare drop command + with mock.patch.object(io, "tool_output") as mock_tool_output: + commands.cmd_drop("") + mock_tool_output.assert_called_with( + "Dropping all files from the chat session except originally read-only files." + ) + + # Verify that original read-only file is preserved, but other files are dropped + self.assertEqual(len(coder.abs_fnames), 0) + self.assertEqual(len(coder.abs_read_only_fnames), 1) + self.assertIn(str(orig_read_only), coder.abs_read_only_fnames) + self.assertNotIn(str(added_read_only), coder.abs_read_only_fnames) + + def test_drop_specific_original_read_only_file(self): + with GitTemporaryDirectory() as repo_dir: + io = InputOutput(pretty=False, fancy_input=False, yes=True) + coder = Coder.create(self.GPT35, None, io) + + # Create test file + orig_read_only = Path(repo_dir) / "orig_read_only.txt" + orig_read_only.write_text("Original read-only file") + + # Initialize commands with original read-only files + commands = Commands(io, coder, original_read_only_fnames=[str(orig_read_only)]) + + # Add file to the chat + coder.abs_read_only_fnames.add(str(orig_read_only)) + + # Verify initial state + self.assertEqual(len(coder.abs_read_only_fnames), 1) + + # Test specific drop command + commands.cmd_drop("orig_read_only.txt") + + # Verify that the original read-only file is dropped when specified explicitly + self.assertEqual(len(coder.abs_read_only_fnames), 0) + + def test_drop_with_no_original_read_only_files(self): + with GitTemporaryDirectory() as repo_dir: + io = InputOutput(pretty=False, fancy_input=False, yes=True) + coder = Coder.create(self.GPT35, None, io) + + # Create test files + added_file = Path(repo_dir) / "added_file.txt" + added_file.write_text("Added file") + + added_read_only = Path(repo_dir) / "added_read_only.txt" + added_read_only.write_text("Added read-only file") + + # Initialize commands with no original read-only files + commands = Commands(io, coder) + + # Add files to the chat + coder.abs_fnames.add(str(added_file)) + coder.abs_read_only_fnames.add(str(added_read_only)) + + # Verify initial state + self.assertEqual(len(coder.abs_fnames), 1) + self.assertEqual(len(coder.abs_read_only_fnames), 1) + + # Test bare drop command + with mock.patch.object(io, "tool_output") as mock_tool_output: + commands.cmd_drop("") + mock_tool_output.assert_called_with("Dropping all files from the chat session.") + + # Verify that all files are dropped + self.assertEqual(len(coder.abs_fnames), 0) + self.assertEqual(len(coder.abs_read_only_fnames), 0) + def test_cmd_load_with_switch_coder(self): with GitTemporaryDirectory() as repo_dir: io = InputOutput(pretty=False, fancy_input=False, yes=True) diff --git a/tests/basic/test_deprecated.py b/tests/basic/test_deprecated.py new file mode 100644 index 000000000..62f9b2ada --- /dev/null +++ b/tests/basic/test_deprecated.py @@ -0,0 +1,140 @@ +import os +from unittest import TestCase +from unittest.mock import MagicMock, patch + +from prompt_toolkit.input import DummyInput +from prompt_toolkit.output import DummyOutput + +from aider.deprecated import handle_deprecated_model_args +from aider.dump import dump # noqa +from aider.main import main + + +class TestDeprecated(TestCase): + def setUp(self): + self.original_env = os.environ.copy() + os.environ["OPENAI_API_KEY"] = "deadbeef" + os.environ["AIDER_CHECK_UPDATE"] = "false" + os.environ["AIDER_ANALYTICS"] = "false" + + def tearDown(self): + os.environ.clear() + os.environ.update(self.original_env) + + @patch("aider.io.InputOutput.tool_warning") + @patch("aider.io.InputOutput.offer_url") + def test_deprecated_args_show_warnings(self, mock_offer_url, mock_tool_warning): + # Prevent URL launches during tests + mock_offer_url.return_value = False + # Test all deprecated flags to ensure they show warnings + deprecated_flags = [ + "--opus", + "--sonnet", + "--haiku", + "--4", + "-4", + "--4o", + "--mini", + "--4-turbo", + "--35turbo", + "--35-turbo", + "--3", + "-3", + "--deepseek", + "--o1-mini", + "--o1-preview", + ] + + for flag in deprecated_flags: + mock_tool_warning.reset_mock() + + with patch("aider.models.Model"), self.subTest(flag=flag): + main( + [flag, "--no-git", "--exit", "--yes"], input=DummyInput(), output=DummyOutput() + ) + + # Look for the deprecation warning in all calls + deprecation_warning = None + dump(flag) + dump(mock_tool_warning.call_args_list) + for call_args in mock_tool_warning.call_args_list: + dump(call_args) + if "deprecated" in call_args[0][0]: + deprecation_warning = call_args[0][0] + break + + self.assertIsNotNone( + deprecation_warning, f"No deprecation warning found for {flag}" + ) + warning_msg = deprecation_warning + + self.assertIn("deprecated", warning_msg) + self.assertIn("use --model", warning_msg.lower()) + + @patch("aider.io.InputOutput.tool_warning") + @patch("aider.io.InputOutput.offer_url") + def test_model_alias_in_warning(self, mock_offer_url, mock_tool_warning): + # Prevent URL launches during tests + mock_offer_url.return_value = False + # Test that the warning uses the model alias if available + with patch("aider.models.MODEL_ALIASES", {"gpt4": "gpt-4-0613"}): + with patch("aider.models.Model"): + main( + ["--4", "--no-git", "--exit", "--yes"], input=DummyInput(), output=DummyOutput() + ) + + # Look for the deprecation warning in all calls + deprecation_warning = None + for call_args in mock_tool_warning.call_args_list: + if "deprecated" in call_args[0][0] and "--model gpt4" in call_args[0][0]: + deprecation_warning = call_args[0][0] + break + + self.assertIsNotNone( + deprecation_warning, "No deprecation warning with model alias found" + ) + warning_msg = deprecation_warning + self.assertIn("--model gpt4", warning_msg) + self.assertNotIn("--model gpt-4-0613", warning_msg) + + def test_model_is_set_correctly(self): + test_cases = [ + ("opus", "claude-3-opus-20240229"), + ("sonnet", "anthropic/claude-3-7-sonnet-20250219"), + ("haiku", "claude-3-5-haiku-20241022"), + ("4", "gpt-4-0613"), + # Testing the dash variant with underscore in attribute name + ("4o", "gpt-4o"), + ("mini", "gpt-4o-mini"), + ("4_turbo", "gpt-4-1106-preview"), + ("35turbo", "gpt-3.5-turbo"), + ("deepseek", "deepseek/deepseek-chat"), + ("o1_mini", "o1-mini"), + ("o1_preview", "o1-preview"), + ] + + for flag, expected_model in test_cases: + print(flag, expected_model) + + with self.subTest(flag=flag): + # Create a mock IO instance + mock_io = MagicMock() + + # Create args with ONLY the current flag set to True + args = MagicMock() + args.model = None + + # Ensure all flags are False by default + for test_flag, _ in test_cases: + setattr(args, test_flag, False) + + # Set only the current flag to True + setattr(args, flag, True) + + dump(args) + + # Call the handle_deprecated_model_args function + handle_deprecated_model_args(args, mock_io) + + # Check that args.model was set to the expected model + self.assertEqual(args.model, expected_model) diff --git a/tests/basic/test_editblock.py b/tests/basic/test_editblock.py index 0a1f1bf5b..e93edb7c3 100644 --- a/tests/basic/test_editblock.py +++ b/tests/basic/test_editblock.py @@ -108,29 +108,6 @@ Hope you like it! edits = list(eb.find_original_update_blocks(edit)) self.assertEqual(edits, [("foo.txt", "Two\n", "Tooooo\n")]) - def test_find_original_update_blocks_mangled_filename_w_source_tag(self): - source = "source" - - edit = """ -Here's the change: - -<%s>foo.txt -<<<<<<< SEARCH -One -======= -Two ->>>>>>> REPLACE - - -Hope you like it! -""" % (source, source) - - fence = ("<%s>" % source, "" % source) - - with self.assertRaises(ValueError) as cm: - _edits = list(eb.find_original_update_blocks(edit, fence)) - self.assertIn("missing filename", str(cm.exception)) - def test_find_original_update_blocks_quote_below_filename(self): edit = """ Here's the change: @@ -181,10 +158,11 @@ Tooooo oops! +>>>>>>> REPLACE """ with self.assertRaises(ValueError) as cm: - list(eb.find_original_update_blocks(edit)) + _blocks = list(eb.find_original_update_blocks(edit)) self.assertIn("filename", str(cm.exception)) def test_find_original_update_blocks_no_final_newline(self): @@ -575,6 +553,66 @@ Hope you like it! edits = list(eb.find_original_update_blocks(edit, fence=quad_backticks)) self.assertEqual(edits, [("foo.txt", "", "Tooooo\n")]) + # Test for shell script blocks with sh language identifier (issue #3785) + def test_find_original_update_blocks_with_sh_language_identifier(self): + # https://github.com/Aider-AI/aider/issues/3785 + edit = """ +Here's a shell script: + +```sh +test_hello.sh +<<<<<<< SEARCH +======= +#!/bin/bash +# Check if exactly one argument is provided +if [ "$#" -ne 1 ]; then + echo "Usage: $0 " >&2 + exit 1 +fi + +# Echo the first argument +echo "$1" + +exit 0 +>>>>>>> REPLACE +``` +""" + + edits = list(eb.find_original_update_blocks(edit)) + # Instead of comparing exact strings, check that we got the right file and structure + self.assertEqual(len(edits), 1) + self.assertEqual(edits[0][0], "test_hello.sh") + self.assertEqual(edits[0][1], "") + + # Check that the content contains the expected shell script elements + result_content = edits[0][2] + self.assertIn("#!/bin/bash", result_content) + self.assertIn('if [ "$#" -ne 1 ];', result_content) + self.assertIn('echo "Usage: $0 "', result_content) + self.assertIn("exit 1", result_content) + self.assertIn('echo "$1"', result_content) + self.assertIn("exit 0", result_content) + + # Test for C# code blocks with csharp language identifier + def test_find_original_update_blocks_with_csharp_language_identifier(self): + edit = """ +Here's a C# code change: + +```csharp +Program.cs +<<<<<<< SEARCH +Console.WriteLine("Hello World!"); +======= +Console.WriteLine("Hello, C# World!"); +>>>>>>> REPLACE +``` +""" + + edits = list(eb.find_original_update_blocks(edit)) + search_text = 'Console.WriteLine("Hello World!");\n' + replace_text = 'Console.WriteLine("Hello, C# World!");\n' + self.assertEqual(edits, [("Program.cs", search_text, replace_text)]) + if __name__ == "__main__": unittest.main() diff --git a/tests/basic/test_exceptions.py b/tests/basic/test_exceptions.py index f9262a665..5f9c095f8 100644 --- a/tests/basic/test_exceptions.py +++ b/tests/basic/test_exceptions.py @@ -63,3 +63,22 @@ def test_context_window_error(): ) ex_info = ex.get_ex_info(ctx_error) assert ex_info.retry is False + + +def test_openrouter_error(): + """Test specific handling of OpenRouter API errors""" + ex = LiteLLMExceptions() + from litellm import APIConnectionError + + # Create an APIConnectionError with OpenrouterException message + openrouter_error = APIConnectionError( + message="APIConnectionError: OpenrouterException - 'choices'", + model="openrouter/model", + llm_provider="openrouter", + ) + + ex_info = ex.get_ex_info(openrouter_error) + assert ex_info.retry is True + assert "OpenRouter" in ex_info.description + assert "overloaded" in ex_info.description + assert "rate" in ex_info.description diff --git a/tests/basic/test_io.py b/tests/basic/test_io.py index 3f313219d..e925ef66d 100644 --- a/tests/basic/test_io.py +++ b/tests/basic/test_io.py @@ -34,6 +34,35 @@ class TestInputOutput(unittest.TestCase): io = InputOutput(fancy_input=False) self.assertFalse(io.pretty) + def test_color_initialization(self): + """Test that color values are properly initialized with # prefix""" + # Test with hex colors without # + io = InputOutput( + user_input_color="00cc00", + tool_error_color="FF2222", + tool_warning_color="FFA500", + assistant_output_color="0088ff", + pretty=True, + ) + + # Check that # was added to hex colors + self.assertEqual(io.user_input_color, "#00cc00") + self.assertEqual(io.tool_error_color, "#FF2222") + self.assertEqual(io.tool_warning_color, "#FFA500") # Already had # + self.assertEqual(io.assistant_output_color, "#0088ff") + + # Test with named colors (should be unchanged) + io = InputOutput(user_input_color="blue", tool_error_color="red", pretty=True) + + self.assertEqual(io.user_input_color, "blue") + self.assertEqual(io.tool_error_color, "red") + + # Test with pretty=False (should not modify colors) + io = InputOutput(user_input_color="00cc00", tool_error_color="FF2222", pretty=False) + + self.assertIsNone(io.user_input_color) + self.assertIsNone(io.tool_error_color) + def test_dumb_terminal(self): with patch.dict(os.environ, {"TERM": "dumb"}): io = InputOutput(fancy_input=True) @@ -393,6 +422,59 @@ class TestInputOutputMultilineMode(unittest.TestCase): io.prompt_ask("Test prompt?") self.assertTrue(io.multiline_mode) # Should be restored + def test_ensure_hash_prefix(self): + """Test that ensure_hash_prefix correctly adds # to valid hex colors""" + from aider.io import ensure_hash_prefix + + # Test valid hex colors without # + self.assertEqual(ensure_hash_prefix("000"), "#000") + self.assertEqual(ensure_hash_prefix("fff"), "#fff") + self.assertEqual(ensure_hash_prefix("F00"), "#F00") + self.assertEqual(ensure_hash_prefix("123456"), "#123456") + self.assertEqual(ensure_hash_prefix("abcdef"), "#abcdef") + self.assertEqual(ensure_hash_prefix("ABCDEF"), "#ABCDEF") + + # Test hex colors that already have # + self.assertEqual(ensure_hash_prefix("#000"), "#000") + self.assertEqual(ensure_hash_prefix("#123456"), "#123456") + + # Test invalid inputs (should return unchanged) + self.assertEqual(ensure_hash_prefix(""), "") + self.assertEqual(ensure_hash_prefix(None), None) + self.assertEqual(ensure_hash_prefix("red"), "red") # Named color + self.assertEqual(ensure_hash_prefix("12345"), "12345") # Wrong length + self.assertEqual(ensure_hash_prefix("1234567"), "1234567") # Wrong length + self.assertEqual(ensure_hash_prefix("xyz"), "xyz") # Invalid hex chars + self.assertEqual(ensure_hash_prefix("12345g"), "12345g") # Invalid hex chars + + def test_tool_output_color_handling(self): + """Test that tool_output correctly handles hex colors without # prefix""" + from unittest.mock import patch + + from rich.text import Text + + # Create IO with hex color without # for tool_output_color + io = InputOutput(tool_output_color="FFA500", pretty=True) + + # Patch console.print to avoid actual printing + with patch.object(io.console, "print") as mock_print: + # This would raise ColorParseError without the fix + io.tool_output("Test message") + + # Verify the call was made without error + mock_print.assert_called_once() + + # Verify the style was correctly created with # prefix + # The first argument is the message, second would be the style + kwargs = mock_print.call_args.kwargs + self.assertIn("style", kwargs) + + # Test with other hex color + io = InputOutput(tool_output_color="00FF00", pretty=True) + with patch.object(io.console, "print") as mock_print: + io.tool_output("Test message") + mock_print.assert_called_once() + if __name__ == "__main__": unittest.main() diff --git a/tests/basic/test_main.py b/tests/basic/test_main.py index 3374b1323..0836c75d7 100644 --- a/tests/basic/test_main.py +++ b/tests/basic/test_main.py @@ -14,7 +14,7 @@ from prompt_toolkit.output import DummyOutput from aider.coders import Coder from aider.dump import dump # noqa: F401 from aider.io import InputOutput -from aider.main import check_gitignore, main, setup_git +from aider.main import check_gitignore, load_dotenv_files, main, setup_git from aider.utils import GitTemporaryDirectory, IgnorantTemporaryDirectory, make_repo @@ -684,6 +684,116 @@ class TestMain(TestCase): ) self.assertTrue(coder.detect_urls) + def test_accepts_settings_warnings(self): + # Test that appropriate warnings are shown based on accepts_settings configuration + with GitTemporaryDirectory(): + # Test model that accepts the thinking_tokens setting + with ( + patch("aider.io.InputOutput.tool_warning") as mock_warning, + patch("aider.models.Model.set_thinking_tokens") as mock_set_thinking, + ): + main( + [ + "--model", + "anthropic/claude-3-7-sonnet-20250219", + "--thinking-tokens", + "1000", + "--yes", + "--exit", + ], + input=DummyInput(), + output=DummyOutput(), + ) + # No warning should be shown as this model accepts thinking_tokens + for call in mock_warning.call_args_list: + self.assertNotIn("thinking_tokens", call[0][0]) + # Method should be called + mock_set_thinking.assert_called_once_with("1000") + + # Test model that doesn't have accepts_settings for thinking_tokens + with ( + patch("aider.io.InputOutput.tool_warning") as mock_warning, + patch("aider.models.Model.set_thinking_tokens") as mock_set_thinking, + ): + main( + [ + "--model", + "gpt-4o", + "--thinking-tokens", + "1000", + "--check-model-accepts-settings", + "--yes", + "--exit", + ], + input=DummyInput(), + output=DummyOutput(), + ) + # Warning should be shown + warning_shown = False + for call in mock_warning.call_args_list: + if "thinking_tokens" in call[0][0]: + warning_shown = True + self.assertTrue(warning_shown) + # Method should NOT be called because model doesn't support it and check flag is on + mock_set_thinking.assert_not_called() + + # Test model that accepts the reasoning_effort setting + with ( + patch("aider.io.InputOutput.tool_warning") as mock_warning, + patch("aider.models.Model.set_reasoning_effort") as mock_set_reasoning, + ): + main( + ["--model", "o1", "--reasoning-effort", "3", "--yes", "--exit"], + input=DummyInput(), + output=DummyOutput(), + ) + # No warning should be shown as this model accepts reasoning_effort + for call in mock_warning.call_args_list: + self.assertNotIn("reasoning_effort", call[0][0]) + # Method should be called + mock_set_reasoning.assert_called_once_with("3") + + # Test model that doesn't have accepts_settings for reasoning_effort + with ( + patch("aider.io.InputOutput.tool_warning") as mock_warning, + patch("aider.models.Model.set_reasoning_effort") as mock_set_reasoning, + ): + main( + ["--model", "gpt-3.5-turbo", "--reasoning-effort", "3", "--yes", "--exit"], + input=DummyInput(), + output=DummyOutput(), + ) + # Warning should be shown + warning_shown = False + for call in mock_warning.call_args_list: + if "reasoning_effort" in call[0][0]: + warning_shown = True + self.assertTrue(warning_shown) + # Method should still be called by default + mock_set_reasoning.assert_not_called() + + @patch("aider.models.ModelInfoManager.set_verify_ssl") + def test_no_verify_ssl_sets_model_info_manager(self, mock_set_verify_ssl): + with GitTemporaryDirectory(): + # Mock Model class to avoid actual model initialization + with patch("aider.models.Model") as mock_model: + # Configure the mock to avoid the TypeError + mock_model.return_value.info = {} + mock_model.return_value.name = "gpt-4" # Add a string name + mock_model.return_value.validate_environment.return_value = { + "missing_keys": [], + "keys_in_environment": [], + } + + # Mock fuzzy_match_models to avoid string operations on MagicMock + with patch("aider.models.fuzzy_match_models", return_value=[]): + main( + ["--no-verify-ssl", "--exit", "--yes"], + input=DummyInput(), + output=DummyOutput(), + ) + mock_set_verify_ssl.assert_called_once_with(False) + def test_pytest_env_vars(self): # Verify that environment variables from pytest.ini are properly set self.assertEqual(os.environ.get("AIDER_ANALYTICS"), "false") @@ -741,6 +851,102 @@ class TestMain(TestCase): result = main(["--api-key", "INVALID_FORMAT", "--exit", "--yes"]) self.assertEqual(result, 1) + def test_git_config_include(self): + # Test that aider respects git config includes for user.name and user.email + with GitTemporaryDirectory() as git_dir: + git_dir = Path(git_dir) + + # Create an includable config file with user settings + include_config = git_dir / "included.gitconfig" + include_config.write_text( + "[user]\n name = Included User\n email = included@example.com\n" + ) + + # Set up main git config to include the other file + repo = git.Repo(git_dir) + include_path = str(include_config).replace("\\", "/") + repo.git.config("--local", "include.path", str(include_path)) + + # Verify the config is set up correctly using git command + self.assertEqual(repo.git.config("user.name"), "Included User") + self.assertEqual(repo.git.config("user.email"), "included@example.com") + + # Manually check the git config file to confirm include directive + git_config_path = git_dir / ".git" / "config" + git_config_content = git_config_path.read_text() + + # Run aider and verify it doesn't change the git config + main(["--yes", "--exit"], input=DummyInput(), output=DummyOutput()) + + # Check that the user settings are still the same using git command + repo = git.Repo(git_dir) # Re-open repo to ensure we get fresh config + self.assertEqual(repo.git.config("user.name"), "Included User") + self.assertEqual(repo.git.config("user.email"), "included@example.com") + + # Manually check the git config file again to ensure it wasn't modified + git_config_content_after = git_config_path.read_text() + self.assertEqual(git_config_content, git_config_content_after) + + def test_git_config_include_directive(self): + # Test that aider respects the include directive in git config + with GitTemporaryDirectory() as git_dir: + git_dir = Path(git_dir) + + # Create an includable config file with user settings + include_config = git_dir / "included.gitconfig" + include_config.write_text( + "[user]\n name = Directive User\n email = directive@example.com\n" + ) + + # Set up main git config with include directive + git_config = git_dir / ".git" / "config" + # Use normalized path with forward slashes for git config + include_path = str(include_config).replace("\\", "/") + with open(git_config, "a") as f: + f.write(f"\n[include]\n path = {include_path}\n") + + # Read the modified config file + modified_config_content = git_config.read_text() + + # Verify the include directive was added correctly + self.assertIn("[include]", modified_config_content) + + # Verify the config is set up correctly using git command + repo = git.Repo(git_dir) + self.assertEqual(repo.git.config("user.name"), "Directive User") + self.assertEqual(repo.git.config("user.email"), "directive@example.com") + + # Run aider and verify it doesn't change the git config + main(["--yes", "--exit"], input=DummyInput(), output=DummyOutput()) + + # Check that the git config file wasn't modified + config_after_aider = git_config.read_text() + self.assertEqual(modified_config_content, config_after_aider) + + # Check that the user settings are still the same using git command + repo = git.Repo(git_dir) # Re-open repo to ensure we get fresh config + self.assertEqual(repo.git.config("user.name"), "Directive User") + self.assertEqual(repo.git.config("user.email"), "directive@example.com") + + def test_resolve_aiderignore_path(self): + # Import the function directly to test it + from aider.args import resolve_aiderignore_path + + # Test with absolute path + abs_path = os.path.abspath("/tmp/test/.aiderignore") + self.assertEqual(resolve_aiderignore_path(abs_path), abs_path) + + # Test with relative path and git root + git_root = "/path/to/git/root" + rel_path = ".aiderignore" + self.assertEqual( + resolve_aiderignore_path(rel_path, git_root), str(Path(git_root) / rel_path) + ) + + # Test with relative path and no git root + rel_path = ".aiderignore" + self.assertEqual(resolve_aiderignore_path(rel_path), rel_path) + def test_invalid_edit_format(self): with GitTemporaryDirectory(): with patch("aider.io.InputOutput.offer_url") as mock_offer_url: @@ -777,7 +983,7 @@ class TestMain(TestCase): coder = main( ["--exit", "--yes"], input=DummyInput(), output=DummyOutput(), return_coder=True ) - self.assertIn("openrouter/anthropic/claude", coder.main_model.name.lower()) + self.assertIn("openrouter/", coder.main_model.name.lower()) del os.environ["OPENROUTER_API_KEY"] # Test OpenAI API key @@ -793,12 +999,15 @@ class TestMain(TestCase): coder = main( ["--exit", "--yes"], input=DummyInput(), output=DummyOutput(), return_coder=True ) - self.assertIn("flash", coder.main_model.name.lower()) + self.assertIn("gemini", coder.main_model.name.lower()) del os.environ["GEMINI_API_KEY"] - # Test no API keys - result = main(["--exit", "--yes"], input=DummyInput(), output=DummyOutput()) - self.assertEqual(result, 1) + # Test no API keys - should offer OpenRouter OAuth + with patch("aider.onboarding.offer_openrouter_oauth") as mock_offer_oauth: + mock_offer_oauth.return_value = None # Simulate user declining or failure + result = main(["--exit", "--yes"], input=DummyInput(), output=DummyOutput()) + self.assertEqual(result, 1) # Expect failure since no model could be selected + mock_offer_oauth.assert_called_once() def test_model_precedence(self): with GitTemporaryDirectory(): @@ -836,7 +1045,7 @@ class TestMain(TestCase): def test_reasoning_effort_option(self): coder = main( - ["--reasoning-effort", "3", "--yes", "--exit"], + ["--reasoning-effort", "3", "--no-check-model-accepts-settings", "--yes", "--exit"], input=DummyInput(), output=DummyOutput(), return_coder=True, @@ -844,3 +1053,295 @@ class TestMain(TestCase): self.assertEqual( coder.main_model.extra_params.get("extra_body", {}).get("reasoning_effort"), "3" ) + + def test_thinking_tokens_option(self): + coder = main( + ["--model", "sonnet", "--thinking-tokens", "1000", "--yes", "--exit"], + input=DummyInput(), + output=DummyOutput(), + return_coder=True, + ) + self.assertEqual( + coder.main_model.extra_params.get("thinking", {}).get("budget_tokens"), 1000 + ) + + def test_list_models_includes_metadata_models(self): + # Test that models from model-metadata.json appear in list-models output + with GitTemporaryDirectory(): + # Create a temporary model-metadata.json with test models + metadata_file = Path(".aider.model.metadata.json") + test_models = { + "unique-model-name": { + "max_input_tokens": 8192, + "litellm_provider": "test-provider", + "mode": "chat", # Added mode attribute + }, + "another-provider/another-unique-model": { + "max_input_tokens": 4096, + "litellm_provider": "another-provider", + "mode": "chat", # Added mode attribute + }, + } + metadata_file.write_text(json.dumps(test_models)) + + # Capture stdout to check the output + with patch("sys.stdout", new_callable=StringIO) as mock_stdout: + main( + [ + "--list-models", + "unique-model", + "--model-metadata-file", + str(metadata_file), + "--yes", + "--no-gitignore", + ], + input=DummyInput(), + output=DummyOutput(), + ) + output = mock_stdout.getvalue() + + # Check that the unique model name from our metadata file is listed + self.assertIn("test-provider/unique-model-name", output) + + def test_list_models_includes_all_model_sources(self): + # Test that models from both litellm.model_cost and model-metadata.json + # appear in list-models + with GitTemporaryDirectory(): + # Create a temporary model-metadata.json with test models + metadata_file = Path(".aider.model.metadata.json") + test_models = { + "metadata-only-model": { + "max_input_tokens": 8192, + "litellm_provider": "test-provider", + "mode": "chat", # Added mode attribute + } + } + metadata_file.write_text(json.dumps(test_models)) + + # Capture stdout to check the output + with patch("sys.stdout", new_callable=StringIO) as mock_stdout: + main( + [ + "--list-models", + "metadata-only-model", + "--model-metadata-file", + str(metadata_file), + "--yes", + "--no-gitignore", + ], + input=DummyInput(), + output=DummyOutput(), + ) + output = mock_stdout.getvalue() + + dump(output) + + # Check that both models appear in the output + self.assertIn("test-provider/metadata-only-model", output) + + def test_check_model_accepts_settings_flag(self): + # Test that --check-model-accepts-settings affects whether settings are applied + with GitTemporaryDirectory(): + # When flag is on, setting shouldn't be applied to non-supporting model + with patch("aider.models.Model.set_thinking_tokens") as mock_set_thinking: + main( + [ + "--model", + "gpt-4o", + "--thinking-tokens", + "1000", + "--check-model-accepts-settings", + "--yes", + "--exit", + ], + input=DummyInput(), + output=DummyOutput(), + ) + # Method should not be called because model doesn't support it and flag is on + mock_set_thinking.assert_not_called() + + def test_list_models_with_direct_resource_patch(self): + # Test that models from resources/model-metadata.json are included in list-models output + with GitTemporaryDirectory(): + # Create a temporary file with test model metadata + test_file = Path(self.tempdir) / "test-model-metadata.json" + test_resource_models = { + "special-model": { + "max_input_tokens": 8192, + "litellm_provider": "resource-provider", + "mode": "chat", + } + } + test_file.write_text(json.dumps(test_resource_models)) + + # Create a mock for the resource file path + mock_resource_path = MagicMock() + mock_resource_path.__str__.return_value = str(test_file) + + # Create a mock for the files function that returns an object with joinpath + mock_files = MagicMock() + mock_files.joinpath.return_value = mock_resource_path + + with patch("aider.main.importlib_resources.files", return_value=mock_files): + # Capture stdout to check the output + with patch("sys.stdout", new_callable=StringIO) as mock_stdout: + main( + ["--list-models", "special", "--yes", "--no-gitignore"], + input=DummyInput(), + output=DummyOutput(), + ) + output = mock_stdout.getvalue() + + # Check that the resource model appears in the output + self.assertIn("resource-provider/special-model", output) + + # When flag is off, setting should be applied regardless of support + with patch("aider.models.Model.set_reasoning_effort") as mock_set_reasoning: + main( + [ + "--model", + "gpt-3.5-turbo", + "--reasoning-effort", + "3", + "--no-check-model-accepts-settings", + "--yes", + "--exit", + ], + input=DummyInput(), + output=DummyOutput(), + ) + # Method should be called because flag is off + mock_set_reasoning.assert_called_once_with("3") + + def test_model_accepts_settings_attribute(self): + with GitTemporaryDirectory(): + # Test with a model where we override the accepts_settings attribute + with patch("aider.models.Model") as MockModel: + # Setup mock model instance to simulate accepts_settings attribute + mock_instance = MockModel.return_value + mock_instance.name = "test-model" + mock_instance.accepts_settings = ["reasoning_effort"] + mock_instance.validate_environment.return_value = { + "missing_keys": [], + "keys_in_environment": [], + } + mock_instance.info = {} + mock_instance.weak_model_name = None + mock_instance.get_weak_model.return_value = None + + # Run with both settings, but model only accepts reasoning_effort + main( + [ + "--model", + "test-model", + "--reasoning-effort", + "3", + "--thinking-tokens", + "1000", + "--check-model-accepts-settings", + "--yes", + "--exit", + ], + input=DummyInput(), + output=DummyOutput(), + ) + + # Only set_reasoning_effort should be called, not set_thinking_tokens + mock_instance.set_reasoning_effort.assert_called_once_with("3") + mock_instance.set_thinking_tokens.assert_not_called() + + @patch("aider.main.InputOutput") + def test_stream_and_cache_warning(self, MockInputOutput): + mock_io_instance = MockInputOutput.return_value + with GitTemporaryDirectory(): + main( + ["--stream", "--cache-prompts", "--exit", "--yes"], + input=DummyInput(), + output=DummyOutput(), + ) + mock_io_instance.tool_warning.assert_called_with( + "Cost estimates may be inaccurate when using streaming and caching." + ) + + @patch("aider.main.InputOutput") + def test_stream_without_cache_no_warning(self, MockInputOutput): + mock_io_instance = MockInputOutput.return_value + with GitTemporaryDirectory(): + main( + ["--stream", "--exit", "--yes"], + input=DummyInput(), + output=DummyOutput(), + ) + for call in mock_io_instance.tool_warning.call_args_list: + self.assertNotIn("Cost estimates may be inaccurate", call[0][0]) + + def test_load_dotenv_files_override(self): + with GitTemporaryDirectory() as git_dir: + git_dir = Path(git_dir) + + # Create fake home and .aider directory + fake_home = git_dir / "fake_home" + fake_home.mkdir() + aider_dir = fake_home / ".aider" + aider_dir.mkdir() + + # Create oauth keys file + oauth_keys_file = aider_dir / "oauth-keys.env" + oauth_keys_file.write_text("OAUTH_VAR=oauth_val\nSHARED_VAR=oauth_shared\n") + + # Create git root .env file + git_root_env = git_dir / ".env" + git_root_env.write_text("GIT_VAR=git_val\nSHARED_VAR=git_shared\n") + + # Create CWD .env file in a subdir + cwd_subdir = git_dir / "subdir" + cwd_subdir.mkdir() + cwd_env = cwd_subdir / ".env" + cwd_env.write_text("CWD_VAR=cwd_val\nSHARED_VAR=cwd_shared\n") + + # Change to subdir + original_cwd = os.getcwd() + os.chdir(cwd_subdir) + + # Clear relevant env vars before test + for var in ["OAUTH_VAR", "SHARED_VAR", "GIT_VAR", "CWD_VAR"]: + if var in os.environ: + del os.environ[var] + + with patch("pathlib.Path.home", return_value=fake_home): + loaded_files = load_dotenv_files(str(git_dir), None) + + # Assert files were loaded in expected order (oauth first) + self.assertIn(str(oauth_keys_file.resolve()), loaded_files) + self.assertIn(str(git_root_env.resolve()), loaded_files) + self.assertIn(str(cwd_env.resolve()), loaded_files) + self.assertLess( + loaded_files.index(str(oauth_keys_file.resolve())), + loaded_files.index(str(git_root_env.resolve())), + ) + self.assertLess( + loaded_files.index(str(git_root_env.resolve())), + loaded_files.index(str(cwd_env.resolve())), + ) + + # Assert environment variables reflect the override order + self.assertEqual(os.environ.get("OAUTH_VAR"), "oauth_val") + self.assertEqual(os.environ.get("GIT_VAR"), "git_val") + self.assertEqual(os.environ.get("CWD_VAR"), "cwd_val") + # SHARED_VAR should be overridden by the last loaded file (cwd .env) + self.assertEqual(os.environ.get("SHARED_VAR"), "cwd_shared") + + # Restore CWD + os.chdir(original_cwd) + + @patch("aider.main.InputOutput") + def test_cache_without_stream_no_warning(self, MockInputOutput): + mock_io_instance = MockInputOutput.return_value + with GitTemporaryDirectory(): + main( + ["--cache-prompts", "--exit", "--yes", "--no-stream"], + input=DummyInput(), + output=DummyOutput(), + ) + for call in mock_io_instance.tool_warning.call_args_list: + self.assertNotIn("Cost estimates may be inaccurate", call[0][0]) diff --git a/tests/basic/test_model_info_manager.py b/tests/basic/test_model_info_manager.py new file mode 100644 index 000000000..b28f3d56d --- /dev/null +++ b/tests/basic/test_model_info_manager.py @@ -0,0 +1,80 @@ +import os +import tempfile +from pathlib import Path +from unittest import TestCase +from unittest.mock import MagicMock, patch + +from aider.models import ModelInfoManager + + +class TestModelInfoManager(TestCase): + def setUp(self): + self.original_env = os.environ.copy() + self.manager = ModelInfoManager() + # Create a temporary directory for cache + self.temp_dir = tempfile.TemporaryDirectory() + self.manager.cache_dir = Path(self.temp_dir.name) + self.manager.cache_file = self.manager.cache_dir / "model_prices_and_context_window.json" + self.manager.cache_dir.mkdir(exist_ok=True) + + def tearDown(self): + self.temp_dir.cleanup() + os.environ.clear() + os.environ.update(self.original_env) + + @patch("requests.get") + def test_update_cache_respects_verify_ssl(self, mock_get): + # Setup mock response + mock_response = MagicMock() + mock_response.status_code = 200 + mock_response.json.return_value = {"test_model": {"max_tokens": 4096}} + mock_get.return_value = mock_response + + # Test with default verify_ssl=True + self.manager._update_cache() + mock_get.assert_called_with(self.manager.MODEL_INFO_URL, timeout=5, verify=True) + + # Test with verify_ssl=False + mock_get.reset_mock() + self.manager.set_verify_ssl(False) + self.manager._update_cache() + mock_get.assert_called_with(self.manager.MODEL_INFO_URL, timeout=5, verify=False) + + def test_lazy_loading_cache(self): + # Create a cache file + self.manager.cache_file.write_text('{"test_model": {"max_tokens": 4096}}') + + # Verify cache is not loaded on initialization + self.assertFalse(self.manager._cache_loaded) + self.assertIsNone(self.manager.content) + + # Access content through get_model_from_cached_json_db + with patch.object(self.manager, "_update_cache") as mock_update: + result = self.manager.get_model_from_cached_json_db("test_model") + + # Verify cache was loaded + self.assertTrue(self.manager._cache_loaded) + self.assertIsNotNone(self.manager.content) + self.assertEqual(result, {"max_tokens": 4096}) + + # Verify _update_cache was not called since cache exists and is valid + mock_update.assert_not_called() + + @patch("requests.get") + def test_verify_ssl_setting_before_cache_loading(self, mock_get): + # Setup mock response + mock_response = MagicMock() + mock_response.status_code = 200 + mock_response.json.return_value = {"test_model": {"max_tokens": 4096}} + mock_get.return_value = mock_response + + # Set verify_ssl to False before any cache operations + self.manager.set_verify_ssl(False) + + # Force cache update by making it look expired + with patch("time.time", return_value=9999999999): + # This should trigger _update_cache + self.manager.get_model_from_cached_json_db("test_model") + + # Verify _update_cache was called with verify=False + mock_get.assert_called_with(self.manager.MODEL_INFO_URL, timeout=5, verify=False) diff --git a/tests/basic/test_models.py b/tests/basic/test_models.py index be42f3bf4..b4fbfc239 100644 --- a/tests/basic/test_models.py +++ b/tests/basic/test_models.py @@ -105,6 +105,21 @@ class TestModels(unittest.TestCase): any("bogus-model" in msg for msg in warning_messages) ) # Check that one of the warnings mentions the bogus model + @patch("aider.models.check_for_dependencies") + def test_sanity_check_model_calls_check_dependencies(self, mock_check_deps): + """Test that sanity_check_model calls check_for_dependencies""" + mock_io = MagicMock() + model = MagicMock() + model.name = "test-model" + model.missing_keys = [] + model.keys_in_environment = True + model.info = {"some": "info"} + + sanity_check_model(mock_io, model) + + # Verify check_for_dependencies was called with the model name + mock_check_deps.assert_called_once_with(mock_io, "test-model") + def test_model_aliases(self): # Test common aliases model = Model("4") @@ -145,6 +160,103 @@ class TestModels(unittest.TestCase): self.assertEqual(model.name, "github/o1-preview") self.assertEqual(model.use_temperature, False) + def test_parse_token_value(self): + # Create a model instance to test the parse_token_value method + model = Model("gpt-4") + + # Test integer inputs + self.assertEqual(model.parse_token_value(8096), 8096) + self.assertEqual(model.parse_token_value(1000), 1000) + + # Test string inputs + self.assertEqual(model.parse_token_value("8096"), 8096) + + # Test k/K suffix (kilobytes) + self.assertEqual(model.parse_token_value("8k"), 8 * 1024) + self.assertEqual(model.parse_token_value("8K"), 8 * 1024) + self.assertEqual(model.parse_token_value("10.5k"), 10.5 * 1024) + self.assertEqual(model.parse_token_value("0.5K"), 0.5 * 1024) + + # Test m/M suffix (megabytes) + self.assertEqual(model.parse_token_value("1m"), 1 * 1024 * 1024) + self.assertEqual(model.parse_token_value("1M"), 1 * 1024 * 1024) + self.assertEqual(model.parse_token_value("0.5M"), 0.5 * 1024 * 1024) + + # Test with spaces + self.assertEqual(model.parse_token_value(" 8k "), 8 * 1024) + + # Test conversion from other types + self.assertEqual(model.parse_token_value(8.0), 8) + + def test_set_thinking_tokens(self): + # Test that set_thinking_tokens correctly sets the tokens with different formats + model = Model("gpt-4") + + # Test with integer + model.set_thinking_tokens(8096) + self.assertEqual(model.extra_params["thinking"]["budget_tokens"], 8096) + self.assertFalse(model.use_temperature) + + # Test with string + model.set_thinking_tokens("10k") + self.assertEqual(model.extra_params["thinking"]["budget_tokens"], 10 * 1024) + + # Test with decimal value + model.set_thinking_tokens("0.5M") + self.assertEqual(model.extra_params["thinking"]["budget_tokens"], 0.5 * 1024 * 1024) + + @patch("aider.models.check_pip_install_extra") + def test_check_for_dependencies_bedrock(self, mock_check_pip): + """Test that check_for_dependencies calls check_pip_install_extra for Bedrock models""" + from aider.io import InputOutput + + io = InputOutput() + + # Test with a Bedrock model + from aider.models import check_for_dependencies + + check_for_dependencies(io, "bedrock/anthropic.claude-3-sonnet-20240229-v1:0") + + # Verify check_pip_install_extra was called with correct arguments + mock_check_pip.assert_called_once_with( + io, "boto3", "AWS Bedrock models require the boto3 package.", ["boto3"] + ) + + @patch("aider.models.check_pip_install_extra") + def test_check_for_dependencies_vertex_ai(self, mock_check_pip): + """Test that check_for_dependencies calls check_pip_install_extra for Vertex AI models""" + from aider.io import InputOutput + + io = InputOutput() + + # Test with a Vertex AI model + from aider.models import check_for_dependencies + + check_for_dependencies(io, "vertex_ai/gemini-1.5-pro") + + # Verify check_pip_install_extra was called with correct arguments + mock_check_pip.assert_called_once_with( + io, + "google.cloud.aiplatform", + "Google Vertex AI models require the google-cloud-aiplatform package.", + ["google-cloud-aiplatform"], + ) + + @patch("aider.models.check_pip_install_extra") + def test_check_for_dependencies_other_model(self, mock_check_pip): + """Test that check_for_dependencies doesn't call check_pip_install_extra for other models""" + from aider.io import InputOutput + + io = InputOutput() + + # Test with a non-Bedrock, non-Vertex AI model + from aider.models import check_for_dependencies + + check_for_dependencies(io, "gpt-4") + + # Verify check_pip_install_extra was not called + mock_check_pip.assert_not_called() + def test_get_repo_map_tokens(self): # Test default case (no max_input_tokens in info) model = Model("gpt-4") @@ -210,7 +322,7 @@ class TestModels(unittest.TestCase): self.assertTrue(model.use_repo_map) self.assertTrue(model.examples_as_sys_msg) self.assertFalse(model.use_temperature) - self.assertEqual(model.remove_reasoning, "think") + self.assertEqual(model.reasoning_tag, "think") # Test provider/deepseek-r1 case model = Model("someprovider/deepseek-r1") @@ -218,7 +330,7 @@ class TestModels(unittest.TestCase): self.assertTrue(model.use_repo_map) self.assertTrue(model.examples_as_sys_msg) self.assertFalse(model.use_temperature) - self.assertEqual(model.remove_reasoning, "think") + self.assertEqual(model.reasoning_tag, "think") # Test provider/deepseek-v3 case model = Model("anotherprovider/deepseek-v3") @@ -262,66 +374,6 @@ class TestModels(unittest.TestCase): self.assertEqual(model.editor_edit_format, "editor-diff") self.assertTrue(model.use_repo_map) - def test_remove_reasoning_content(self): - # Test with no removal configured - model = Model("gpt-4") - text = "Here is some reasoning and regular text" - self.assertEqual(model.remove_reasoning_content(text), text) - - # Test with removal configured - model = Model("deepseek-r1") # This model has remove_reasoning="think" - text = """Here is some text - -This is reasoning that should be removed -Over multiple lines - -And more text here""" - expected = """Here is some text - -And more text here""" - self.assertEqual(model.remove_reasoning_content(text), expected) - - # Test with multiple reasoning blocks - text = """Start -Block 1 -Middle -Block 2 -End""" - expected = """Start - -Middle - -End""" - self.assertEqual(model.remove_reasoning_content(text), expected) - - # Test with no reasoning blocks - text = "Just regular text" - self.assertEqual(model.remove_reasoning_content(text), text) - - @patch("aider.models.litellm.completion") - def test_simple_send_with_retries_removes_reasoning(self, mock_completion): - model = Model("deepseek-r1") # This model has remove_reasoning="think" - - # Mock the completion response - mock_response = MagicMock() - mock_response.choices = [MagicMock(message=MagicMock(content="""Here is some text - -This reasoning should be removed - -And this text should remain"""))] - mock_completion.return_value = mock_response - - messages = [{"role": "user", "content": "test"}] - result = model.simple_send_with_retries(messages) - - expected = """Here is some text - -And this text should remain""" - self.assertEqual(result, expected) - - # Verify the completion was called - mock_completion.assert_called_once() - def test_aider_extra_model_settings(self): import tempfile diff --git a/tests/basic/test_onboarding.py b/tests/basic/test_onboarding.py new file mode 100644 index 000000000..5c90a5648 --- /dev/null +++ b/tests/basic/test_onboarding.py @@ -0,0 +1,439 @@ +import argparse +import base64 +import hashlib +import os +import unittest +from unittest.mock import MagicMock, patch + +import requests + +# Import the functions to be tested +from aider.onboarding import ( + check_openrouter_tier, + exchange_code_for_key, + find_available_port, + generate_pkce_codes, + offer_openrouter_oauth, + select_default_model, + try_to_select_default_model, +) + + +# Mock the Analytics class as it's used in some functions +class DummyAnalytics: + def event(self, *args, **kwargs): + pass + + +# Mock the InputOutput class +class DummyIO: + def tool_output(self, *args, **kwargs): + pass + + def tool_warning(self, *args, **kwargs): + pass + + def tool_error(self, *args, **kwargs): + pass + + def confirm_ask(self, *args, **kwargs): + return False # Default to no confirmation + + def offer_url(self, *args, **kwargs): + pass + + +class TestOnboarding(unittest.TestCase): + @patch("requests.get") + def test_check_openrouter_tier_free(self, mock_get): + """Test check_openrouter_tier identifies free tier.""" + mock_response = MagicMock() + mock_response.json.return_value = {"data": {"is_free_tier": True}} + mock_response.raise_for_status.return_value = None + mock_get.return_value = mock_response + self.assertTrue(check_openrouter_tier("fake_key")) + mock_get.assert_called_once_with( + "https://openrouter.ai/api/v1/auth/key", + headers={"Authorization": "Bearer fake_key"}, + timeout=5, + ) + + @patch("requests.get") + def test_check_openrouter_tier_paid(self, mock_get): + """Test check_openrouter_tier identifies paid tier.""" + mock_response = MagicMock() + mock_response.json.return_value = {"data": {"is_free_tier": False}} + mock_response.raise_for_status.return_value = None + mock_get.return_value = mock_response + self.assertFalse(check_openrouter_tier("fake_key")) + + @patch("requests.get") + def test_check_openrouter_tier_api_error(self, mock_get): + """Test check_openrouter_tier defaults to free on API error.""" + mock_get.side_effect = requests.exceptions.RequestException("API Error") + self.assertTrue(check_openrouter_tier("fake_key")) + + @patch("requests.get") + def test_check_openrouter_tier_missing_key(self, mock_get): + """Test check_openrouter_tier defaults to free if key is missing in response.""" + mock_response = MagicMock() + mock_response.json.return_value = {"data": {}} # Missing 'is_free_tier' + mock_response.raise_for_status.return_value = None + mock_get.return_value = mock_response + self.assertTrue(check_openrouter_tier("fake_key")) + + @patch("aider.onboarding.check_openrouter_tier") + @patch.dict(os.environ, {}, clear=True) + def test_try_select_default_model_no_keys(self, mock_check_tier): + """Test no model is selected when no keys are present.""" + self.assertIsNone(try_to_select_default_model()) + mock_check_tier.assert_not_called() + + @patch("aider.onboarding.check_openrouter_tier", return_value=True) # Assume free tier + @patch.dict(os.environ, {"OPENROUTER_API_KEY": "or_key"}, clear=True) + def test_try_select_default_model_openrouter_free(self, mock_check_tier): + """Test OpenRouter free model selection.""" + self.assertEqual( + try_to_select_default_model(), "openrouter/google/gemini-2.5-pro-exp-03-25:free" + ) + mock_check_tier.assert_called_once_with("or_key") + + @patch("aider.onboarding.check_openrouter_tier", return_value=False) # Assume paid tier + @patch.dict(os.environ, {"OPENROUTER_API_KEY": "or_key"}, clear=True) + def test_try_select_default_model_openrouter_paid(self, mock_check_tier): + """Test OpenRouter paid model selection.""" + self.assertEqual(try_to_select_default_model(), "openrouter/anthropic/claude-3.7-sonnet") + mock_check_tier.assert_called_once_with("or_key") + + @patch("aider.onboarding.check_openrouter_tier") + @patch.dict(os.environ, {"ANTHROPIC_API_KEY": "an_key"}, clear=True) + def test_try_select_default_model_anthropic(self, mock_check_tier): + """Test Anthropic model selection.""" + self.assertEqual(try_to_select_default_model(), "sonnet") + mock_check_tier.assert_not_called() + + @patch("aider.onboarding.check_openrouter_tier") + @patch.dict(os.environ, {"DEEPSEEK_API_KEY": "ds_key"}, clear=True) + def test_try_select_default_model_deepseek(self, mock_check_tier): + """Test Deepseek model selection.""" + self.assertEqual(try_to_select_default_model(), "deepseek") + mock_check_tier.assert_not_called() + + @patch("aider.onboarding.check_openrouter_tier") + @patch.dict(os.environ, {"OPENAI_API_KEY": "oa_key"}, clear=True) + def test_try_select_default_model_openai(self, mock_check_tier): + """Test OpenAI model selection.""" + self.assertEqual(try_to_select_default_model(), "gpt-4o") + mock_check_tier.assert_not_called() + + @patch("aider.onboarding.check_openrouter_tier") + @patch.dict(os.environ, {"GEMINI_API_KEY": "gm_key"}, clear=True) + def test_try_select_default_model_gemini(self, mock_check_tier): + """Test Gemini model selection.""" + self.assertEqual(try_to_select_default_model(), "gemini/gemini-2.5-pro-exp-03-25") + mock_check_tier.assert_not_called() + + @patch("aider.onboarding.check_openrouter_tier") + @patch.dict(os.environ, {"VERTEXAI_PROJECT": "vx_proj"}, clear=True) + def test_try_select_default_model_vertex(self, mock_check_tier): + """Test Vertex AI model selection.""" + self.assertEqual(try_to_select_default_model(), "vertex_ai/gemini-2.5-pro-exp-03-25") + mock_check_tier.assert_not_called() + + @patch("aider.onboarding.check_openrouter_tier", return_value=False) # Paid + @patch.dict( + os.environ, {"OPENROUTER_API_KEY": "or_key", "OPENAI_API_KEY": "oa_key"}, clear=True + ) + def test_try_select_default_model_priority_openrouter(self, mock_check_tier): + """Test OpenRouter key takes priority.""" + self.assertEqual(try_to_select_default_model(), "openrouter/anthropic/claude-3.7-sonnet") + mock_check_tier.assert_called_once_with("or_key") + + @patch("aider.onboarding.check_openrouter_tier") + @patch.dict(os.environ, {"ANTHROPIC_API_KEY": "an_key", "OPENAI_API_KEY": "oa_key"}, clear=True) + def test_try_select_default_model_priority_anthropic(self, mock_check_tier): + """Test Anthropic key takes priority over OpenAI.""" + self.assertEqual(try_to_select_default_model(), "sonnet") + mock_check_tier.assert_not_called() + + @patch("socketserver.TCPServer") + def test_find_available_port_success(self, mock_tcp_server): + """Test finding an available port.""" + # Simulate port 8484 being available + mock_tcp_server.return_value.__enter__.return_value = None # Allow context manager + port = find_available_port(start_port=8484, end_port=8484) + self.assertEqual(port, 8484) + mock_tcp_server.assert_called_once_with(("localhost", 8484), None) + + @patch("socketserver.TCPServer") + def test_find_available_port_in_use(self, mock_tcp_server): + """Test finding the next available port if the first is in use.""" + # Simulate port 8484 raising OSError, 8485 being available + mock_tcp_server.side_effect = [OSError, MagicMock()] + mock_tcp_server.return_value.__enter__.return_value = None # Allow context manager + port = find_available_port(start_port=8484, end_port=8485) + self.assertEqual(port, 8485) + self.assertEqual(mock_tcp_server.call_count, 2) + mock_tcp_server.assert_any_call(("localhost", 8484), None) + mock_tcp_server.assert_any_call(("localhost", 8485), None) + + @patch("socketserver.TCPServer", side_effect=OSError) + def test_find_available_port_none_available(self, mock_tcp_server): + """Test returning None if no ports are available in the range.""" + port = find_available_port(start_port=8484, end_port=8485) + self.assertIsNone(port) + self.assertEqual(mock_tcp_server.call_count, 2) # Tried 8484 and 8485 + + def test_generate_pkce_codes(self): + """Test PKCE code generation.""" + verifier, challenge = generate_pkce_codes() + self.assertIsInstance(verifier, str) + self.assertIsInstance(challenge, str) + self.assertGreater(len(verifier), 40) # Check reasonable length + self.assertGreater(len(challenge), 40) + # Verify the challenge is the SHA256 hash of the verifier, base64 encoded + hasher = hashlib.sha256() + hasher.update(verifier.encode("utf-8")) + expected_challenge = base64.urlsafe_b64encode(hasher.digest()).rstrip(b"=").decode("utf-8") + self.assertEqual(challenge, expected_challenge) + + @patch("requests.post") + def test_exchange_code_for_key_success(self, mock_post): + """Test successful code exchange for API key.""" + mock_response = MagicMock() + mock_response.json.return_value = {"key": "test_api_key"} + mock_response.raise_for_status.return_value = None + mock_post.return_value = mock_response + io_mock = DummyIO() + + api_key = exchange_code_for_key("auth_code", "verifier", io_mock) + + self.assertEqual(api_key, "test_api_key") + mock_post.assert_called_once_with( + "https://openrouter.ai/api/v1/auth/keys", + headers={"Content-Type": "application/json"}, + json={ + "code": "auth_code", + "code_verifier": "verifier", + "code_challenge_method": "S256", + }, + timeout=30, + ) + + @patch("requests.post") + def test_exchange_code_for_key_missing_key(self, mock_post): + """Test code exchange when 'key' is missing in response.""" + mock_response = MagicMock() + mock_response.json.return_value = {"other_data": "value"} # Missing 'key' + mock_response.raise_for_status.return_value = None + mock_response.text = '{"other_data": "value"}' + mock_post.return_value = mock_response + io_mock = DummyIO() + io_mock.tool_error = MagicMock() # Track error output + + api_key = exchange_code_for_key("auth_code", "verifier", io_mock) + + self.assertIsNone(api_key) + io_mock.tool_error.assert_any_call("Error: 'key' not found in OpenRouter response.") + io_mock.tool_error.assert_any_call('Response: {"other_data": "value"}') + + @patch("requests.post") + def test_exchange_code_for_key_http_error(self, mock_post): + """Test code exchange with HTTP error.""" + mock_response = MagicMock() + mock_response.status_code = 400 + mock_response.reason = "Bad Request" + mock_response.text = '{"error": "invalid_code"}' + http_error = requests.exceptions.HTTPError(response=mock_response) + mock_post.side_effect = http_error + io_mock = DummyIO() + io_mock.tool_error = MagicMock() + + api_key = exchange_code_for_key("auth_code", "verifier", io_mock) + + self.assertIsNone(api_key) + io_mock.tool_error.assert_any_call( + "Error exchanging code for OpenRouter key: 400 Bad Request" + ) + io_mock.tool_error.assert_any_call('Response: {"error": "invalid_code"}') + + @patch("requests.post") + def test_exchange_code_for_key_timeout(self, mock_post): + """Test code exchange with timeout.""" + mock_post.side_effect = requests.exceptions.Timeout("Timeout") + io_mock = DummyIO() + io_mock.tool_error = MagicMock() + + api_key = exchange_code_for_key("auth_code", "verifier", io_mock) + + self.assertIsNone(api_key) + io_mock.tool_error.assert_called_once_with( + "Error: Request to OpenRouter timed out during code exchange." + ) + + @patch("requests.post") + def test_exchange_code_for_key_request_exception(self, mock_post): + """Test code exchange with general request exception.""" + req_exception = requests.exceptions.RequestException("Network Error") + mock_post.side_effect = req_exception + io_mock = DummyIO() + io_mock.tool_error = MagicMock() + + api_key = exchange_code_for_key("auth_code", "verifier", io_mock) + + self.assertIsNone(api_key) + io_mock.tool_error.assert_called_once_with( + f"Error exchanging code for OpenRouter key: {req_exception}" + ) + + # --- Tests for select_default_model --- + + @patch("aider.onboarding.try_to_select_default_model", return_value="gpt-4o") + @patch("aider.onboarding.offer_openrouter_oauth") + def test_select_default_model_already_specified(self, mock_offer_oauth, mock_try_select): + """Test select_default_model returns args.model if provided.""" + args = argparse.Namespace(model="specific-model") + io_mock = DummyIO() + analytics_mock = DummyAnalytics() + selected_model = select_default_model(args, io_mock, analytics_mock) + self.assertEqual(selected_model, "specific-model") + mock_try_select.assert_not_called() + mock_offer_oauth.assert_not_called() + + @patch("aider.onboarding.try_to_select_default_model", return_value="gpt-4o") + @patch("aider.onboarding.offer_openrouter_oauth") + def test_select_default_model_found_via_env(self, mock_offer_oauth, mock_try_select): + """Test select_default_model returns model found by try_to_select.""" + args = argparse.Namespace(model=None) # No model specified + io_mock = DummyIO() + io_mock.tool_warning = MagicMock() # Track warnings + analytics_mock = DummyAnalytics() + analytics_mock.event = MagicMock() # Track events + + selected_model = select_default_model(args, io_mock, analytics_mock) + + self.assertEqual(selected_model, "gpt-4o") + mock_try_select.assert_called_once() + io_mock.tool_warning.assert_called_once_with( + "Using gpt-4o model with API key from environment." + ) + analytics_mock.event.assert_called_once_with("auto_model_selection", model="gpt-4o") + mock_offer_oauth.assert_not_called() + + @patch( + "aider.onboarding.try_to_select_default_model", side_effect=[None, None] + ) # Fails first, fails after oauth attempt + @patch( + "aider.onboarding.offer_openrouter_oauth", return_value=False + ) # OAuth offered but fails/declined + def test_select_default_model_no_keys_oauth_fail(self, mock_offer_oauth, mock_try_select): + """Test select_default_model offers OAuth when no keys, but OAuth fails.""" + args = argparse.Namespace(model=None) + io_mock = DummyIO() + io_mock.tool_warning = MagicMock() + io_mock.offer_url = MagicMock() + analytics_mock = DummyAnalytics() + + selected_model = select_default_model(args, io_mock, analytics_mock) + + self.assertIsNone(selected_model) + self.assertEqual(mock_try_select.call_count, 2) # Called before and after oauth attempt + mock_offer_oauth.assert_called_once_with(io_mock, analytics_mock) + io_mock.tool_warning.assert_called_once_with( + "No LLM model was specified and no API keys were provided." + ) + io_mock.offer_url.assert_called_once() # Should offer docs URL + + @patch( + "aider.onboarding.try_to_select_default_model", + side_effect=[None, "openrouter/google/gemini-2.5-pro-exp-03-25:free"], + ) # Fails first, succeeds after oauth + @patch( + "aider.onboarding.offer_openrouter_oauth", return_value=True + ) # OAuth offered and succeeds + def test_select_default_model_no_keys_oauth_success(self, mock_offer_oauth, mock_try_select): + """Test select_default_model offers OAuth, which succeeds.""" + args = argparse.Namespace(model=None) + io_mock = DummyIO() + io_mock.tool_warning = MagicMock() + analytics_mock = DummyAnalytics() + + selected_model = select_default_model(args, io_mock, analytics_mock) + + self.assertEqual(selected_model, "openrouter/google/gemini-2.5-pro-exp-03-25:free") + self.assertEqual(mock_try_select.call_count, 2) # Called before and after oauth + mock_offer_oauth.assert_called_once_with(io_mock, analytics_mock) + # Only one warning is expected: "No LLM model..." + self.assertEqual(io_mock.tool_warning.call_count, 1) + io_mock.tool_warning.assert_called_once_with( + "No LLM model was specified and no API keys were provided." + ) + # The second call to try_select finds the model, so the *outer* function logs the usage. + # Note: The warning comes from the second call within select_default_model, + # not try_select itself. + # We verify the final state and model returned. + + # --- Tests for offer_openrouter_oauth --- + @patch("aider.onboarding.start_openrouter_oauth_flow", return_value="new_or_key") + @patch.dict(os.environ, {}, clear=True) # Ensure no key exists initially + def test_offer_openrouter_oauth_confirm_yes_success(self, mock_start_oauth): + """Test offer_openrouter_oauth when user confirms and OAuth succeeds.""" + io_mock = DummyIO() + io_mock.confirm_ask = MagicMock(return_value=True) # User says yes + analytics_mock = DummyAnalytics() + analytics_mock.event = MagicMock() + + result = offer_openrouter_oauth(io_mock, analytics_mock) + + self.assertTrue(result) + io_mock.confirm_ask.assert_called_once() + mock_start_oauth.assert_called_once_with(io_mock, analytics_mock) + self.assertEqual(os.environ.get("OPENROUTER_API_KEY"), "new_or_key") + analytics_mock.event.assert_any_call("oauth_flow_initiated", provider="openrouter") + analytics_mock.event.assert_any_call("oauth_flow_success") + # Clean up env var + del os.environ["OPENROUTER_API_KEY"] + + @patch("aider.onboarding.start_openrouter_oauth_flow", return_value=None) # OAuth fails + @patch.dict(os.environ, {}, clear=True) + def test_offer_openrouter_oauth_confirm_yes_fail(self, mock_start_oauth): + """Test offer_openrouter_oauth when user confirms but OAuth fails.""" + io_mock = DummyIO() + io_mock.confirm_ask = MagicMock(return_value=True) # User says yes + io_mock.tool_error = MagicMock() + analytics_mock = DummyAnalytics() + analytics_mock.event = MagicMock() + + result = offer_openrouter_oauth(io_mock, analytics_mock) + + self.assertFalse(result) + io_mock.confirm_ask.assert_called_once() + mock_start_oauth.assert_called_once_with(io_mock, analytics_mock) + self.assertNotIn("OPENROUTER_API_KEY", os.environ) + io_mock.tool_error.assert_called_once_with( + "OpenRouter authentication did not complete successfully." + ) + analytics_mock.event.assert_any_call("oauth_flow_initiated", provider="openrouter") + analytics_mock.event.assert_any_call("oauth_flow_failure") + + @patch("aider.onboarding.start_openrouter_oauth_flow") + def test_offer_openrouter_oauth_confirm_no(self, mock_start_oauth): + """Test offer_openrouter_oauth when user declines.""" + io_mock = DummyIO() + io_mock.confirm_ask = MagicMock(return_value=False) # User says no + analytics_mock = DummyAnalytics() + analytics_mock.event = MagicMock() + + result = offer_openrouter_oauth(io_mock, analytics_mock) + + self.assertFalse(result) + io_mock.confirm_ask.assert_called_once() + mock_start_oauth.assert_not_called() + analytics_mock.event.assert_not_called() # No OAuth events if declined + + # --- More complex test for start_openrouter_oauth_flow (simplified) --- + # This test focuses on the successful path, mocking heavily + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/basic/test_reasoning.py b/tests/basic/test_reasoning.py new file mode 100644 index 000000000..0386f29bc --- /dev/null +++ b/tests/basic/test_reasoning.py @@ -0,0 +1,609 @@ +import unittest +from unittest.mock import MagicMock, patch + +from aider.coders.base_coder import Coder +from aider.dump import dump # noqa +from aider.io import InputOutput +from aider.models import Model +from aider.reasoning_tags import ( + REASONING_END, + REASONING_START, + remove_reasoning_content, +) + + +class TestReasoning(unittest.TestCase): + def test_send_with_reasoning_content(self): + """Test that reasoning content is properly formatted and output.""" + # Setup IO with no pretty + io = InputOutput(pretty=False) + io.assistant_output = MagicMock() + + # Setup model and coder + model = Model("gpt-3.5-turbo") + coder = Coder.create(model, None, io=io, stream=False) + + # Test data + reasoning_content = "My step-by-step reasoning process" + main_content = "Final answer after reasoning" + + # Mock completion response with reasoning content + class MockCompletion: + def __init__(self, content, reasoning_content): + self.content = content + # Add required attributes expected by show_send_output + self.choices = [MagicMock()] + self.choices[0].message.content = content + self.choices[0].message.reasoning_content = reasoning_content + self.finish_reason = "stop" + + mock_completion = MockCompletion(main_content, reasoning_content) + + # Create a mock hash object + mock_hash = MagicMock() + mock_hash.hexdigest.return_value = "mock_hash_digest" + + # Mock the model's send_completion method to return the expected tuple format + with patch.object(model, "send_completion", return_value=(mock_hash, mock_completion)): + # Call send with a simple message + messages = [{"role": "user", "content": "test prompt"}] + list(coder.send(messages)) + + # Now verify ai_output was called with the right content + io.assistant_output.assert_called_once() + output = io.assistant_output.call_args[0][0] + + dump(output) + + # Output should contain formatted reasoning tags + self.assertIn(REASONING_START, output) + self.assertIn(REASONING_END, output) + + # Output should include both reasoning and main content + self.assertIn(reasoning_content, output) + self.assertIn(main_content, output) + + # Verify that partial_response_content only contains the main content + coder.remove_reasoning_content() + self.assertEqual(coder.partial_response_content.strip(), main_content.strip()) + + # Ensure proper order: reasoning first, then main content + reasoning_pos = output.find(reasoning_content) + main_pos = output.find(main_content) + self.assertLess( + reasoning_pos, main_pos, "Reasoning content should appear before main content" + ) + + def test_send_with_reasoning_content_stream(self): + """Test that streaming reasoning content is properly formatted and output.""" + # Setup IO with pretty output for streaming + io = InputOutput(pretty=True) + mock_mdstream = MagicMock() + io.get_assistant_mdstream = MagicMock(return_value=mock_mdstream) + + # Setup model and coder + model = Model("gpt-3.5-turbo") + coder = Coder.create(model, None, io=io, stream=True) + + # Ensure the coder shows pretty output + coder.show_pretty = MagicMock(return_value=True) + + # Mock streaming response chunks + class MockStreamingChunk: + def __init__( + self, content=None, reasoning_content=None, reasoning=None, finish_reason=None + ): + self.choices = [MagicMock()] + self.choices[0].delta = MagicMock() + self.choices[0].finish_reason = finish_reason + + # Set content if provided + if content is not None: + self.choices[0].delta.content = content + else: + # Need to handle attribute access that would raise AttributeError + delattr(self.choices[0].delta, "content") + + # Set reasoning_content if provided + if reasoning_content is not None: + self.choices[0].delta.reasoning_content = reasoning_content + else: + # Need to handle attribute access that would raise AttributeError + delattr(self.choices[0].delta, "reasoning_content") + + # Set reasoning if provided + if reasoning is not None: + self.choices[0].delta.reasoning = reasoning + else: + # Need to handle attribute access that would raise AttributeError + delattr(self.choices[0].delta, "reasoning") + + # Create chunks to simulate streaming + chunks = [ + # First chunk with reasoning content starts the tag + MockStreamingChunk(reasoning_content="My step-by-step "), + # Additional reasoning content + MockStreamingChunk(reasoning_content="reasoning process"), + # Switch to main content - this will automatically end the reasoning tag + MockStreamingChunk(content="Final "), + # More main content + MockStreamingChunk(content="answer "), + MockStreamingChunk(content="after reasoning"), + # End the response + MockStreamingChunk(finish_reason="stop"), + ] + + # Create a mock hash object + mock_hash = MagicMock() + mock_hash.hexdigest.return_value = "mock_hash_digest" + + # Mock the model's send_completion to return the hash and completion + with ( + patch.object(model, "send_completion", return_value=(mock_hash, chunks)), + patch.object(model, "token_count", return_value=10), + ): # Mock token count to avoid serialization issues + # Set mdstream directly on the coder object + coder.mdstream = mock_mdstream + + # Call send with a simple message + messages = [{"role": "user", "content": "test prompt"}] + list(coder.send(messages)) + + # Verify mdstream.update was called multiple times + mock_mdstream.update.assert_called() + + coder.live_incremental_response(True) + + # Explicitly get all calls to update + update_calls = mock_mdstream.update.call_args_list + + # There should be at least two calls - one for streaming and one final + self.assertGreaterEqual( + len(update_calls), 2, "Should have at least two calls to update (streaming + final)" + ) + + # Check that at least one call has final=True (should be the last one) + has_final_true = any(call[1].get("final", False) for call in update_calls) + self.assertTrue(has_final_true, "At least one update call should have final=True") + + # Get the text from the last update call + final_text = update_calls[-1][0][0] + + # The final text should include both reasoning and main content with proper formatting + self.assertIn(REASONING_START, final_text) + self.assertIn("My step-by-step reasoning process", final_text) + self.assertIn(REASONING_END, final_text) + self.assertIn("Final answer after reasoning", final_text) + + # Ensure proper order: reasoning first, then main content + reasoning_pos = final_text.find("My step-by-step reasoning process") + main_pos = final_text.find("Final answer after reasoning") + self.assertLess( + reasoning_pos, main_pos, "Reasoning content should appear before main content" + ) + + # Verify that partial_response_content only contains the main content + coder.remove_reasoning_content() + expected_content = "Final answer after reasoning" + self.assertEqual(coder.partial_response_content.strip(), expected_content) + + def test_send_with_think_tags(self): + """Test that tags are properly processed and formatted.""" + # Setup IO with no pretty + io = InputOutput(pretty=False) + io.assistant_output = MagicMock() + + # Setup model and coder + model = Model("gpt-3.5-turbo") + model.reasoning_tag = "think" # Set to remove tags + coder = Coder.create(model, None, io=io, stream=False) + + # Test data + reasoning_content = "My step-by-step reasoning process" + main_content = "Final answer after reasoning" + + # Create content with think tags + combined_content = f""" +{reasoning_content} + + +{main_content}""" + + # Mock completion response with think tags in content + class MockCompletion: + def __init__(self, content): + self.content = content + # Add required attributes expected by show_send_output + self.choices = [MagicMock()] + self.choices[0].message.content = content + self.choices[0].message.reasoning_content = None # No separate reasoning_content + self.finish_reason = "stop" + + mock_completion = MockCompletion(combined_content) + + # Create a mock hash object + mock_hash = MagicMock() + mock_hash.hexdigest.return_value = "mock_hash_digest" + + # Mock the model's send_completion method to return the expected tuple format + with patch.object(model, "send_completion", return_value=(mock_hash, mock_completion)): + # Call send with a simple message + messages = [{"role": "user", "content": "test prompt"}] + list(coder.send(messages)) + + # Now verify ai_output was called with the right content + io.assistant_output.assert_called_once() + output = io.assistant_output.call_args[0][0] + + dump(output) + + # Output should contain formatted reasoning tags + self.assertIn(REASONING_START, output) + self.assertIn(REASONING_END, output) + + # Output should include both reasoning and main content + self.assertIn(reasoning_content, output) + self.assertIn(main_content, output) + + # Ensure proper order: reasoning first, then main content + reasoning_pos = output.find(reasoning_content) + main_pos = output.find(main_content) + self.assertLess( + reasoning_pos, main_pos, "Reasoning content should appear before main content" + ) + + # Verify that partial_response_content only contains the main content + coder.remove_reasoning_content() + self.assertEqual(coder.partial_response_content.strip(), main_content.strip()) + + def test_send_with_think_tags_stream(self): + """Test that streaming with tags is properly processed and formatted.""" + # Setup IO with pretty output for streaming + io = InputOutput(pretty=True) + mock_mdstream = MagicMock() + io.get_assistant_mdstream = MagicMock(return_value=mock_mdstream) + + # Setup model and coder + model = Model("gpt-3.5-turbo") + model.reasoning_tag = "think" # Set to remove tags + coder = Coder.create(model, None, io=io, stream=True) + + # Ensure the coder shows pretty output + coder.show_pretty = MagicMock(return_value=True) + + # Mock streaming response chunks + class MockStreamingChunk: + def __init__( + self, content=None, reasoning_content=None, reasoning=None, finish_reason=None + ): + self.choices = [MagicMock()] + self.choices[0].delta = MagicMock() + self.choices[0].finish_reason = finish_reason + + # Set content if provided + if content is not None: + self.choices[0].delta.content = content + else: + # Need to handle attribute access that would raise AttributeError + delattr(self.choices[0].delta, "content") + + # Set reasoning_content if provided + if reasoning_content is not None: + self.choices[0].delta.reasoning_content = reasoning_content + else: + # Need to handle attribute access that would raise AttributeError + delattr(self.choices[0].delta, "reasoning_content") + + # Set reasoning if provided + if reasoning is not None: + self.choices[0].delta.reasoning = reasoning + else: + # Need to handle attribute access that would raise AttributeError + delattr(self.choices[0].delta, "reasoning") + + # Create chunks to simulate streaming with think tags + chunks = [ + # Start with open think tag + MockStreamingChunk(content="\n", reasoning_content=None), + # Reasoning content inside think tags + MockStreamingChunk(content="My step-by-step ", reasoning_content=None), + MockStreamingChunk(content="reasoning process\n", reasoning_content=None), + # Close think tag + MockStreamingChunk(content="\n\n", reasoning_content=None), + # Main content + MockStreamingChunk(content="Final ", reasoning_content=None), + MockStreamingChunk(content="answer ", reasoning_content=None), + MockStreamingChunk(content="after reasoning", reasoning_content=None), + # End the response + MockStreamingChunk(finish_reason="stop"), + ] + + # Create a mock hash object + mock_hash = MagicMock() + mock_hash.hexdigest.return_value = "mock_hash_digest" + + # Mock the model's send_completion to return the hash and completion + with patch.object(model, "send_completion", return_value=(mock_hash, chunks)): + # Set mdstream directly on the coder object + coder.mdstream = mock_mdstream + + # Call send with a simple message + messages = [{"role": "user", "content": "test prompt"}] + list(coder.send(messages)) + + # Verify mdstream.update was called multiple times + mock_mdstream.update.assert_called() + + coder.live_incremental_response(True) + + # Explicitly get all calls to update + update_calls = mock_mdstream.update.call_args_list + + # There should be at least two calls - one for streaming and one final + self.assertGreaterEqual( + len(update_calls), 2, "Should have at least two calls to update (streaming + final)" + ) + + # Check that at least one call has final=True (should be the last one) + has_final_true = any(call[1].get("final", False) for call in update_calls) + self.assertTrue(has_final_true, "At least one update call should have final=True") + + # Get the text from the last update call + final_text = update_calls[-1][0][0] + + # The final text should include both reasoning and main content with proper formatting + self.assertIn(REASONING_START, final_text) + self.assertIn("My step-by-step reasoning process", final_text) + self.assertIn(REASONING_END, final_text) + self.assertIn("Final answer after reasoning", final_text) + + # Ensure proper order: reasoning first, then main content + reasoning_pos = final_text.find("My step-by-step reasoning process") + main_pos = final_text.find("Final answer after reasoning") + self.assertLess( + reasoning_pos, main_pos, "Reasoning content should appear before main content" + ) + + def test_remove_reasoning_content(self): + """Test the remove_reasoning_content function from reasoning_tags module.""" + # Test with no removal configured + text = "Here is some reasoning and regular text" + self.assertEqual(remove_reasoning_content(text, None), text) + + # Test with removal configured + text = """Here is some text + +This is reasoning that should be removed +Over multiple lines + +And more text here""" + expected = """Here is some text + +And more text here""" + self.assertEqual(remove_reasoning_content(text, "think"), expected) + + # Test with multiple reasoning blocks + text = """Start +Block 1 +Middle +Block 2 +End""" + expected = """Start + +Middle + +End""" + self.assertEqual(remove_reasoning_content(text, "think"), expected) + + # Test with no reasoning blocks + text = "Just regular text" + self.assertEqual(remove_reasoning_content(text, "think"), text) + + def test_send_with_reasoning(self): + """Test that reasoning content from the 'reasoning' attribute is properly formatted + and output.""" + # Setup IO with no pretty + io = InputOutput(pretty=False) + io.assistant_output = MagicMock() + + # Setup model and coder + model = Model("gpt-3.5-turbo") + coder = Coder.create(model, None, io=io, stream=False) + + # Test data + reasoning_content = "My step-by-step reasoning process" + main_content = "Final answer after reasoning" + + # Mock completion response with reasoning content + class MockCompletion: + def __init__(self, content, reasoning): + self.content = content + # Add required attributes expected by show_send_output + self.choices = [MagicMock()] + self.choices[0].message.content = content + self.choices[0].message.reasoning = ( + reasoning # Using reasoning instead of reasoning_content + ) + delattr(self.choices[0].message, "reasoning_content") + self.finish_reason = "stop" + + mock_completion = MockCompletion(main_content, reasoning_content) + + # Create a mock hash object + mock_hash = MagicMock() + mock_hash.hexdigest.return_value = "mock_hash_digest" + + # Mock the model's send_completion method to return the expected tuple format + with patch.object(model, "send_completion", return_value=(mock_hash, mock_completion)): + # Call send with a simple message + messages = [{"role": "user", "content": "test prompt"}] + list(coder.send(messages)) + + # Now verify ai_output was called with the right content + io.assistant_output.assert_called_once() + output = io.assistant_output.call_args[0][0] + + dump(output) + + # Output should contain formatted reasoning tags + self.assertIn(REASONING_START, output) + self.assertIn(REASONING_END, output) + + # Output should include both reasoning and main content + self.assertIn(reasoning_content, output) + self.assertIn(main_content, output) + + # Verify that partial_response_content only contains the main content + coder.remove_reasoning_content() + self.assertEqual(coder.partial_response_content.strip(), main_content.strip()) + + # Ensure proper order: reasoning first, then main content + reasoning_pos = output.find(reasoning_content) + main_pos = output.find(main_content) + self.assertLess( + reasoning_pos, main_pos, "Reasoning content should appear before main content" + ) + + def test_send_with_reasoning_stream(self): + """Test that streaming reasoning content from the 'reasoning' attribute is properly + formatted and output.""" + # Setup IO with pretty output for streaming + io = InputOutput(pretty=True) + mock_mdstream = MagicMock() + io.get_assistant_mdstream = MagicMock(return_value=mock_mdstream) + + # Setup model and coder + model = Model("gpt-3.5-turbo") + coder = Coder.create(model, None, io=io, stream=True) + + # Ensure the coder shows pretty output + coder.show_pretty = MagicMock(return_value=True) + + # Mock streaming response chunks + class MockStreamingChunk: + def __init__( + self, content=None, reasoning_content=None, reasoning=None, finish_reason=None + ): + self.choices = [MagicMock()] + self.choices[0].delta = MagicMock() + self.choices[0].finish_reason = finish_reason + + # Set content if provided + if content is not None: + self.choices[0].delta.content = content + else: + # Need to handle attribute access that would raise AttributeError + delattr(self.choices[0].delta, "content") + + # Set reasoning_content if provided + if reasoning_content is not None: + self.choices[0].delta.reasoning_content = reasoning_content + else: + # Need to handle attribute access that would raise AttributeError + delattr(self.choices[0].delta, "reasoning_content") + + # Set reasoning if provided + if reasoning is not None: + self.choices[0].delta.reasoning = reasoning + else: + # Need to handle attribute access that would raise AttributeError + delattr(self.choices[0].delta, "reasoning") + + # Create chunks to simulate streaming - using reasoning attribute instead of + # reasoning_content + chunks = [ + # First chunk with reasoning content starts the tag + MockStreamingChunk(reasoning="My step-by-step "), + # Additional reasoning content + MockStreamingChunk(reasoning="reasoning process"), + # Switch to main content - this will automatically end the reasoning tag + MockStreamingChunk(content="Final "), + # More main content + MockStreamingChunk(content="answer "), + MockStreamingChunk(content="after reasoning"), + # End the response + MockStreamingChunk(finish_reason="stop"), + ] + + # Create a mock hash object + mock_hash = MagicMock() + mock_hash.hexdigest.return_value = "mock_hash_digest" + + # Mock the model's send_completion to return the hash and completion + with ( + patch.object(model, "send_completion", return_value=(mock_hash, chunks)), + patch.object(model, "token_count", return_value=10), + ): # Mock token count to avoid serialization issues + # Set mdstream directly on the coder object + coder.mdstream = mock_mdstream + + # Call send with a simple message + messages = [{"role": "user", "content": "test prompt"}] + list(coder.send(messages)) + + # Verify mdstream.update was called multiple times + mock_mdstream.update.assert_called() + + coder.live_incremental_response(True) + + # Explicitly get all calls to update + update_calls = mock_mdstream.update.call_args_list + + # There should be at least two calls - one for streaming and one final + self.assertGreaterEqual( + len(update_calls), 2, "Should have at least two calls to update (streaming + final)" + ) + + # Check that at least one call has final=True (should be the last one) + has_final_true = any(call[1].get("final", False) for call in update_calls) + self.assertTrue(has_final_true, "At least one update call should have final=True") + + # Get the text from the last update call + final_text = update_calls[-1][0][0] + + # The final text should include both reasoning and main content with proper formatting + self.assertIn(REASONING_START, final_text) + self.assertIn("My step-by-step reasoning process", final_text) + self.assertIn(REASONING_END, final_text) + self.assertIn("Final answer after reasoning", final_text) + + # Ensure proper order: reasoning first, then main content + reasoning_pos = final_text.find("My step-by-step reasoning process") + main_pos = final_text.find("Final answer after reasoning") + self.assertLess( + reasoning_pos, main_pos, "Reasoning content should appear before main content" + ) + + # Verify that partial_response_content only contains the main content + coder.remove_reasoning_content() + expected_content = "Final answer after reasoning" + self.assertEqual(coder.partial_response_content.strip(), expected_content) + + @patch("aider.models.litellm.completion") + def test_simple_send_with_retries_removes_reasoning(self, mock_completion): + """Test that simple_send_with_retries correctly removes reasoning content.""" + model = Model("deepseek-r1") # This model has reasoning_tag="think" + + # Mock the completion response + mock_response = MagicMock() + mock_response.choices = [MagicMock(message=MagicMock(content="""Here is some text + +This reasoning should be removed + +And this text should remain"""))] + mock_completion.return_value = mock_response + + messages = [{"role": "user", "content": "test"}] + result = model.simple_send_with_retries(messages) + + expected = """Here is some text + +And this text should remain""" + self.assertEqual(result, expected) + + # Verify the completion was called + mock_completion.assert_called_once() + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/basic/test_repo.py b/tests/basic/test_repo.py index d16bee2fd..aa863c570 100644 --- a/tests/basic/test_repo.py +++ b/tests/basic/test_repo.py @@ -405,3 +405,51 @@ class TestRepo(unittest.TestCase): git_repo = GitRepo(InputOutput(), None, None) git_repo.commit(fnames=[str(fname)]) + + def test_git_commit_verify(self): + """Test that git_commit_verify controls whether --no-verify is passed to git commit""" + # Skip on Windows as hook execution works differently + if platform.system() == "Windows": + return + + with GitTemporaryDirectory(): + # Create a new repo + raw_repo = git.Repo() + + # Create a file to commit + fname = Path("test_file.txt") + fname.write_text("initial content") + raw_repo.git.add(str(fname)) + + # Do the initial commit + raw_repo.git.commit("-m", "Initial commit") + + # Now create a pre-commit hook that always fails + hooks_dir = Path(raw_repo.git_dir) / "hooks" + hooks_dir.mkdir(exist_ok=True) + + pre_commit_hook = hooks_dir / "pre-commit" + pre_commit_hook.write_text("#!/bin/sh\nexit 1\n") # Always fail + pre_commit_hook.chmod(0o755) # Make executable + + # Modify the file + fname.write_text("modified content") + + # Create GitRepo with verify=True (default) + io = InputOutput() + git_repo_verify = GitRepo(io, None, None, git_commit_verify=True) + + # Attempt to commit - should fail due to pre-commit hook + commit_result = git_repo_verify.commit(fnames=[str(fname)], message="Should fail") + self.assertIsNone(commit_result) + + # Create GitRepo with verify=False + git_repo_no_verify = GitRepo(io, None, None, git_commit_verify=False) + + # Attempt to commit - should succeed by bypassing the hook + commit_result = git_repo_no_verify.commit(fnames=[str(fname)], message="Should succeed") + self.assertIsNotNone(commit_result) + + # Verify the commit was actually made + latest_commit_msg = raw_repo.head.commit.message + self.assertEqual(latest_commit_msg.strip(), "Should succeed") diff --git a/tests/basic/test_repomap.py b/tests/basic/test_repomap.py index d5770b2c1..17a5753c3 100644 --- a/tests/basic/test_repomap.py +++ b/tests/basic/test_repomap.py @@ -282,74 +282,145 @@ class TestRepoMapTypescript(unittest.TestCase): class TestRepoMapAllLanguages(unittest.TestCase): def setUp(self): self.GPT35 = Model("gpt-3.5-turbo") + self.fixtures_dir = Path(__file__).parent.parent / "fixtures" / "languages" - def test_get_repo_map_all_languages(self): - language_files = { - "c": ("c", "main"), - "cpp": ("cpp", "main"), - "elixir": ("ex", "Greeter"), - "java": ("java", "Greeting"), - "javascript": ("js", "Person"), - "kotlin": ("kt", "Greeting"), - "ocaml": ("ml", "Greeter"), - "php": ("php", "greet"), - "python": ("py", "Person"), - "ql": ("ql", "greet"), - "ruby": ("rb", "greet"), - "rust": ("rs", "Person"), - "typescript": ("ts", "greet"), - "tsx": ("tsx", "UserProps"), - "csharp": ("cs", "IGreeter"), - "elisp": ("el", "greeter"), - "elm": ("elm", "Person"), - "go": ("go", "Greeter"), - "hcl": ("tf", "aws_vpc"), - } + def test_language_c(self): + self._test_language_repo_map("c", "c", "main") - fixtures_dir = Path(__file__).parent.parent / "fixtures" / "languages" + def test_language_cpp(self): + self._test_language_repo_map("cpp", "cpp", "main") - for lang, key_symbol in language_files.items(): - # Get the fixture file path and name based on language - fixture_dir = fixtures_dir / lang - ext, key_symbol = language_files[lang] - filename = f"test.{ext}" - fixture_path = fixture_dir / filename - self.assertTrue( - fixture_path.exists(), f"Fixture file missing for {lang}: {fixture_path}" + def test_language_d(self): + self._test_language_repo_map("d", "d", "main") + + def test_language_dart(self): + self._test_language_repo_map("dart", "dart", "Person") + + def test_language_elixir(self): + self._test_language_repo_map("elixir", "ex", "Greeter") + + def test_language_gleam(self): + self._test_language_repo_map("gleam", "gleam", "greet") + + def test_language_java(self): + self._test_language_repo_map("java", "java", "Greeting") + + def test_language_javascript(self): + self._test_language_repo_map("javascript", "js", "Person") + + def test_language_kotlin(self): + self._test_language_repo_map("kotlin", "kt", "Greeting") + + def test_language_lua(self): + self._test_language_repo_map("lua", "lua", "greet") + + # "ocaml": ("ml", "Greeter"), # not supported in tsl-pack (yet?) + + def test_language_php(self): + self._test_language_repo_map("php", "php", "greet") + + def test_language_python(self): + self._test_language_repo_map("python", "py", "Person") + + # "ql": ("ql", "greet"), # not supported in tsl-pack (yet?) + + def test_language_ruby(self): + self._test_language_repo_map("ruby", "rb", "greet") + + def test_language_rust(self): + self._test_language_repo_map("rust", "rs", "Person") + + def test_language_typescript(self): + self._test_language_repo_map("typescript", "ts", "greet") + + def test_language_tsx(self): + self._test_language_repo_map("tsx", "tsx", "UserProps") + + def test_language_csharp(self): + self._test_language_repo_map("csharp", "cs", "IGreeter") + + def test_language_elisp(self): + self._test_language_repo_map("elisp", "el", "greeter") + + def test_language_elm(self): + self._test_language_repo_map("elm", "elm", "Person") + + def test_language_go(self): + self._test_language_repo_map("go", "go", "Greeter") + + def test_language_hcl(self): + self._test_language_repo_map("hcl", "tf", "aws_vpc") + + def test_language_arduino(self): + self._test_language_repo_map("arduino", "ino", "setup") + + def test_language_chatito(self): + self._test_language_repo_map("chatito", "chatito", "intent") + + def test_language_commonlisp(self): + self._test_language_repo_map("commonlisp", "lisp", "greet") + + def test_language_pony(self): + self._test_language_repo_map("pony", "pony", "Greeter") + + def test_language_properties(self): + self._test_language_repo_map("properties", "properties", "database.url") + + def test_language_r(self): + self._test_language_repo_map("r", "r", "calculate") + + def test_language_racket(self): + self._test_language_repo_map("racket", "rkt", "greet") + + def test_language_solidity(self): + self._test_language_repo_map("solidity", "sol", "SimpleStorage") + + def test_language_swift(self): + self._test_language_repo_map("swift", "swift", "Greeter") + + def test_language_udev(self): + self._test_language_repo_map("udev", "rules", "USB_DRIVER") + + def test_language_scala(self): + self._test_language_repo_map("scala", "scala", "Greeter") + + def _test_language_repo_map(self, lang, key, symbol): + """Helper method to test repo map generation for a specific language.""" + # Get the fixture file path and name based on language + fixture_dir = self.fixtures_dir / lang + filename = f"test.{key}" + fixture_path = fixture_dir / filename + self.assertTrue(fixture_path.exists(), f"Fixture file missing for {lang}: {fixture_path}") + + # Read the fixture content + with open(fixture_path, "r", encoding="utf-8") as f: + content = f.read() + with GitTemporaryDirectory() as temp_dir: + test_file = os.path.join(temp_dir, filename) + with open(test_file, "w", encoding="utf-8") as f: + f.write(content) + + io = InputOutput() + repo_map = RepoMap(main_model=self.GPT35, root=temp_dir, io=io) + other_files = [test_file] + result = repo_map.get_repo_map([], other_files) + dump(lang) + dump(result) + + self.assertGreater(len(result.strip().splitlines()), 1) + + # Check if the result contains all the expected files and symbols + self.assertIn( + filename, result, f"File for language {lang} not found in repo map: {result}" + ) + self.assertIn( + symbol, + result, + f"Key symbol '{symbol}' for language {lang} not found in repo map: {result}", ) - # Read the fixture content - with open(fixture_path, "r", encoding="utf-8") as f: - content = f.read() - with GitTemporaryDirectory() as temp_dir: - test_file = os.path.join(temp_dir, filename) - with open(test_file, "w", encoding="utf-8") as f: - f.write(content) - - io = InputOutput() - repo_map = RepoMap(main_model=self.GPT35, root=temp_dir, io=io) - other_files = [filename] - result = repo_map.get_repo_map([], other_files) - dump(lang) - dump(result) - - self.assertGreater(len(result.strip().splitlines()), 1) - - # Check if the result contains all the expected files and symbols - self.assertIn( - filename, result, f"File for language {lang} not found in repo map: {result}" - ) - self.assertIn( - key_symbol, - result, - ( - f"Key symbol '{key_symbol}' for language {lang} not found in repo map:" - f" {result}" - ), - ) - - # close the open cache files, so Windows won't error - del repo_map + # close the open cache files, so Windows won't error + del repo_map def test_repo_map_sample_code_base(self): # Path to the sample code base diff --git a/tests/basic/test_sanity_check_repo.py b/tests/basic/test_sanity_check_repo.py index fd17fb3b4..860572ec5 100644 --- a/tests/basic/test_sanity_check_repo.py +++ b/tests/basic/test_sanity_check_repo.py @@ -8,8 +8,6 @@ from git import GitError, Repo from aider import urls from aider.main import sanity_check_repo -from aider.repo import GitRepo -from aider.io import InputOutput @pytest.fixture @@ -184,41 +182,3 @@ def test_sanity_check_repo_with_no_repo(mock_io): # Assert that no errors or outputs were logged mock_io.tool_error.assert_not_called() mock_io.tool_output.assert_not_called() - - -def corrupt_git_index(repo_path): - index_path = os.path.join(repo_path, ".git", "index") - with open(index_path, "r+b") as f: - # Verify the file has the correct signature - signature = f.read(4) - if signature != b"DIRC": - raise ValueError("Invalid git index file signature.") - - # Seek to the data section and inject invalid bytes to simulate encoding error - f.seek(77) - f.write(b"\xF5" * 5) - - -def test_sanity_check_repo_with_corrupt_index(create_repo, mock_io): - repo_path, repo = create_repo - # Corrupt the Git index file - corrupt_git_index(repo_path) - - # Create GitRepo instance - git_repo = GitRepo(InputOutput(), None, repo_path) - - # Call the function - result = sanity_check_repo(git_repo, mock_io) - - # Assert that the function returns False - assert result is False - - # Assert that the appropriate error messages were logged - mock_io.tool_error.assert_called_with("Unable to read git repository, it may be corrupt?") - mock_io.tool_output.assert_called_with( - ( - "Failed to read the Git repository. This issue is likely caused by a path encoded " - "in a format different from the expected encoding \"utf-8\".\n" - "Internal error: 'utf-8' codec can't decode byte 0xf5 in position 3: invalid start byte" - ) - ) diff --git a/tests/basic/test_ssl_verification.py b/tests/basic/test_ssl_verification.py new file mode 100644 index 000000000..3a12f8581 --- /dev/null +++ b/tests/basic/test_ssl_verification.py @@ -0,0 +1,84 @@ +import os +from unittest import TestCase +from unittest.mock import MagicMock, patch + +from prompt_toolkit.input import DummyInput +from prompt_toolkit.output import DummyOutput + +from aider.main import main + + +class TestSSLVerification(TestCase): + def setUp(self): + self.original_env = os.environ.copy() + os.environ["OPENAI_API_KEY"] = "test-key" + os.environ["AIDER_CHECK_UPDATE"] = "false" + os.environ["AIDER_ANALYTICS"] = "false" + + def tearDown(self): + os.environ.clear() + os.environ.update(self.original_env) + + @patch("aider.io.InputOutput.offer_url") + @patch("aider.models.ModelInfoManager.set_verify_ssl") + @patch("aider.llm.litellm._load_litellm") + @patch("httpx.Client") + @patch("httpx.AsyncClient") + @patch("aider.models.fuzzy_match_models", return_value=[]) + def test_no_verify_ssl_flag_sets_model_info_manager( + self, + mock_fuzzy_match, + mock_async_client, + mock_client, + mock_load_litellm, + mock_set_verify_ssl, + mock_offer_url, + ): + # Prevent actual URL opening + mock_offer_url.return_value = False + # Mock the litellm._lazy_module to avoid AttributeError + mock_load_litellm.return_value = None + mock_module = MagicMock() + + # Mock Model class to avoid actual model initialization + with patch("aider.models.Model") as mock_model: + # Configure the mock to avoid the TypeError + mock_model.return_value.info = {} + mock_model.return_value.validate_environment.return_value = { + "missing_keys": [], + "keys_in_environment": [], + } + + with patch("aider.llm.litellm._lazy_module", mock_module): + # Run main with --no-verify-ssl flag + main( + ["--no-verify-ssl", "--exit", "--yes"], + input=DummyInput(), + output=DummyOutput(), + ) + + # Verify model_info_manager.set_verify_ssl was called with False + mock_set_verify_ssl.assert_called_once_with(False) + + # Verify httpx clients were created with verify=False + mock_client.assert_called_once_with(verify=False) + mock_async_client.assert_called_once_with(verify=False) + + # Verify SSL_VERIFY environment variable was set to empty string + self.assertEqual(os.environ.get("SSL_VERIFY"), "") + + @patch("aider.io.InputOutput.offer_url") + @patch("aider.models.model_info_manager.set_verify_ssl") + def test_default_ssl_verification(self, mock_set_verify_ssl, mock_offer_url): + # Prevent actual URL opening + mock_offer_url.return_value = False + # Run main without --no-verify-ssl flag + with patch("aider.main.InputOutput"): + with patch("aider.coders.Coder.create"): + main(["--exit", "--yes"], input=DummyInput(), output=DummyOutput()) + + # Verify model_info_manager.set_verify_ssl was not called + mock_set_verify_ssl.assert_not_called() + + # Verify SSL_VERIFY environment variable was not set + self.assertNotIn("SSL_VERIFY", os.environ) diff --git a/tests/basic/test_watch.py b/tests/basic/test_watch.py index 706fd1ade..524f80e08 100644 --- a/tests/basic/test_watch.py +++ b/tests/basic/test_watch.py @@ -155,3 +155,12 @@ def test_ai_comment_pattern(): assert ( question_js_has_bang == "?" ), "Expected at least one bang (!) comment in watch_question.js fixture" + + # Test Lisp fixture + lisp_path = fixtures_dir / "watch.lisp" + lisp_lines, lisp_comments, lisp_has_bang = watcher.get_ai_comments(str(lisp_path)) + lisp_expected = 7 + assert ( + len(lisp_lines) == lisp_expected + ), f"Expected {lisp_expected} AI comments in Lisp fixture, found {len(lisp_lines)}" + assert lisp_has_bang == "!", "Expected at least one bang (!) comment in Lisp fixture" diff --git a/tests/fixtures/languages/arduino/test.ino b/tests/fixtures/languages/arduino/test.ino new file mode 100644 index 000000000..524b91102 --- /dev/null +++ b/tests/fixtures/languages/arduino/test.ino @@ -0,0 +1,21 @@ +// Simple Arduino sketch + +void setup() { + // Initialize serial communication + Serial.begin(9600); + pinMode(LED_BUILTIN, OUTPUT); +} + +void loop() { + // Main code that runs repeatedly + digitalWrite(LED_BUILTIN, HIGH); + delay(1000); + digitalWrite(LED_BUILTIN, LOW); + delay(1000); + Serial.println("Blinking LED"); +} + +// A custom function +int calculateDelay(int baseDelay, int multiplier) { + return baseDelay * multiplier; +} diff --git a/tests/fixtures/languages/c/test.c b/tests/fixtures/languages/c/test.c index f26b97c98..8031a1f0b 100644 --- a/tests/fixtures/languages/c/test.c +++ b/tests/fixtures/languages/c/test.c @@ -4,3 +4,18 @@ int main() { printf("Hello, World!\n"); return 0; } +#include + +/** + * The main entry point of the program + * @return 0 on success + */ +int main(int argc, char **argv) { + printf("Hello, World!\n"); + return 0; +} + +// Helper function +void print_message(const char *message) { + printf("%s\n", message); +} diff --git a/tests/fixtures/languages/chatito/test.chatito b/tests/fixtures/languages/chatito/test.chatito new file mode 100644 index 000000000..9240ba459 --- /dev/null +++ b/tests/fixtures/languages/chatito/test.chatito @@ -0,0 +1,20 @@ +%[intent]('training': '60', 'testing': '40') + ~[greet] + ~[greet] @[name?] ~[endPolite?] + +%[name]('training': '50', 'testing': '50') + John + Anna + Bob + Sarah + +~[greet] + hi + hello + hey + greetings + +~[endPolite] + please + thanks + thank you diff --git a/tests/fixtures/languages/commonlisp/test.lisp b/tests/fixtures/languages/commonlisp/test.lisp new file mode 100644 index 000000000..5cf2173cd --- /dev/null +++ b/tests/fixtures/languages/commonlisp/test.lisp @@ -0,0 +1,17 @@ +;;; Simple Common Lisp example + +(defun greet (name) + "Return a greeting string for NAME." + (format nil "Hello, ~a!" name)) + +(defvar *greeting-style* 'formal + "Style to use for greetings.") + +(defclass person () + ((name :initarg :name :accessor person-name) + (age :initarg :age :accessor person-age)) + (:documentation "A class representing a person.")) + +(defmethod print-object ((obj person) stream) + (print-unreadable-object (obj stream :type t) + (format stream "~a, age ~a" (person-name obj) (person-age obj)))) diff --git a/tests/fixtures/languages/d/test.d b/tests/fixtures/languages/d/test.d new file mode 100644 index 000000000..6f4c57c75 --- /dev/null +++ b/tests/fixtures/languages/d/test.d @@ -0,0 +1,26 @@ +import std.stdio; + +/** + * Main function for the D language test file. + */ +void main() { + writeln("Hello, D language!"); + + auto greeter = new Greeter("World"); + writeln(greeter.greet()); +} + +/** + * A simple greeter class in D + */ +class Greeter { + private string name; + + this(string name) { + this.name = name; + } + + string greet() { + return "Hello, " ~ name ~ "!"; + } +} diff --git a/tests/fixtures/languages/dart/test.dart b/tests/fixtures/languages/dart/test.dart new file mode 100644 index 000000000..ae299df9d --- /dev/null +++ b/tests/fixtures/languages/dart/test.dart @@ -0,0 +1,21 @@ +// A simple Dart class for testing ctags detection +class Person { + String name; + int age; + + Person(this.name, this.age); + + void greet() { + print('Hello, my name is $name and I am $age years old.'); + } + + bool isAdult() { + return age >= 18; + } +} + +void main() { + var person = Person('John', 30); + person.greet(); + print('Is adult: ${person.isAdult()}'); +} diff --git a/tests/fixtures/languages/elm/test.elm b/tests/fixtures/languages/elm/test.elm index 7784f60eb..e78412b1a 100644 --- a/tests/fixtures/languages/elm/test.elm +++ b/tests/fixtures/languages/elm/test.elm @@ -36,3 +36,24 @@ main = div [ class "greeting" ] [ text (greet Formal defaultPerson) ] +module Main exposing (..) + +-- Define a Person type +type alias Person = + { name : String + , age : Int + } + +-- Create a person +newPerson : String -> Int -> Person +newPerson name age = + { name = name + , age = age + } + +-- Main function +main = + let + person = newPerson "John Doe" 30 + in + text ("Hello, " ++ person.name) diff --git a/tests/fixtures/languages/gleam/test.gleam b/tests/fixtures/languages/gleam/test.gleam new file mode 100644 index 000000000..f0c5aa32e --- /dev/null +++ b/tests/fixtures/languages/gleam/test.gleam @@ -0,0 +1,10 @@ +import gleam/io + +pub fn greet(name: String) -> String { + "Hello, " <> name <> "!" +} + +pub fn main() { + greet("World") + |> io.println +} diff --git a/tests/fixtures/languages/lua/test.lua b/tests/fixtures/languages/lua/test.lua new file mode 100644 index 000000000..7ef930f11 --- /dev/null +++ b/tests/fixtures/languages/lua/test.lua @@ -0,0 +1,25 @@ +-- Simple Lua module with a greeting function + +-- Person class definition +local Person = {} +Person.__index = Person + +function Person.new(name) + local self = setmetatable({}, Person) + self.name = name + return self +end + +-- Main greeting function to be detected by ctags +function greet(person) + return "Hello, " .. person.name .. "!" +end + +-- Example usage +local p = Person.new("World") +print(greet(p)) + +return { + Person = Person, + greet = greet +} diff --git a/tests/fixtures/languages/pony/test.pony b/tests/fixtures/languages/pony/test.pony new file mode 100644 index 000000000..799ad861b --- /dev/null +++ b/tests/fixtures/languages/pony/test.pony @@ -0,0 +1,8 @@ +class Greeter + fun greet(name: String): String => + "Hello, " + name + "!" + +actor Main + new create(env: Env) => + let greeter = Greeter + env.out.print(greeter.greet("Pony")) diff --git a/tests/fixtures/languages/properties/test.properties b/tests/fixtures/languages/properties/test.properties new file mode 100644 index 000000000..e41c40c47 --- /dev/null +++ b/tests/fixtures/languages/properties/test.properties @@ -0,0 +1,14 @@ +# Database Configuration +database.url=jdbc:mysql://localhost:3306/mydb +database.username=admin +database.password=secret + +# Application Settings +app.name=My Application +app.version=1.0.0 +app.debug=true + +# Server Configuration +server.port=8080 +server.host=localhost +server.maxConnections=100 diff --git a/tests/fixtures/languages/r/test.r b/tests/fixtures/languages/r/test.r new file mode 100644 index 000000000..191881e76 --- /dev/null +++ b/tests/fixtures/languages/r/test.r @@ -0,0 +1,17 @@ +# Simple R function for testing repository mapping +calculate <- function(x, y) { + # This function performs a simple calculation + result <- x * y + return(result) +} + +# Another function to test detection +process_data <- function(data) { + # Process some data + return(data * 2) +} + +# Example usage +sample_data <- c(1, 2, 3, 4, 5) +result <- calculate(10, 5) +processed <- process_data(sample_data) diff --git a/tests/fixtures/languages/racket/test.rkt b/tests/fixtures/languages/racket/test.rkt new file mode 100644 index 000000000..05be192cf --- /dev/null +++ b/tests/fixtures/languages/racket/test.rkt @@ -0,0 +1,8 @@ +#lang racket + +;; Define a simple greeting function +(define (greet name) + (string-append "Hello, " name "!")) + +;; Example usage +(greet "World") diff --git a/tests/fixtures/languages/scala/test.scala b/tests/fixtures/languages/scala/test.scala new file mode 100644 index 000000000..3300aa299 --- /dev/null +++ b/tests/fixtures/languages/scala/test.scala @@ -0,0 +1,61 @@ +package com.example.test + +// A trait definition +trait Greeter { + def greet(name: String): String +} + +// A class definition with parameters +class FormalGreeter(prefix: String) extends Greeter { + // A method definition + override def greet(name: String): String = { + s"$prefix, $name!" + } + + // A val definition + val defaultPrefix: String = "Hello" + + // A var definition + var counter: Int = 0 +} + +// An object definition +object GreeterFactory { + // A function definition + def createGreeter(formal: Boolean): Greeter = { + if (formal) { + new FormalGreeter("Good day") + } else { + new CasualGreeter + } + } + + // A type definition + type GreeterType = Greeter +} + +// An enum definition +enum Greeting { + // Simple enum cases + case Hello, Hi, Hey + + // Full enum case with parameters + case Custom(text: String) +} + +// A class that uses generics +class Container[T](val value: T) { + def map[U](f: T => U): Container[U] = new Container(f(value)) +} + +// A case class +case class Person(name: String, age: Int) { + def introduce(): String = { + val greeter = GreeterFactory.createGreeter(age > 30) + greeter.greet(name) + s" I am $age years old." + } +} + +class CasualGreeter extends Greeter { + override def greet(name: String): String = s"Hey, $name!" +} diff --git a/tests/fixtures/languages/solidity/test.sol b/tests/fixtures/languages/solidity/test.sol new file mode 100644 index 000000000..f78e64884 --- /dev/null +++ b/tests/fixtures/languages/solidity/test.sol @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +contract SimpleStorage { + uint256 private value; + + event ValueChanged(uint256 newValue); + + constructor(uint256 initialValue) { + value = initialValue; + } + + function setValue(uint256 newValue) public { + value = newValue; + emit ValueChanged(newValue); + } + + function getValue() public view returns (uint256) { + return value; + } +} diff --git a/tests/fixtures/languages/swift/test.swift b/tests/fixtures/languages/swift/test.swift new file mode 100644 index 000000000..8e1fbb86f --- /dev/null +++ b/tests/fixtures/languages/swift/test.swift @@ -0,0 +1,18 @@ +// Swift greeting example +class Greeter { + let name: String + + init(name: String) { + self.name = name + } + + func greet() -> String { + return "Hello, \(name)!" + } +} + +// Example usage +func exampleGreeting() { + let greeter = Greeter(name: "World") + print(greeter.greet()) +} diff --git a/tests/fixtures/languages/udev/test.rules b/tests/fixtures/languages/udev/test.rules new file mode 100644 index 000000000..e6cbb91ec --- /dev/null +++ b/tests/fixtures/languages/udev/test.rules @@ -0,0 +1,22 @@ +# Define a label for a specific device +LABEL="my_usb_device", ATTRS{idVendor}=="1234", ATTRS{idProduct}=="5678" + +# Reference a label in a GOTO +SUBSYSTEM=="usb", GOTO="my_peripheral" + +# Define environment variables +ENV{DEVTYPE}="usb_device" +ENV{USB_DRIVER}="usb-storage" + +# Reference environment variables +ENV{DEVTYPE}=="usb_device", SYMLINK+="usb_storage_%k" + +# Variable substitution +SYMLINK+="disk/by-label/$env{ID_FS_LABEL}" + +# Label for a section of rules +LABEL="my_peripheral" +SUBSYSTEM=="usb", MODE="0666" + +# End label +LABEL="end_my_rules" diff --git a/tests/fixtures/sample-code-base-repo-map.txt b/tests/fixtures/sample-code-base-repo-map.txt index 29671525f..b6ddbfeab 100644 --- a/tests/fixtures/sample-code-base-repo-map.txt +++ b/tests/fixtures/sample-code-base-repo-map.txt @@ -1,32 +1,32 @@ tests/fixtures/sample-code-base/sample.js: -⋮... +⋮ │function greet(name) { │ return `Hello, ${name}!`; -⋮... +⋮ │function calculateCircleArea(radius) { │ return Math.PI * radius * radius; -⋮... +⋮ │function isPrime(number) { │ if (number <= 1) return false; │ for (let i = 2; i <= Math.sqrt(number); i++) { │ if (number % i === 0) return false; │ } │ return true; -⋮... +⋮ │function reverseString(str) { │ return str.split('').reverse().join(''); -⋮... +⋮ │function getRandomNumber(min, max) { │ return Math.floor(Math.random() * (max - min + 1)) + min; -⋮... +⋮ │function filterEvenNumbers(numbers) { │ return numbers.filter(num => num % 2 !== 0); -⋮... +⋮ │function factorial(n) { │ if (n === 0 || n === 1) return 1; │ return n * factorial(n - 1); -⋮... +⋮ tests/fixtures/sample-code-base/sample.py: │class Car: @@ -34,22 +34,22 @@ tests/fixtures/sample-code-base/sample.py: │ self.make = make │ self.model = model │ self.year = year -⋮... +⋮ │ def accelerate(self, increment): -⋮... +⋮ │ def brake(self, decrement): -⋮... +⋮ │ def honk(self): -⋮... +⋮ │class Garage: │ def __init__(self): -⋮... +⋮ │ def add_car(self, car): -⋮... +⋮ │ def remove_car(self, car): -⋮... +⋮ │ def list_cars(self): -⋮... +⋮ │def main(): -⋮... +⋮ diff --git a/tests/fixtures/watch.lisp b/tests/fixtures/watch.lisp new file mode 100644 index 000000000..1aae8fb8f --- /dev/null +++ b/tests/fixtures/watch.lisp @@ -0,0 +1,19 @@ +(defun hello-world () + ;; ai this is a simple hello world function + (format t "Hello, World!")) + +(defun add (a b) + ; ai! fix this function to handle nil values + (+ a b)) + +(defun multiply (a b) + ;;; ai? why is this function not working with large numbers? + (* a b)) + +; ai this is a single semicolon comment + +;; ai this is a double semicolon comment + +;;; ai this is a triple semicolon comment + +;;;; ai! this is a quadruple semicolon comment diff --git a/tests/help/test_help.py b/tests/help/test_help.py index 11102db28..a7222185e 100644 --- a/tests/help/test_help.py +++ b/tests/help/test_help.py @@ -1,6 +1,9 @@ +import time import unittest from unittest.mock import MagicMock +from requests.exceptions import ConnectionError, ReadTimeout + import aider from aider.coders import Coder from aider.commands import Commands @@ -10,6 +13,40 @@ from aider.models import Model class TestHelp(unittest.TestCase): + @staticmethod + def retry_with_backoff(func, max_time=60, initial_delay=1, backoff_factor=2): + """ + Execute a function with exponential backoff retry logic. + + Args: + func: Function to execute + max_time: Maximum time in seconds to keep retrying + initial_delay: Initial delay between retries in seconds + backoff_factor: Multiplier for delay after each retry + + Returns: + The result of the function if successful + + Raises: + The last exception encountered if all retries fail + """ + start_time = time.time() + delay = initial_delay + last_exception = None + + while time.time() - start_time < max_time: + try: + return func() + except (ReadTimeout, ConnectionError) as e: + last_exception = e + time.sleep(delay) + delay = min(delay * backoff_factor, 15) # Cap max delay at 15 seconds + + # If we've exhausted our retry time, raise the last exception + if last_exception: + raise last_exception + raise Exception("Retry timeout exceeded but no exception was caught") + @classmethod def setUpClass(cls): io = InputOutput(pretty=False, yes=True) @@ -22,13 +59,17 @@ class TestHelp(unittest.TestCase): help_coder_run = MagicMock(return_value="") aider.coders.HelpCoder.run = help_coder_run - try: - commands.cmd_help("hi") - except aider.commands.SwitchCoder: - pass - else: - # If no exception was raised, fail the test - assert False, "SwitchCoder exception was not raised" + def run_help_command(): + try: + commands.cmd_help("hi") + except aider.commands.SwitchCoder: + pass + else: + # If no exception was raised, fail the test + assert False, "SwitchCoder exception was not raised" + + # Use retry with backoff for the help command that loads models + cls.retry_with_backoff(run_help_command) help_coder_run.assert_called_once()