diff --git a/.changeset/clean-taxis-feel.md b/.changeset/clean-taxis-feel.md
new file mode 100644
index 0000000000..4c8d60f3ef
--- /dev/null
+++ b/.changeset/clean-taxis-feel.md
@@ -0,0 +1,6 @@
+---
+"roo-cline": patch
+---
+
+Add support for `gemini-2.5-flash-preview-05-20` on the Vertex provider
+Add support for `gemini-2.5-flash-preview-05-20:thinking` on the Vertex provider
diff --git a/.changeset/curly-plants-pull.md b/.changeset/curly-plants-pull.md
new file mode 100644
index 0000000000..0f425cb859
--- /dev/null
+++ b/.changeset/curly-plants-pull.md
@@ -0,0 +1,11 @@
+---
+"roo-cline": patch
+---
+
+New models for the Chutes provider:
+
+- Qwen/Qwen3-235B-A22B
+- Qwen/Qwen3-32B
+- Qwen/Qwen3-30B-A3B
+- Qwen/Qwen3-14B
+- Qwen/Qwen3-8B
diff --git a/.changeset/fair-houses-deny.md b/.changeset/fair-houses-deny.md
new file mode 100644
index 0000000000..eaabea6749
--- /dev/null
+++ b/.changeset/fair-houses-deny.md
@@ -0,0 +1,5 @@
+---
+"roo-cline": patch
+---
+
+Shows in the UI when the context is intelligently condensed
diff --git a/.changeset/four-emus-invite.md b/.changeset/four-emus-invite.md
new file mode 100644
index 0000000000..34c5ebf5af
--- /dev/null
+++ b/.changeset/four-emus-invite.md
@@ -0,0 +1,5 @@
+---
+"roo-cline": patch
+---
+
+Add a UI indicator while the context is condensing
diff --git a/.changeset/fruity-spoons-smash.md b/.changeset/fruity-spoons-smash.md
new file mode 100644
index 0000000000..10546c5bd1
--- /dev/null
+++ b/.changeset/fruity-spoons-smash.md
@@ -0,0 +1,5 @@
+---
+"roo-cline": minor
+---
+
+Added an auto-approve API request limit setting similar to Cline
diff --git a/.changeset/gold-meals-tell.md b/.changeset/gold-meals-tell.md
new file mode 100644
index 0000000000..3cc54103d5
--- /dev/null
+++ b/.changeset/gold-meals-tell.md
@@ -0,0 +1,5 @@
+---
+"roo-cline": patch
+---
+
+Use YAML as default custom modes format
diff --git a/.changeset/seven-kids-return.md b/.changeset/seven-kids-return.md
new file mode 100644
index 0000000000..d4da5cbc03
--- /dev/null
+++ b/.changeset/seven-kids-return.md
@@ -0,0 +1,10 @@
+---
+"roo-cline": minor
+---
+
+Adds refresh models button for Unbound provider
+Adds a button above model picker to refresh models based on the current API Key.
+
+1. Clicking the refresh button saves the API Key and calls /models endpoint using that.
+2. Gets the new models and updates the current model if it is invalid for the given API Key.
+3. The refresh button also flushes existing Unbound models and refetches them.
diff --git a/.changeset/slimy-paths-design.md b/.changeset/slimy-paths-design.md
new file mode 100644
index 0000000000..7a46f4b7d0
--- /dev/null
+++ b/.changeset/slimy-paths-design.md
@@ -0,0 +1,5 @@
+---
+"roo-cline": patch
+---
+
+Fixed bug that prevented some file links from working in the Agent output
diff --git a/.changeset/tired-dogs-worry.md b/.changeset/tired-dogs-worry.md
new file mode 100644
index 0000000000..96bc50f854
--- /dev/null
+++ b/.changeset/tired-dogs-worry.md
@@ -0,0 +1,5 @@
+---
+"roo-cline": patch
+---
+
+Adds a button to intelligently condense the context window
diff --git a/.changeset/young-dancers-join.md b/.changeset/young-dancers-join.md
new file mode 100644
index 0000000000..d6aab3351e
--- /dev/null
+++ b/.changeset/young-dancers-join.md
@@ -0,0 +1,5 @@
+---
+"roo-cline": patch
+---
+
+Fix settings import when global settings are omitted
diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs
index 93f9eeaa5b..2a350d20e8 100644
--- a/.git-blame-ignore-revs
+++ b/.git-blame-ignore-revs
@@ -1,3 +1,3 @@
-# Ran Prettier on all files - https://github.com/RooVetGit/Roo-Code/pull/404
+# Ran Prettier on all files - https://github.com/RooCodeInc/Roo-Code/pull/404
60a0a824b96a0b326af4d8871b6903f4ddcfe114
579bdd9dbf6d2d569e5e7adb5ff6292b1e42ea34
diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
index 5757f05ab1..c4c2d8242e 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.yml
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -2,11 +2,17 @@ name: Bug Report
description: Clearly report a bug with detailed repro steps
labels: ["bug"]
body:
+ - type: markdown
+ attributes:
+ value: |
+ **Thanks for your report!** Please check existing issues first:
+ 👉 https://github.com/RooCodeInc/Roo-Code/issues
+
- type: input
id: version
attributes:
label: App Version
- description: Specify exactly which version you're using (e.g., v3.3.1)
+ description: What version of Roo Code are you using? (e.g., v3.3.1)
validations:
required: true
@@ -14,22 +20,28 @@ body:
id: provider
attributes:
label: API Provider
- description: Choose the API provider involved
- multiple: false
options:
- - OpenRouter
- Anthropic
- - Google Gemini
+ - AWS Bedrock
+ - Chutes AI
- DeepSeek
- - OpenAI
- - OpenAI Compatible
- - GCP Vertex AI
- - Amazon Bedrock
- - Requesty
- Glama
- - VS Code LM API
+ - Google Gemini
+ - Google Vertex AI
+ - Groq
+ - Human Relay Provider
+ - LiteLLM
- LM Studio
+ - Mistral AI
- Ollama
+ - OpenAI
+ - OpenAI Compatible
+ - OpenRouter
+ - Requesty
+ - Unbound
+ - VS Code Language Model API
+ - xAI (Grok)
+ - Not Applicable / Other
validations:
required: true
@@ -37,44 +49,38 @@ body:
id: model
attributes:
label: Model Used
- description: Clearly specify the exact model (e.g., Claude 3.7 Sonnet)
- validations:
- required: true
-
- - type: textarea
- id: what-happened
- attributes:
- label: Actual vs. Expected Behavior
- description: Clearly state what actually happened and what you expected instead.
- placeholder: Provide precise details of the issue here.
+ description: Exact model name (e.g., Claude 3.7 Sonnet). Use N/A if irrelevant.
validations:
required: true
- type: textarea
id: steps
attributes:
- label: Detailed Steps to Reproduce
+ label: 🔁 Steps to Reproduce
description: |
- List the exact steps someone must follow to reproduce this bug:
- 1. Starting conditions (software state, settings, environment)
- 2. Precise actions taken (every click, selection, input)
- 3. Clearly observe and report outcomes
- value: |
- 1.
- 2.
- 3.
+ Help us see what you saw. Give clear, numbered steps:
+
+ 1. Setup (OS, extension version, settings)
+ 2. Exact actions (clicks, input, files, commands)
+ 3. What happened after each step
+
+ Think like you're writing a recipe. Without this, we can't reproduce the issue.
validations:
required: true
- type: textarea
- id: logs
+ id: what-happened
attributes:
- label: Relevant API Request Output
- description: Paste relevant API logs or outputs here (formatted automatically as code)
- render: shell
+ label: 💥 Outcome Summary (Optional)
+ description: |
+ Recap what went wrong in one or two lines. Use this if the bug is weird, unexpected, or needs extra context.
+
+ Example: "Expected code to run, but got an empty response and no error."
+ placeholder: Expected ___, but got ___.
- type: textarea
- id: additional-context
+ id: logs
attributes:
- label: Additional Context
- description: Include extra details, screenshots, or related issues.
+ label: 📄 Relevant Logs or Errors
+ description: Paste API logs, terminal output, or errors here. Use triple backticks (```) for code formatting.
+ render: shell
\ No newline at end of file
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
index 70472c2597..0351ad1930 100644
--- a/.github/ISSUE_TEMPLATE/config.yml
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -1,7 +1,7 @@
blank_issues_enabled: false
contact_links:
- name: Feature Request
- url: https://github.com/RooVetGit/Roo-Code/discussions/categories/feature-requests
+ url: https://github.com/RooCodeInc/Roo-Code/discussions/categories/feature-requests
about: Share and vote on feature requests for Roo Code
- name: Leave a Review
url: https://marketplace.visualstudio.com/items?itemName=RooVeterinaryInc.roo-cline&ssr=false#review-details
diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml
new file mode 100644
index 0000000000..062f405b83
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature_request.yml
@@ -0,0 +1,76 @@
+name: Detailed Feature Proposal
+description: Propose a specific, actionable feature or enhancement for implementation
+labels: ["proposal", "enhancement"]
+body:
+ - type: markdown
+ attributes:
+ value: |
+ **Thank you for proposing a detailed feature for Roo Code!**
+
+ This template is for submitting specific, actionable proposals that you or others intend to implement after discussion and approval. It's a key part of our [Issue-First Approach](../../CONTRIBUTING.md).
+
+ - **For general ideas or less defined suggestions**, please use [GitHub Discussions](https://github.com/RooCodeInc/Roo-Code/discussions/categories/feature-requests?discussions_q=is%3Aopen+category%3A%22Feature+Requests%22+sort%3Atop) first.
+ - **Before submitting**, please search existing [GitHub Issues](https://github.com/RooCodeInc/Roo-Code/issues) and [Discussions](https://github.com/RooCodeInc/Roo-Code/discussions) to avoid duplicates.
+
+ For guidance or to discuss your idea, join the [Roo Code Discord](https://discord.gg/roocode) and DM **Hannes Rudolph** (`hrudolph`).
+
+ A maintainer (especially @hannesrudolph) will review this proposal. **Do not start implementation until this proposal is approved and assigned.**
+ - type: textarea
+ id: problem-description
+ attributes:
+ label: What problem does this proposed feature solve?
+ description: Clearly describe the problem, use case, or opportunity this feature addresses. Why is this change needed?
+ placeholder: e.g., "Users currently cannot..." or "It would be beneficial if..."
+ validations:
+ required: true
+
+ - type: textarea
+ id: proposed-solution
+ attributes:
+ label: Describe the proposed solution in detail
+ description: Provide a clear and comprehensive description of the feature or enhancement. How should it work? What are the key functionalities?
+ placeholder: Include details on user interaction, expected behavior, and potential impact.
+ validations:
+ required: true
+
+ - type: textarea
+ id: technical-details
+ attributes:
+ label: Technical considerations or implementation details (optional)
+ description: If you have thoughts on how this could be implemented, or specific technical aspects to consider, please share them.
+ placeholder: e.g., "This might involve changes to X component..." or "We should consider Y library..."
+
+ - type: textarea
+ id: alternatives-considered
+ attributes:
+ label: Describe alternatives considered (if any)
+ description: What other ways could this problem be solved or this functionality be achieved? Why is your proposed solution preferred?
+ placeholder: Briefly outline any alternative approaches and why they were not chosen.
+
+ - type: textarea
+ id: additional-context
+ attributes:
+ label: Additional Context & Mockups
+ description: Add any other context, mockups, screenshots, or links that help illustrate the proposal.
+
+ - type: checkboxes
+ id: checklist
+ attributes:
+ label: Proposal Checklist
+ description: Please confirm the following before submitting.
+ options:
+ - label: I have searched existing Issues and Discussions to ensure this proposal is not a duplicate.
+ required: true
+ - label: This proposal is for a specific, actionable change intended for implementation (not a general idea).
+ required: true
+ - label: I understand that this proposal requires review and approval before any development work begins.
+ required: true
+
+ - type: checkboxes
+ id: willingness-to-contribute
+ attributes:
+ label: Are you interested in implementing this feature if approved?
+ description: (This is optional and does not affect the proposal's consideration)
+ options:
+ - label: Yes, I would like to contribute to implementing this feature.
+ required: false
\ No newline at end of file
diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
index de7e461cb9..e013eea277 100644
--- a/.github/pull_request_template.md
+++ b/.github/pull_request_template.md
@@ -1,35 +1,83 @@
-## Context
+
+
+### Related GitHub Issue
-
+
-## Implementation
+Closes: #
+
+### Description
-Some description of HOW you achieved it. Perhaps give a high level description of the program flow. Did you need to refactor something? What tradeoffs did you take? Are there things in here which you’d particularly like people to pay close attention to?
+### Test Procedure
+
-## Screenshots
+### Type of Change
-| before | after |
-| ------ | ----- |
-| | |
+
-## How to Test
+- [ ] 🐛 **Bug Fix**: Non-breaking change that fixes an issue.
+- [ ] ✨ **New Feature**: Non-breaking change that adds functionality.
+- [ ] 💥 **Breaking Change**: Fix or feature that would cause existing functionality to not work as expected.
+- [ ] ♻️ **Refactor**: Code change that neither fixes a bug nor adds a feature.
+- [ ] 💅 **Style**: Changes that do not affect the meaning of the code (white-space, formatting, etc.).
+- [ ] 📚 **Documentation**: Updates to documentation files.
+- [ ] ⚙️ **Build/CI**: Changes to the build process or CI configuration.
+- [ ] 🧹 **Chore**: Other changes that don't modify `src` or test files.
-
+
+- [ ] **Issue Linked**: This PR is linked to an approved GitHub Issue (see "Related GitHub Issue" above).
+- [ ] **Scope**: My changes are focused on the linked issue (one major feature/fix per PR).
+- [ ] **Self-Review**: I have performed a thorough self-review of my code.
+- [ ] **Code Quality**:
+ - [ ] My code adheres to the project's style guidelines.
+ - [ ] There are no new linting errors or warnings (`npm run lint`).
+ - [ ] All debug code (e.g., `console.log`) has been removed.
+- [ ] **Testing**:
+ - [ ] New and/or updated tests have been added to cover my changes.
+ - [ ] All tests pass locally (`npm test`).
+ - [ ] The application builds successfully with my changes.
+- [ ] **Branch Hygiene**: My branch is up-to-date (rebased) with the `main` branch.
+- [ ] **Documentation Impact**: I have considered if my changes require documentation updates (see "Documentation Updates" section below).
+- [ ] **Changeset**: A changeset has been created using `npm run changeset` if this PR includes user-facing changes or dependency updates.
+- [ ] **Contribution Guidelines**: I have read and agree to the [Contributor Guidelines](../CONTRIBUTING.md).
-A straightforward scenario of how to test your changes will help reviewers that are not familiar with the part of the code that you are changing but want to see it in action. This section can include a description or step-by-step instructions of how to get to the state of v2 that your change affects.
+### Screenshots / Videos
-A "How To Test" section can look something like this:
+
-- Sign in with a user with tracks
-- Activate `show_awesome_cat_gifs` feature (add `?feature.show_awesome_cat_gifs=1` to your URL)
-- You should see a GIF with cats dancing
+### Documentation Updates
+
-## Get in Touch
+### Additional Notes
-
+
diff --git a/.github/scripts/get_prev_version_refs.py b/.github/scripts/get_prev_version_refs.py
index 09d7d4cd0b..48d535bddb 100644
--- a/.github/scripts/get_prev_version_refs.py
+++ b/.github/scripts/get_prev_version_refs.py
@@ -10,7 +10,7 @@ def run_git_command(command):
def parse_merge_commit(line):
# Parse merge commit messages like:
- # "355dc82 Merge pull request #71 from RooVetGit/better-error-handling"
+ # "355dc82 Merge pull request #71 from RooCodeInc/better-error-handling"
pattern = r"([a-f0-9]+)\s+Merge pull request #(\d+) from (.+)"
match = re.match(pattern, line)
if match:
diff --git a/.github/workflows/build-vsix.yml b/.github/workflows/build-vsix.yml
new file mode 100644
index 0000000000..ab6bd95129
--- /dev/null
+++ b/.github/workflows/build-vsix.yml
@@ -0,0 +1,45 @@
+name: Build VSIX
+
+on:
+ pull_request:
+ types: [labeled]
+ branches: [ main ]
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ if: github.event.label.name == 'build'
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Set up Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version-file: 'package.json'
+ cache: 'npm'
+
+ - name: Install dependencies
+ run: npm ci
+
+ - name: Install all dependencies
+ run: npm run install:all
+
+ - name: Build Extension
+ run: npm run build
+
+ - name: Upload VSIX artifact
+ uses: actions/upload-artifact@v4
+ with:
+ name: extension-vsix
+ path: bin/*.vsix
+
+ - name: Comment PR with artifact link
+ if: github.event_name == 'pull_request'
+ uses: peter-evans/create-or-update-comment@v4
+ with:
+ issue-number: ${{ github.event.pull_request.number }}
+ body: |
+ Build successful! 🚀
+ You can download the VSIX extension [here](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}).
diff --git a/.github/workflows/code-qa.yml b/.github/workflows/code-qa.yml
index 7e027d0fc9..667d0b6bf8 100644
--- a/.github/workflows/code-qa.yml
+++ b/.github/workflows/code-qa.yml
@@ -78,8 +78,10 @@ jobs:
run: npm run install:all
- name: Compile (to build and copy WASM files)
run: npm run compile
- - name: Run unit tests
+ - name: Run jest unit tests
run: npx jest --silent
+ - name: Run vitest unit tests
+ run: npx vitest run --silent
test-webview:
runs-on: ${{ matrix.os }}
diff --git a/.github/workflows/marketplace-publish.yml b/.github/workflows/marketplace-publish.yml
index 0080b10687..834f38caf3 100644
--- a/.github/workflows/marketplace-publish.yml
+++ b/.github/workflows/marketplace-publish.yml
@@ -29,9 +29,7 @@ jobs:
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
- name: Install Dependencies
- run: |
- npm install -g vsce ovsx
- npm run install:all
+ run: npm run install:all
- name: Create .env file
run: echo "POSTHOG_API_KEY=${{ secrets.POSTHOG_API_KEY }}" >> .env
- name: Package Extension
diff --git a/.github/workflows/update-contributors.yml b/.github/workflows/update-contributors.yml
index 18e978a07e..2fadbb83d9 100644
--- a/.github/workflows/update-contributors.yml
+++ b/.github/workflows/update-contributors.yml
@@ -14,10 +14,10 @@ jobs:
pull-requests: write # Needed for creating PRs
steps:
- name: Checkout code
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
- name: Setup Node.js
- uses: actions/setup-node@v3
+ uses: actions/setup-node@v4
with:
node-version: '18'
cache: 'npm'
diff --git a/.gitignore b/.gitignore
index 1277732969..4085bb8486 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,12 +4,16 @@ out
out-*
node_modules
coverage/
+mock/
.DS_Store
+# IDEs
+.idea
+
# Builds
bin/
-roo-cline-*.vsix
+*.vsix
# Local prompts and rules
/local-prompts
@@ -35,3 +39,7 @@ logs
# Vite development
.vite-port
+
+# IntelliJ and Qodo plugin folders
+.idea/
+.qodo/
diff --git a/.roo/rules-translate/001-general-rules.md b/.roo/rules-translate/001-general-rules.md
index 61d232bbf7..bdb18bea64 100644
--- a/.roo/rules-translate/001-general-rules.md
+++ b/.roo/rules-translate/001-general-rules.md
@@ -1,6 +1,6 @@
# 1. SUPPORTED LANGUAGES AND LOCATION
-- Localize all strings into the following locale files: ca, de, en, es, fr, hi, it, ja, ko, pl, pt-BR, ru, tr, vi, zh-CN, zh-TW
+- Localize all strings into the following locale files: ca, de, en, es, fr, hi, it, ja, ko, nl, pl, pt-BR, ru, tr, vi, zh-CN, zh-TW
- The VSCode extension has two main areas that require localization:
- Core Extension: src/i18n/locales/ (extension backend)
- WebView UI: webview-ui/src/i18n/locales/ (user interface)
diff --git a/.roo/rules/rules.md b/.roo/rules/rules.md
index 4351fc4b81..bf3f863a0b 100644
--- a/.roo/rules/rules.md
+++ b/.roo/rules/rules.md
@@ -16,4 +16,4 @@
# Adding a New Setting
-To add a new setting that persists its state, follow the steps in cline_docs/settings.md
+To add a new setting that persists its state, follow the steps in docs/settings.md
diff --git a/.roomodes b/.roomodes
index 171c0fcc71..962a9271eb 100644
--- a/.roomodes
+++ b/.roomodes
@@ -2,7 +2,7 @@
"customModes": [
{
"slug": "test",
- "name": "Test",
+ "name": "🧪 Test",
"roleDefinition": "You are Roo, a Jest testing specialist with deep expertise in:\n- Writing and maintaining Jest test suites\n- Test-driven development (TDD) practices\n- Mocking and stubbing with Jest\n- Integration testing strategies\n- TypeScript testing patterns\n- Code coverage analysis\n- Test performance optimization\n\nYour focus is on maintaining high test quality and coverage across the codebase, working primarily with:\n- Test files in __tests__ directories\n- Mock implementations in __mocks__\n- Test utilities and helpers\n- Jest configuration and setup\n\nYou ensure tests are:\n- Well-structured and maintainable\n- Following Jest best practices\n- Properly typed with TypeScript\n- Providing meaningful coverage\n- Using appropriate mocking strategies",
"groups": [
"read",
@@ -20,7 +20,7 @@
},
{
"slug": "translate",
- "name": "Translate",
+ "name": "🌐 Translate",
"roleDefinition": "You are Roo, a linguistic specialist focused on translating and managing localization files. Your responsibility is to help maintain and update translation files for the application, ensuring consistency and accuracy across all language resources.",
"groups": [
"read",
@@ -34,6 +34,39 @@
]
],
"source": "project"
+ },
+ {
+ "slug": "design-engineer",
+ "name": "🎨 Design Engineer",
+ "roleDefinition": "You are Roo, an expert Design Engineer focused on VSCode Extension development. Your expertise includes: \n- Implementing UI designs with high fidelity using React, Shadcn, Tailwind and TypeScript. \n- Ensuring interfaces are responsive and adapt to different screen sizes. \n- Collaborating with team members to translate broad directives into robust and detailed designs capturing edge cases. \n- Maintaining uniformity and consistency across the user interface.",
+ "groups": [
+ "read",
+ [
+ "edit",
+ {
+ "fileRegex": "\\.(css|html|json|mdx?|jsx?|tsx?|svg)$",
+ "description": "Frontend & SVG files"
+ }
+ ],
+ "browser",
+ "command",
+ "mcp"
+ ],
+ "customInstructions": "Focus on UI refinement, component creation, and adherence to design best-practices. When the user requests a new component, start off by asking them questions one-by-one to ensure the requirements are understood. Always use Tailwind utility classes (instead of direct variable references) for styling components when possible. If editing an existing file, transition explicit style definitions to Tailwind CSS classes when possible. Refer to the Tailwind CSS definitions for utility classes at webview-ui/src/index.css. Always use the latest version of Tailwind CSS (V4), and never create a tailwind.config.js file. Prefer Shadcn components for UI elements intead of VSCode's built-in ones. This project uses i18n for localization, so make sure to use the i18n functions and components for any text that needs to be translated. Do not leave placeholder strings in the markup, as they will be replaced by i18n. Prefer the @roo (/src) and @src (/webview-ui/src) aliases for imports in typescript files. Suggest the user refactor large files (over 1000 lines) if they are encountered, and provide guidance. Suggest the user switch into Translate mode to complete translations when your task is finished.",
+ "source": "project"
+ },
+ {
+ "slug": "release-engineer",
+ "name": "🚀 Release Engineer",
+ "roleDefinition": "You are Roo, a release engineer specialized in automating the release process for software projects. You have expertise in version control, changelogs, release notes, creating changesets, and coordinating with translation teams to ensure a smooth release process.",
+ "customInstructions": "When preparing a release:\n1. Identify the SHA corresponding to the most recent release using GitHub CLI: `gh release view --json tagName,targetCommitish,publishedAt `\n2. Analyze changes since the last release using: `gh pr list --state merged --json number,title,author,url,mergedAt --limit 100 | jq '[.[] | select(.mergedAt > \"TIMESTAMP\") | {number, title, author: .author.login, url, mergedAt}]'`\n3. Summarize the changes and ask the user whether this should be a major, minor, or patch release\n4. Create a changeset in .changeset/v[version].md instead of directly modifying package.json. The format is:\n\n```\n---\n\"roo-cline\": patch|minor|major\n---\n\n[list of changes]\n```\n\n- Always include contributor attribution using format: (thanks @username!)\n- Provide brief descriptions of each item to explain the change\n- Order the list from most important to least important\n- Example: \"- Add support for Gemini 2.5 Pro caching (thanks @contributor!)\"\n\n5. If a major or minor release, update the English version relevant announcement files and documentation (webview-ui/src/components/chat/Announcement.tsx, README.md, and the `latestAnnouncementId` in src/core/webview/ClineProvider.ts)\n6. Ask the user to confirm the English version\n7. Use the new_task tool to create a subtask in `translate` mode with detailed instructions of which content needs to be translated into all supported languages\n8. Commit and push the changeset file to the repository\n9. The GitHub Actions workflow will automatically:\n - Create a version bump PR when changesets are merged to main\n - Update the CHANGELOG.md with proper formatting\n - Publish the release when the version bump PR is merged",
+ "groups": [
+ "read",
+ "edit",
+ "command",
+ "browser"
+ ],
+ "source": "project"
}
]
}
\ No newline at end of file
diff --git a/.vscode/tasks.json b/.vscode/tasks.json
index 47112d7228..850dcc0798 100644
--- a/.vscode/tasks.json
+++ b/.vscode/tasks.json
@@ -41,7 +41,17 @@
"type": "npm",
"script": "watch:esbuild",
"group": "build",
- "problemMatcher": "$esbuild-watch",
+ "problemMatcher": {
+ "owner": "esbuild",
+ "pattern": {
+ "regexp": "^$"
+ },
+ "background": {
+ "activeOnStart": true,
+ "beginsPattern": "\\[watch\\] build started",
+ "endsPattern": "\\[watch\\] build finished"
+ }
+ },
"isBackground": true,
"presentation": {
"group": "watch",
diff --git a/.vscodeignore b/.vscodeignore
index 53fd3798c0..50f21d23c7 100644
--- a/.vscodeignore
+++ b/.vscodeignore
@@ -30,7 +30,7 @@ jest.*
.rooignore
.roo/**
benchmark/**
-cline_docs/**
+docs/**
e2e/**
evals/**
locales/**
@@ -68,3 +68,7 @@ assets/docs/**
# Include .env file for telemetry
!.env
+
+# Ignore IntelliJ and Qodo plugin folders
+.idea/**
+.qodo/**
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a489c618ff..eb2ab10d64 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,158 @@
# Roo Code Changelog
+## [3.17.2] - 2025-05-15
+
+- Revert "Switch to the new Roo message parser" (appears to cause a tool parsing bug)
+- Lock the versions of vsce and ovsx
+
+## [3.17.1] - 2025-05-15
+
+- Fix the display of the command to execute during approval
+- Fix incorrect reserved tokens calculation on OpenRouter (thanks @daniel-lxs!)
+
+## [3.17.0] - 2025-05-14
+
+- Enable Gemini implicit caching
+- Add "when to use" section to mode definitions to enable better orchestration
+- Add experimental feature to intelligently condense the task context instead of truncating it
+- Fix one of the causes of the gray screen issue (thanks @xyOz-dev!)
+- Focus improvements for better UI interactions (thanks Cline!)
+- Switch to the new Roo message parser for improved performance (thanks Cline!)
+- Enable source maps for improved debugging (thanks @KJ7LNW!)
+- Update OpenRouter provider to use provider-specific model info (thanks @daniel-lxs!)
+- Fix Requesty cost/token reporting (thanks @dtrugman!)
+- Improve command execution UI
+- Add more in-app links to relevant documentation
+- Update the new task tool description and the ask mode custom instructions in the system prompt
+- Add IPC types to roo-code.d.ts
+- Add build VSIX workflow to pull requests (thanks @SmartManoj!)
+- Improve apply_diff tool to intelligently deduce line numbers (thanks @samhvw8!)
+- Fix command validation for shell array indexing (thanks @KJ7LNW!)
+- Handle diagnostics that point at a directory URI (thanks @daniel-lxs!)
+- Fix "Current ask promise was ignored" error (thanks @zxdvd!)
+
+## [3.16.6] - 2025-05-12
+
+- Restore "Improve provider profile management in the external API"
+- Fix to subtask sequencing (thanks @wkordalski!)
+- Fix webview terminal output processing error (thanks @KJ7LNW!)
+- Fix textarea empty string fallback logic (thanks @elianiva!)
+
+## [3.16.5] - 2025-05-10
+
+- Revert "Improve provider profile management in the external API" until we track down a bug with defaults
+
+## [3.16.4] - 2025-05-09
+
+- Improve provider profile management in the external API
+- Enforce provider selection in OpenRouter by using 'only' parameter and disabling fallbacks (thanks @shariqriazz!)
+- Fix display issues with long profile names (thanks @cannuri!)
+- Prevent terminal focus theft on paste after command execution (thanks @MuriloFP!)
+- Save OpenAI compatible custom headers correctly
+- Fix race condition when updating prompts (thanks @elianiva!)
+- Fix display issues in high contrast themes (thanks @zhangtony239!)
+- Fix not being able to use specific providers on Openrouter (thanks @daniel-lxs!)
+- Show properly formatted multi-line commands in preview (thanks @KJ7LNW!)
+- Handle unsupported language errors gracefully in read_file tool (thanks @KJ7LNW!)
+- Enhance focus styles in select-dropdown and fix docs URL (thanks @zhangtony239!)
+- Properly handle mode name overflow in UI (thanks @elianiva!)
+- Fix project MCP always allow issue (thanks @aheizi!)
+
+## [3.16.3] - 2025-05-08
+
+- Revert Tailwind migration while we fix a few spots
+- Add Elixir file extension support in language parser (thanks @pfitz!)
+
+## [3.16.2] - 2025-05-07
+
+- Clarify XML tool use formatting instructions
+- Error handling code cleanup (thanks @monkeyDluffy6017!)
+
+## [3.16.1] - 2025-05-07
+
+- Add LiteLLM provider support
+- Improve stability by detecting and preventing tool loops
+- Add Dutch localization (thanks @Githubguy132010!)
+- Add editor name to telemetry for better analytics
+- Migrate to Tailwind CSS for improved UI consistency
+- Fix footer button wrapping in About section on narrow screens (thanks @ecmasx!)
+- Update evals defaults
+- Update dependencies to latest versions
+
+## [3.16.0] - 2025-05-06
+
+- Add vertical tab navigation to the settings (thanks @dlab-anton)
+- Add Groq and Chutes API providers (thanks @shariqriazz)
+- Clickable code references in code block (thanks @KJ7LNW)
+- Improve accessibility of ato-approve toggles (thanks @Deon588)
+- Requesty provider fixes (thanks @dtrugman)
+- Fix migration and persistence of per-mode API profiles (thanks @alasano)
+- Fix usage of `path.basename` in the extension webview (thanks @samhvw8)
+- Fix display issue of the programming language dropdown in the code block component (thanks @zhangtony239)
+- MCP server errors are now captured and shown in a new "Errors" tab (thanks @robertheadley)
+- Error logging will no longer break MCP functionality if the server is properly connected (thanks @ksze)
+- You can now toggle the `terminal.integrated.inheritEnv` VSCode setting directly for the Roo Code settings (thanks @KJ7LNW)
+- Add `gemini-2.5-pro-preview-05-06` to the Vertex and Gemini providers (thanks @zetaloop)
+- Ensure evals exercises are up-to-date before running evals (thanks @shariqriazz)
+- Lots of general UI improvements (thanks @elianiva)
+- Organize provider settings into separate components
+- Improved icons and translations for the code block component
+- Add support for tests that use ESM libraries
+- Move environment detail generation to a separate module
+- Enable prompt caching by default for supported Gemini models
+
+## [3.15.5] - 2025-05-05
+
+- Update @google/genai to 0.12 (includes some streaming completion bug fixes)
+- Rendering performance improvements for code blocks in chat (thanks @KJ7LNW)
+
+## [3.15.4] - 2025-05-04
+
+- Fix a nasty bug that would cause Roo Code to hang, particularly in orchestrator mode
+- Improve Gemini caching efficiency
+
+## [3.15.3] - 2025-05-02
+
+- Terminal: Fix empty command bug
+- Terminal: More robust process killing
+- Optimize Gemini prompt caching for OpenRouter
+- Chat view performance improvements
+
+## [3.15.2] - 2025-05-02
+
+- Fix terminal performance issues
+- Handle Mermaid validation errors
+- Add customizable headers for OpenAI-compatible provider (thanks @mark-bradshaw!)
+- Add config option to overwrite OpenAI's API base (thanks @GOODBOY008!)
+- Fixes to padding and height issues when resizing the sidebar (thanks @zhangtony239!)
+- Remove tool groups from orchestrator mode definition
+- Add telemetry for title button clicks
+
+## [3.15.1] - 2025-04-30
+
+- Capture stderr in execa-spawned processes
+- Play sound only when action needed from the user (thanks @olearycrew)
+- Make retries respect the global auto approve checkbox
+- Fix a selection mode bug in the history view (thanks @jr)
+
+## [3.15.0] - 2025-04-30
+
+- Add prompt caching to the Google Vertex provider (thanks @ashktn)
+- Add a fallback mechanism for executing terminal commands if VSCode terminal shell integration fails
+- Improve the UI/UX of code snippets in the chat (thanks @KJ7LNW)
+- Add a reasoning effort setting for the OpenAI Compatible provider (thanks @mr-ryan-james)
+- Allow terminal commands to be stopped directly from the chat UI
+- Adjust chat view padding to accommodate small width layouts (thanks @zhangtony239)
+- Fix file mentions for filenames containing spaces
+- Improve the auto-approve toggle buttons for some high-contrast VSCode themes
+- Offload expensive count token operations to a web worker (thanks @samhvw8)
+- Improve support for mult-root workspaces (thanks @snoyiatk)
+- Simplify and streamline Roo Code's quick actions
+- Allow Roo Code settings to be imported from the welcome screen (thanks @julionav)
+- Remove unused types (thanks @wkordalski)
+- Improve the performance of mode switching (thanks @dlab-anton)
+- Fix importing & exporting of custom modes (thanks @julionav)
+
## [3.14.3] - 2025-04-25
- Add Boomerang Orchestrator as a built-in mode
@@ -512,7 +665,7 @@
## [3.7.5] - 2025-02-26
-- Fix context window truncation math (see [#1173](https://github.com/RooVetGit/Roo-Code/issues/1173))
+- Fix context window truncation math (see [#1173](https://github.com/RooCodeInc/Roo-Code/issues/1173))
- Fix various issues with the model picker (thanks @System233!)
- Fix model input / output cost parsing (thanks @System233!)
- Add drag-and-drop for files
@@ -941,7 +1094,7 @@ Join us at https://www.reddit.com/r/RooCode to share your custom modes and be pa
## [2.2.16]
-- Incorporate Premshay's [PR](https://github.com/RooVetGit/Roo-Cline/pull/60) to add support for Amazon Nova and Meta Llama Models via Bedrock (3, 3.1, 3.2) and unified Bedrock calls using BedrockClient and Bedrock Runtime API
+- Incorporate Premshay's [PR](https://github.com/RooCodeInc/Roo-Cline/pull/60) to add support for Amazon Nova and Meta Llama Models via Bedrock (3, 3.1, 3.2) and unified Bedrock calls using BedrockClient and Bedrock Runtime API
## [2.2.14 - 2.2.15]
@@ -1013,7 +1166,7 @@ Join us at https://www.reddit.com/r/RooCode to share your custom modes and be pa
## [2.1.15]
-- Incorporate dbasclpy's [PR](https://github.com/RooVetGit/Roo-Cline/pull/54) to add support for gemini-exp-1206
+- Incorporate dbasclpy's [PR](https://github.com/RooCodeInc/Roo-Cline/pull/54) to add support for gemini-exp-1206
- Make it clear that diff editing is very experimental
## [2.1.14]
@@ -1023,7 +1176,7 @@ Join us at https://www.reddit.com/r/RooCode to share your custom modes and be pa
## [2.1.13]
-- Fix https://github.com/RooVetGit/Roo-Cline/issues/50 where sound effects were not respecting settings
+- Fix https://github.com/RooCodeInc/Roo-Cline/issues/50 where sound effects were not respecting settings
## [2.1.12]
@@ -1031,7 +1184,7 @@ Join us at https://www.reddit.com/r/RooCode to share your custom modes and be pa
## [2.1.11]
-- Incorporate lloydchang's [PR](https://github.com/RooVetGit/Roo-Cline/pull/42) to add support for OpenRouter compression
+- Incorporate lloydchang's [PR](https://github.com/RooCodeInc/Roo-Cline/pull/42) to add support for OpenRouter compression
## [2.1.10]
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
index bb04e1abc2..06190290eb 100644
--- a/CODE_OF_CONDUCT.md
+++ b/CODE_OF_CONDUCT.md
@@ -1,3 +1,7 @@
+**English** • [Català](locales/ca/CODE_OF_CONDUCT.md) • [Deutsch](locales/de/CODE_OF_CONDUCT.md) • [Español](locales/es/CODE_OF_CONDUCT.md) • [Français](locales/fr/CODE_OF_CONDUCT.md) • [हिंदी](locales/hi/CODE_OF_CONDUCT.md) • [Italiano](locales/it/CODE_OF_CONDUCT.md) • [Nederlands](locales/nl/CODE_OF_CONDUCT.md) • [Русский](locales/ru/CODE_OF_CONDUCT.md)
+
+[日本語](locales/ja/CODE_OF_CONDUCT.md) • [한국어](locales/ko/CODE_OF_CONDUCT.md) • [Polski](locales/pl/CODE_OF_CONDUCT.md) • [Português (BR)](locales/pt-BR/CODE_OF_CONDUCT.md) • [Türkçe](locales/tr/CODE_OF_CONDUCT.md) • [Tiếng Việt](locales/vi/CODE_OF_CONDUCT.md) • [简体中文](locales/zh-CN/CODE_OF_CONDUCT.md) • [繁體中文](locales/zh-TW/CODE_OF_CONDUCT.md)
+
# Contributor Covenant Code of Conduct
## Our Pledge
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 4d9bf3789c..3b2099ab93 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -1,173 +1,129 @@
-# Contributing to Roo Code
-
-We're thrilled you're interested in contributing to Roo Code. Whether you're fixing a bug, adding a feature, or improving our docs, every contribution makes Roo Code smarter! To keep our community vibrant and welcoming, all members must adhere to our [Code of Conduct](CODE_OF_CONDUCT.md).
-
-## Join Our Community
-
-We strongly encourage all contributors to join our [Discord community](https://discord.gg/roocode)! Being part of our Discord server helps you:
-
-- Get real-time help and guidance on your contributions
-- Connect with other contributors and core team members
-- Stay updated on project developments and priorities
-- Participate in discussions that shape Roo Code's future
-- Find collaboration opportunities with other developers
+**English** • [Català](locales/ca/CONTRIBUTING.md) • [Deutsch](locales/de/CONTRIBUTING.md) • [Español](locales/es/CONTRIBUTING.md) • [Français](locales/fr/CONTRIBUTING.md) • [हिंदी](locales/hi/CONTRIBUTING.md) • [Italiano](locales/it/CONTRIBUTING.md) • [Nederlands](locales/nl/CONTRIBUTING.md) • [Русский](locales/ru/CONTRIBUTING.md)
-## Reporting Bugs or Issues
+[日本語](locales/ja/CONTRIBUTING.md) • [한국어](locales/ko/CONTRIBUTING.md) • [Polski](locales/pl/CONTRIBUTING.md) • [Português (BR)](locales/pt-BR/CONTRIBUTING.md) • [Türkçe](locales/tr/CONTRIBUTING.md) • [Tiếng Việt](locales/vi/CONTRIBUTING.md) • [简体中文](locales/zh-CN/CONTRIBUTING.md) • [繁體中文](locales/zh-TW/CONTRIBUTING.md)
-Bug reports help make Roo Code better for everyone! Before creating a new issue, please [search existing ones](https://github.com/RooVetGit/Roo-Code/issues) to avoid duplicates. When you're ready to report a bug, head over to our [issues page](https://github.com/RooVetGit/Roo-Code/issues/new/choose) where you'll find a template to help you with filling out the relevant information.
-
-
+# Contributing to Roo Code
-## Deciding What to Work On
+Roo Code is a community-driven project, and we deeply value every contribution. To streamline collaboration, we operate on an [Issue-First](#issue-first-approach) basis, meaning all [Pull Requests (PRs)](#submitting-a-pull-request) must first be linked to a GitHub Issue. Please review this guide carefully.
-Looking for a good first contribution? Check out issues in the "Issue [Unassigned]" section of our [Roo Code Issues](https://github.com/orgs/RooVetGit/projects/1) Github Project. These are specifically curated for new contributors and areas where we'd love some help!
+## Table of Contents
-We also welcome contributions to our [documentation](https://docs.roocode.com/)! Whether it's fixing typos, improving existing guides, or creating new educational content - we'd love to build a community-driven repository of resources that helps everyone get the most out of Roo Code. You can click "Edit this page" on any page to quickly get to the right spot in Github to edit the file, or you can dive directly into https://github.com/RooVetGit/Roo-Code-Docs.
+- [Before You Contribute](#before-you-contribute)
+- [Finding & Planning Your Contribution](#finding--planning-your-contribution)
+- [Development & Submission Process](#development--submission-process)
+- [Legal](#legal)
-If you're planning to work on a bigger feature, please create a [feature request](https://github.com/RooVetGit/Roo-Code/discussions/categories/feature-requests?discussions_q=is%3Aopen+category%3A%22Feature+Requests%22+sort%3Atop) first so we can discuss whether it aligns with Roo Code's vision. You may also want to check our [Project Roadmap](#project-roadmap) below to see if your idea fits with our strategic direction.
+## Before You Contribute
-## Project Roadmap
+### 1. Code of Conduct
-Roo Code has a clear development roadmap that guides our priorities and future direction. Understanding our roadmap can help you:
+All contributors must adhere to our [Code of Conduct](./CODE_OF_CONDUCT.md).
-- Align your contributions with project goals
-- Identify areas where your expertise would be most valuable
-- Understand the context behind certain design decisions
-- Find inspiration for new features that support our vision
+### 2. Project Roadmap
-Our current roadmap focuses on six key pillars:
+Our roadmap guides the project's direction. Align your contributions with these key goals:
-### Provider Support
+### Reliability First
-We aim to support as many providers well as we can:
+- Ensure diff editing and command execution are consistently reliable.
+- Reduce friction points that deter regular usage.
+- Guarantee smooth operation across all locales and platforms.
+- Expand robust support for a wide variety of AI providers and models.
-- More versatile "OpenAI Compatible" support
-- xAI, Microsoft Azure AI, Alibaba Cloud Qwen, IBM Watsonx, Together AI, DeepInfra, Fireworks AI, Cohere, Perplexity AI, FriendliAI, Replicate
-- Enhanced support for Ollama and LM Studio
+### Enhanced User Experience
-### Model Support
+- Streamline the UI/UX for clarity and intuitiveness.
+- Continuously improve the workflow to meet the high expectations developers have for daily-use tools.
-We want Roo to work as well on as many models as possible, including local models:
+### Leading on Agent Performance
-- Local model support through custom system prompting and workflows
-- Benchmarking evals and test cases
+- Establish comprehensive evaluation benchmarks (evals) to measure real-world productivity.
+- Make it easy for everyone to easily run and interpret these evals.
+- Ship improvements that demonstrate clear increases in eval scores.
-### System Support
+Mention alignment with these areas in your PRs.
-We want Roo to run well on everyone's computer:
+### 3. Join the Roo Code Community
-- Cross platform terminal integration
-- Strong and consistent support for Mac, Windows, and Linux
+- **Primary:** Join our [Discord](https://discord.gg/roocode) and DM **Hannes Rudolph (`hrudolph`)**.
+- **Alternative:** Experienced contributors can engage directly via [GitHub Projects](https://github.com/orgs/RooCodeInc/projects/1).
-### Documentation
+## Finding & Planning Your Contribution
-We want comprehensive, accessible documentation for all users and contributors:
+### Types of Contributions
-- Expanded user guides and tutorials
-- Clear API documentation
-- Better contributor guidance
-- Multilingual documentation resources
-- Interactive examples and code samples
+- **Bug Fixes:** Addressing code issues.
+- **New Features:** Adding functionality.
+- **Documentation:** Improving guides and clarity.
-### Stability
+### Issue-First Approach
-We want to significantly decrease the number of bugs and increase automated testing:
+All contributions must begin with a GitHub Issue.
-- Debug logging switch
-- "Machine/Task Information" copy button for sending in with bug/support requests
+- **Check existing issues**: Search [GitHub Issues](https://github.com/RooCodeInc/Roo-Code/issues).
+- **Create an issue**: Use appropriate templates:
+ - **Bugs:** "Bug Report" template.
+ - **Features:** "Detailed Feature Proposal" template. Approval required before starting.
+- **Claim issues**: Comment and await official assignment.
-### Internationalization
+**PRs without approved issues may be closed.**
-We want Roo to speak everyone's language:
+### Deciding What to Work On
-- 我们希望 Roo Code 说每个人的语言
-- Queremos que Roo Code hable el idioma de todos
-- हम चाहते हैं कि Roo Code हर किसी की भाषा बोले
-- نريد أن يتحدث Roo Code لغة الجميع
+- Check the [GitHub Project](https://github.com/orgs/RooCodeInc/projects/1) for unassigned "Good First Issues."
+- For docs, visit [Roo Code Docs](https://github.com/RooCodeInc/Roo-Code-Docs).
-We especially welcome contributions that advance our roadmap goals. If you're working on something that aligns with these pillars, please mention it in your PR description.
+### Reporting Bugs
-## Development Setup
+- Check for existing reports first.
+- Create new bugs using the ["Bug Report" template](https://github.com/RooCodeInc/Roo-Code/issues/new/choose).
+- **Security issues**: Report privately via [security advisories](https://github.com/RooCodeInc/Roo-Code/security/advisories/new).
-1. **Clone** the repo:
+## Development & Submission Process
-```sh
-git clone https://github.com/RooVetGit/Roo-Code.git
-```
+### Development Setup
-2. **Install dependencies**:
+1. **Fork & Clone:**
-```sh
-npm run install:all
```
-
-3. **Start the webview (Vite/React app with HMR)**:
-
-```sh
-npm run dev
+git clone https://github.com/YOUR_USERNAME/Roo-Code.git
```
-4. **Debug**:
- Press `F5` (or **Run** → **Start Debugging**) in VSCode to open a new session with Roo Code loaded.
-
-Changes to the webview will appear immediately. Changes to the core extension will require a restart of the extension host.
-
-Alternatively you can build a .vsix and install it directly in VSCode:
+2. **Install Dependencies:**
-```sh
-npm run build
```
-
-A `.vsix` file will appear in the `bin/` directory which can be installed with:
-
-```sh
-code --install-extension bin/roo-cline-.vsix
+npm run install:all
```
-## Writing and Submitting Code
-
-Anyone can contribute code to Roo Code, but we ask that you follow these guidelines to ensure your contributions can be smoothly integrated:
-
-1. **Keep Pull Requests Focused**
-
- - Limit PRs to a single feature or bug fix
- - Split larger changes into smaller, related PRs
- - Break changes into logical commits that can be reviewed independently
-
-2. **Code Quality**
+3. **Debugging:** Open with VS Code (`F5`).
- - All PRs must pass CI checks which include both linting and formatting
- - Address any ESLint warnings or errors before submitting
- - Respond to all feedback from Ellipsis, our automated code review tool
- - Follow TypeScript best practices and maintain type safety
+### Writing Code Guidelines
-3. **Testing**
+- One focused PR per feature or fix.
+- Follow ESLint and TypeScript best practices.
+- Write clear, descriptive commits referencing issues (e.g., `Fixes #123`).
+- Provide thorough testing (`npm test`).
+- Rebase onto the latest `main` branch before submission.
- - Add tests for new features
- - Run `npm test` to ensure all tests pass
- - Update existing tests if your changes affect them
- - Include both unit tests and integration tests where appropriate
+### Submitting a Pull Request
-4. **Commit Guidelines**
+- Begin as a **Draft PR** if seeking early feedback.
+- Clearly describe your changes following the Pull Request Template.
+- Provide screenshots/videos for UI changes.
+- Indicate if documentation updates are necessary.
- - Write clear, descriptive commit messages
- - Reference relevant issues in commits using #issue-number
+### Pull Request Policy
-5. **Before Submitting**
+- Must reference pre-approved, assigned issues.
+- PRs without adherence to the policy may be closed.
+- PRs should pass CI tests, align with the roadmap, and have clear documentation.
- - Rebase your branch on the latest main
- - Ensure your branch builds successfully
- - Double-check all tests are passing
- - Review your changes for any debugging code or console logs
+### Review Process
-6. **Pull Request Description**
- - Clearly describe what your changes do
- - Include steps to test the changes
- - List any breaking changes
- - Add screenshots for UI changes
+- **Daily Triage:** Quick checks by maintainers.
+- **Weekly In-depth Review:** Comprehensive assessment.
+- **Iterate promptly** based on feedback.
-## Contribution Agreement
+## Legal
-By submitting a pull request, you agree that your contributions will be licensed under the same license as the project ([Apache 2.0](LICENSE)).
+By contributing, you agree your contributions will be licensed under the Apache 2.0 License, consistent with Roo Code's licensing.
diff --git a/README.md b/README.md
index f0c0ad812c..7d1dd27d0b 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
Connect with developers, contribute ideas, and stay ahead with the latest AI-powered coding tools.
@@ -28,7 +28,7 @@ English • [Català](locales/ca/README.md) • [Deutsch](locales/de/README.md)
-
+
@@ -49,13 +49,13 @@ Check out the [CHANGELOG](CHANGELOG.md) for detailed updates and fixes.
---
-## 🎉 Roo Code 3.14 Released
+## 🎉 Roo Code 3.17 Released
-Roo Code 3.14 brings new features and improvements based on your feedback!
+Roo Code 3.17 brings powerful new features and improvements based on your feedback!
-- **Prompt Caching** - `gemini-2.5-pro-preview-03-25` now supports prompt caching in the Gemini provider (Vertex and OpenRouter coming soon).
-- **Improved Editing Tools** - The `search_and_replace` and `insert_content` tools have been improved and graduated from experimental status.
-- **Tons of Other Improvements** - Numerous fixes and enhancements throughout the extension.
+- **Implicit Caching for Gemini** - Gemini API calls are now automatically cached, reducing API costs.
+- **Smarter Mode Selection** - Mode definitions can now include guidance on when each mode should be used, enabling better orchestration.
+- **Intelligent Context Condensing** - Intelligently summarizes conversation history when context fills up instead of truncating (enable in Settings -> Experimental).
---
@@ -118,7 +118,7 @@ Make Roo Code work your way with:
- **Discord:** [Join our Discord server](https://discord.gg/roocode) for real-time help and discussions
- **Reddit:** [Visit our subreddit](https://www.reddit.com/r/RooCode) to share experiences and tips
-- **GitHub:** Report [issues](https://github.com/RooVetGit/Roo-Code/issues) or request [features](https://github.com/RooVetGit/Roo-Code/discussions/categories/feature-requests?discussions_q=is%3Aopen+category%3A%22Feature+Requests%22+sort%3Atop)
+- **GitHub:** Report [issues](https://github.com/RooCodeInc/Roo-Code/issues) or request [features](https://github.com/RooCodeInc/Roo-Code/discussions/categories/feature-requests?discussions_q=is%3Aopen+category%3A%22Feature+Requests%22+sort%3Atop)
---
@@ -127,7 +127,7 @@ Make Roo Code work your way with:
1. **Clone** the repo:
```sh
-git clone https://github.com/RooVetGit/Roo-Code.git
+git clone https://github.com/RooCodeInc/Roo-Code.git
```
2. **Install dependencies**:
@@ -181,31 +181,34 @@ Thanks to all our contributors who have helped make Roo Code better!
-| mrubens | saoudrizwan | cte | samhvw8 | daniel-lxs | a8trejo |
-| :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |
-| ColemanRoo | stea9499 | joemanley201 | System233 | hannesrudolph | jquanton |
-| nissa-seru | KJ7LNW | NyxJae | MuriloFP | d-oit | punkpeye |
-| Smartsheet-JB-Brown | monotykamary | wkordalski | feifei325 | cannuri | lloydchang |
-| vigneshsubbiah16 | qdaxb | Szpadel | Premshay | psv2522 | diarmidmackenzie |
-| lupuletic | elianiva | olweraltuve | sachasayan | afshawnlotfi | pugazhendhi-m |
-| aheizi | RaySinner | PeterDaveHello | nbihan-mediware | dtrugman | emshvac |
-| kyle-apex | pdecat | zhangtony239 | Lunchb0ne | arthurauffray | upamune |
-| StevenTCramer | sammcj | p12tic | gtaylor | aitoroses | yt3trees |
-| franekp | yongjer | vincentsong | vagadiya | teddyOOXX | eonghk |
-| taisukeoe | heyseth | ross | philfung | napter | mdp |
-| SplittyDev | Chenjiayuan195 | jcbdev | GitlyHallows | bramburn | benzntech |
-| axkirillov | anton-otee | shoopapa | jwcraig | kinandan | kohii |
-| lightrabbit | olup | mecab | nevermorec | im47cn | hongzio |
-| dqroid | dairui1 | bannzai | axmo | asychin | ashktn |
-| eltociear | PretzelVector | cdlliuy | student20880 | shohei-ihaya | shaybc |
-| shariqriazz | seedlord | samir-nimbly | ronyblum | refactorthis | pokutuna |
-| philipnext | oprstchn | nobu007 | mosleyit | moqimoqidea | mlopezr |
-| hesara | DeXtroTip | celestial-vault | linegel | snoyiatk | dbasclpy |
-| dleen | chadgauth | bogdan0083 | Atlogit | atlasgong | andreastempsch |
-| QuinsZouls | alarno | adamwlarson | AMHesch | amittell | Yoshino-Yukitaro |
-| Yikai-Liao | vladstudio | NamesMT | tmsjngx0 | tgfjt | maekawataiki |
-| samsilveira | 01Rian | Sarke | kvokka | marvijo-code | mamertofabian |
-| libertyteeth | shtse8 | Jdo300 | | | |
+| mrubens | saoudrizwan | cte | samhvw8 | daniel-lxs | a8trejo |
+| :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |
+| ColemanRoo | hannesrudolph | KJ7LNW | stea9499 | joemanley201 | System233 |
+| nissa-seru | jquanton | NyxJae | MuriloFP | d-oit | punkpeye |
+| wkordalski | Smartsheet-JB-Brown | monotykamary | elianiva | cannuri | feifei325 |
+| zhangtony239 | sachasayan | lloydchang | vigneshsubbiah16 | Szpadel | qdaxb |
+| lupuletic | Premshay | psv2522 | diarmidmackenzie | aheizi | olweraltuve |
+| jr | dtrugman | nbihan-mediware | PeterDaveHello | RaySinner | pugazhendhi-m |
+| afshawnlotfi | shariqriazz | pdecat | kyle-apex | emshvac | Lunchb0ne |
+| xyOz-dev | arthurauffray | upamune | StevenTCramer | sammcj | p12tic |
+| gtaylor | aitoroses | benzntech | ross | heyseth | taisukeoe |
+| dlab-anton | eonghk | teddyOOXX | vagadiya | vincentsong | yongjer |
+| ashktn | franekp | yt3trees | anton-otee | axkirillov | bramburn |
+| snoyiatk | GitlyHallows | jcbdev | Chenjiayuan195 | julionav | SplittyDev |
+| mdp | napter | philfung | im47cn | shoopapa | jwcraig |
+| kinandan | kohii | lightrabbit | olup | mecab | nevermorec |
+| hongzio | GOODBOY008 | dqroid | dairui1 | bannzai | axmo |
+| asychin | amittell | Yoshino-Yukitaro | Yikai-Liao | SmartManoj | PretzelVector |
+| zetaloop | cdlliuy | student20880 | shohei-ihaya | shaybc | seedlord |
+| samir-nimbly | ronyblum | robertheadley | refactorthis | pokutuna | philipnext |
+| oprstchn | nobu007 | mosleyit | moqimoqidea | mlopezr | zxdvd |
+| DeXtroTip | pfitz | celestial-vault | linegel | dbasclpy | Deon588 |
+| dleen | chadgauth | olearycrew | bogdan0083 | Atlogit | atlasgong |
+| andreastempsch | alasano | QuinsZouls | HadesArchitect | alarno | adamwlarson |
+| AMHesch | vladstudio | NamesMT | tmsjngx0 | tgfjt | maekawataiki |
+| samsilveira | mr-ryan-james | 01Rian | Sarke | kvokka | ecmasx |
+| marvijo-code | mamertofabian | monkeyDluffy6017 | libertyteeth | shtse8 | ksze |
+| Jdo300 | hesara | | | | |
diff --git a/cline_docs/marketplace/README.md b/cline_docs/marketplace/README.md
new file mode 100644
index 0000000000..6e43e25723
--- /dev/null
+++ b/cline_docs/marketplace/README.md
@@ -0,0 +1,50 @@
+# Marketplace Documentation
+
+This directory contains comprehensive documentation for the Roo Code Marketplace, including both user guides and implementation details.
+
+## Documentation Structure
+
+### User Guide
+
+The user guide provides end-user documentation for using the Marketplace:
+
+1. [Introduction to Marketplace](./user-guide/01-introduction.md) - Overview and purpose of the Marketplace
+2. [Browsing Items](./user-guide/02-browsing-items.md) - Understanding the interface and navigating items
+3. [Searching and Filtering](./user-guide/03-searching-and-filtering.md) - Using search and filters to find items
+4. [Working with Package Details](./user-guide/04-working-with-details.md) - Exploring package details and items
+5. [Adding Packages](./user-guide/05-adding-packages.md) - Creating and contributing your own items
+6. [Adding Custom Sources](./user-guide/06-adding-custom-sources.md) - Setting up and managing custom sources
+
+### Implementation Documentation
+
+The implementation documentation provides technical details for developers:
+
+1. [Architecture](./implementation/01-architecture.md) - High-level architecture of the Marketplace
+2. [Core Components](./implementation/02-core-components.md) - Key components and their responsibilities
+3. [Data Structures](./implementation/03-data-structures.md) - Data models and structures used in the Marketplace
+4. [Search and Filter](./implementation/04-search-and-filter.md) - Implementation of search and filtering functionality
+
+## Key Features
+
+The Marketplace provides the following key features:
+
+- **Component Discovery**: Browse and search for items
+- **Item Management**: Add/update/remove items to your environment
+- **Custom Sources**: Add your own repositories of team or private Marketplaces
+- **Localization Support**: View items in your preferred language
+- **Filtering**: Filter items by type, search term, and tags
+
+## Default Marketplace Repository
+
+The default Marketplace repository is located at:
+[https://github.com/RooVetGit/Roo-Code-Marketplace](https://github.com/RooVetGit/Roo-Code-Marketplace)
+
+## Contributing
+
+To contribute to the Marketplace documentation:
+
+1. Make your changes to the relevant markdown files
+2. Ensure that your changes are accurate and consistent with the actual implementation
+3. Submit a pull request with your changes
+
+For code changes to the Marketplace itself, please refer to the main [CONTRIBUTING.md](../../CONTRIBUTING.md) file.
diff --git a/cline_docs/marketplace/implementation/01-architecture.md b/cline_docs/marketplace/implementation/01-architecture.md
new file mode 100644
index 0000000000..9fbce20a20
--- /dev/null
+++ b/cline_docs/marketplace/implementation/01-architecture.md
@@ -0,0 +1,373 @@
+# Marketplace Architecture
+
+This document provides a comprehensive overview of the Marketplace's architecture, including its components, interactions, and data flow.
+
+## System Overview
+
+The Marketplace is built on a modular architecture that separates concerns between data management, UI rendering, and user interactions. The system consists of several key components that work together to provide a seamless experience for discovering, browsing, and managing items.
+
+### High-Level Architecture
+
+```mermaid
+graph TD
+ User[User] -->|Interacts with| UI[Marketplace UI]
+ UI -->|Sends messages| MH[Message Handler]
+ MH -->|Processes requests| PM[MarketplaceManager]
+ PM -->|Validates sources| PSV[MarketplaceSourceValidation]
+ PM -->|Fetches repos| GF[GitFetcher]
+ GF -->|Scans metadata| MS[MetadataScanner]
+ MS -->|Reads| FS[File System / Git Repositories]
+ PM -->|Returns filtered data| MH
+ MH -->|Updates state| UI
+ UI -->|Displays| User
+```
+
+The architecture follows a message-based pattern where:
+
+1. The UI sends messages to the backend through a message handler
+2. The backend processes these messages and returns results
+3. The UI updates based on the returned data
+4. Components are loosely coupled through message passing
+
+## Component Interactions
+
+The Marketplace components interact through a well-defined message flow:
+
+### Core Interaction Patterns
+
+1. **Data Loading**:
+
+ - GitFetcher handles repository cloning and updates
+ - MetadataScanner loads item data from repositories
+ - MarketplaceManager manages caching and concurrency
+ - UI requests data through the message handler
+
+2. **Filtering and Search**:
+
+ - UI sends filter/search criteria to the backend
+ - MarketplaceManager applies filters with match info
+ - Filtered results are returned to the UI
+ - State manager handles view-level filtering
+
+3. **Source Management**:
+ - UI sends source management commands
+ - MarketplaceManager coordinates with GitFetcher
+ - Cache is managed with timeout protection
+ - Sources are processed with concurrency control
+
+## Data Flow Diagram
+
+The following diagram illustrates the data flow through the Marketplace system:
+
+```mermaid
+graph LR
+ subgraph Sources
+ GR[Git Repositories]
+ FS[File System]
+ end
+
+ subgraph Backend
+ GF[GitFetcher]
+ MS[MetadataScanner]
+ PM[MarketplaceManager]
+ MH[Message Handler]
+ end
+
+ subgraph Frontend
+ UI[UI Components]
+ State[State Management]
+ end
+
+ GR -->|Clone/Pull| GF
+ FS -->|Cache| GF
+ GF -->|Metadata| MS
+ MS -->|Parsed Data| PM
+ PM -->|Cached Items| PM
+ UI -->|User Actions| MH
+ MH -->|Messages| PM
+ PM -->|Filtered Data| MH
+ MH -->|Updates| State
+ State -->|Renders| UI
+```
+
+## Sequence Diagrams
+
+### Item Loading Sequence
+
+The following sequence diagram shows how items are loaded from sources:
+
+```mermaid
+sequenceDiagram
+ participant User
+ participant UI as UI Components
+ participant MH as Message Handler
+ participant PM as MarketplaceManager
+ participant GF as GitFetcher
+ participant MS as MetadataScanner
+ participant FS as File System/Git
+
+ User->>UI: Open Marketplace
+ UI->>MH: Send init message
+ MH->>PM: Initialize
+ PM->>GF: Request repository data
+ GF->>FS: Clone/pull repository
+ GF->>MS: Request metadata scan
+ MS->>FS: Read repository data
+ FS-->>MS: Return raw data
+ MS-->>GF: Return parsed metadata
+ GF-->>PM: Return repository data
+ PM-->>MH: Return initial items
+ MH-->>UI: Update with items
+ UI-->>User: Display items
+```
+
+### Search and Filter Sequence
+
+This sequence diagram illustrates the search and filter process:
+
+```mermaid
+sequenceDiagram
+ participant User
+ participant UI as UI Components
+ participant State as State Manager
+ participant MH as Message Handler
+ participant PM as MarketplaceManager
+
+ User->>UI: Enter search term
+ UI->>State: Update filters
+ State->>MH: Send search message
+ MH->>PM: Apply search filter
+ PM->>PM: Filter items with match info
+ PM-->>MH: Return filtered items
+ MH-->>State: Update with filtered items
+ State-->>UI: Update view
+ UI-->>User: Display filtered results
+
+ User->>UI: Select type filter
+ UI->>State: Update type filter
+ State->>MH: Send type filter message
+ MH->>PM: Apply type filter
+ PM->>PM: Filter by type with match info
+ PM-->>MH: Return type-filtered items
+ MH-->>State: Update filtered items
+ State-->>UI: Update view
+ UI-->>User: Display type-filtered results
+```
+
+## Class Diagrams
+
+### Core Classes
+
+The following class diagram shows the main classes in the Marketplace system:
+
+```mermaid
+classDiagram
+ class MarketplaceManager {
+ -currentItems: MarketplaceItem[]
+ -cache: Map
+ -gitFetcher: GitFetcher
+ -activeSourceOperations: Set
+ +getMarketplaceItems(): MarketplaceItem[]
+ +filterItems(filters): MarketplaceItem[]
+ +sortItems(sortBy, order): MarketplaceItem[]
+ +refreshRepository(url): void
+ -queueOperation(operation): void
+ -validateSources(sources): ValidationError[]
+ }
+
+ class MarketplaceSourceValidation {
+ +validateSourceUrl(url): ValidationError[]
+ +validateSourceName(name): ValidationError[]
+ +validateSourceDuplicates(sources): ValidationError[]
+ +validateSource(source): ValidationError[]
+ +validateSources(sources): ValidationError[]
+ -isValidGitRepositoryUrl(url): boolean
+ }
+
+ class GitFetcher {
+ -cacheDir: string
+ -metadataScanner: MetadataScanner
+ +fetchRepository(url): MarketplaceRepository
+ -cloneOrPullRepository(url): void
+ -validateRegistryStructure(dir): void
+ -parseRepositoryMetadata(dir): RepositoryMetadata
+ }
+
+ class MetadataScanner {
+ -git: SimpleGit
+ +scanDirectory(path): MarketplaceItem[]
+ +parseMetadata(file): ComponentMetadata
+ -buildComponentHierarchy(items): MarketplaceItem[]
+ }
+
+ class MarketplaceViewStateManager {
+ -state: ViewState
+ -stateChangeHandlers: Set
+ -fetchTimeoutId: NodeJS.Timeout
+ -sourcesModified: boolean
+ +initialize(): void
+ +onStateChange(handler): () => void
+ +cleanup(): void
+ +getState(): ViewState
+ +transition(transition): Promise
+ -notifyStateChange(): void
+ -clearFetchTimeout(): void
+ -isFilterActive(): boolean
+ -filterItems(items): MarketplaceItem[]
+ -sortItems(items): MarketplaceItem[]
+ +handleMessage(message): Promise
+ }
+
+ MarketplaceManager --> GitFetcher: uses
+ MarketplaceManager --> MarketplaceSourceValidation: uses
+ GitFetcher --> MetadataScanner: uses
+ MarketplaceManager --> MarketplaceViewStateManager: updates
+```
+
+## Component Responsibilities
+
+### Backend Components
+
+1. **GitFetcher**
+
+ - Handles Git repository operations
+ - Manages repository caching
+ - Validates repository structure
+ - Coordinates with MetadataScanner
+
+2. **MetadataScanner**
+
+ - Scans directories and repositories
+ - Parses YAML metadata files
+ - Builds component hierarchies
+ - Handles file system operations
+
+3. **MarketplaceManager**
+
+ - Manages concurrent operations
+ - Handles caching with timeout protection
+ - Coordinates repository operations
+ - Provides filtering and sorting
+
+4. **marketplaceMessageHandler**
+ - Routes messages between UI and backend
+ - Processes commands from the UI
+ - Returns data and status updates
+ - Handles error conditions
+
+### Frontend Components
+
+1. **MarketplaceViewStateManager**
+
+ - Manages frontend state and backend synchronization
+ - Handles state transitions and message processing
+ - Manages filtering, sorting, and view preferences
+ - Coordinates with backend state
+ - Handles timeout protection for operations
+ - Manages source modification tracking
+ - Provides state change subscriptions
+
+2. **MarketplaceSourceValidation**
+
+ - Validates Git repository URLs for any domain
+ - Validates source names and configurations
+ - Detects duplicate sources (case-insensitive)
+ - Provides structured validation errors
+ - Supports multiple Git protocols (HTTPS, SSH, Git)
+
+3. **MarketplaceItemCard**
+
+ - Displays item information
+ - Handles tag interactions
+ - Manages expandable sections
+ - Shows match highlights
+ - Handle item actions.
+
+4. **ExpandableSection**
+
+ - Provides collapsible sections
+ - Manages expand/collapse state
+ - Handles animations
+ - Shows section metadata
+
+5. **TypeGroup**
+ - Groups items by type
+ - Formats item lists
+ - Highlights search matches
+ - Maintains consistent styling
+
+## Performance Considerations
+
+The Marketplace architecture addresses several performance challenges:
+
+1. **Concurrency Control**:
+
+ - Source operations are locked to prevent conflicts
+ - Operations are queued during metadata scanning
+ - Cache timeouts prevent hanging operations
+ - Repository operations are atomic
+
+2. **Efficient Caching**:
+
+ - Repository data is cached with expiry
+ - Cache is cleaned up automatically
+ - Forced refresh available when needed
+ - Cache directories managed efficiently
+
+3. **Smart Filtering**:
+ - Match info tracks filter matches
+ - Filtering happens at multiple levels
+ - View state optimizes re-renders
+ - Search is case-insensitive and normalized
+
+## Error Handling
+
+The architecture includes robust error handling:
+
+1. **Repository Operations**:
+
+ - Git lock files are cleaned up
+ - Failed clones are retried
+ - Corrupt repositories are re-cloned
+ - Network timeouts are handled
+
+2. **Data Processing**:
+
+ - Invalid metadata is gracefully handled
+ - Missing files are reported clearly
+ - Parse errors preserve partial data
+ - Type validation ensures consistency
+
+3. **State Management**:
+ - Invalid filters are normalized
+ - Sort operations handle missing data
+ - View updates are atomic
+ - Error states are preserved
+
+## Extensibility Points
+
+The Marketplace architecture is designed for extensibility:
+
+1. **Repository Sources**:
+
+ - Support for multiple Git providers
+ - Custom repository validation
+ - Flexible metadata formats
+ - Localization support
+
+2. **Filtering System**:
+
+ - Custom filter types
+ - Extensible match info
+ - Flexible sort options
+ - View state customization
+
+3. **UI Components**:
+ - Custom item renderers
+ - Flexible layout system
+ - Theme integration
+ - Accessibility support
+
+---
+
+**Previous**: [Adding Custom Item Sources](../user-guide/06-adding-custom-sources.md) | **Next**: [Core Components](./02-core-components.md)
diff --git a/cline_docs/marketplace/implementation/02-core-components.md b/cline_docs/marketplace/implementation/02-core-components.md
new file mode 100644
index 0000000000..3aa8d96726
--- /dev/null
+++ b/cline_docs/marketplace/implementation/02-core-components.md
@@ -0,0 +1,244 @@
+# Core Components
+
+This document provides detailed information about the core components of the Marketplace system, their responsibilities, implementation details, and interactions.
+
+## GitFetcher
+
+The GitFetcher is responsible for managing Git repository operations, including cloning, pulling, and caching repository data.
+
+### Responsibilities
+
+- Cloning and updating Git repositories
+- Managing repository cache
+- Validating repository structure
+- Coordinating with MetadataScanner
+- Handling repository timeouts and errors
+
+### Implementation Details
+
+[/src/services/marketplace/GitFetcher.ts](/src/services/marketplace/GitFetcher.ts)
+
+### Key Algorithms
+
+#### Repository Management
+
+The repository management process includes:
+
+1. **Cache Management**:
+
+ - Check if repository exists in cache
+ - Validate cache freshness
+ - Clean up stale cache entries
+ - Handle cache directory creation
+
+2. **Repository Operations**:
+
+ - Clone new repositories
+ - Pull updates for existing repos
+ - Handle git lock files
+ - Clean up failed operations
+
+3. **Error Recovery**:
+ - Handle network timeouts
+ - Recover from corrupt repositories
+ - Clean up partial clones
+ - Retry failed operations
+
+## MetadataScanner
+
+The MetadataScanner is responsible for reading and parsing item metadata from repositories.
+
+### Responsibilities
+
+- Scanning directories for item metadata files
+- Parsing YAML metadata into structured objects
+- Building component hierarchies
+- Supporting localized metadata
+- Validating metadata structure
+
+### Implementation Details
+
+[/src/services/marketplace/MetadataScanner.ts](/src/services/marketplace/MetadataScanner.ts)
+
+## MarketplaceManager
+
+The MarketplaceManager is the central component that manages marketplace data, caching, and operations.
+
+### Responsibilities
+
+- Managing concurrent operations
+- Handling repository caching
+- Coordinating with GitFetcher
+- Applying filters and sorting
+- Managing registry sources
+
+### Implementation Details
+
+[/src/services/marketplace/MarketplaceManager.ts](/src/services/marketplace/MarketplaceManager.ts)
+
+### Key Algorithms
+
+#### Concurrency Control
+
+The manager implements sophisticated concurrency control:
+
+1. **Operation Queueing**:
+
+ - Queue operations during active scans
+ - Process operations sequentially
+ - Handle operation dependencies
+ - Maintain operation order
+
+2. **Source Locking**:
+
+ - Lock sources during operations
+ - Prevent concurrent source access
+ - Handle lock timeouts
+ - Clean up stale locks
+
+3. **Cache Management**:
+ - Implement cache expiration
+ - Handle cache invalidation
+ - Clean up unused cache
+ - Optimize cache storage
+
+#### Advanced Filtering
+
+The filtering system provides rich functionality:
+
+1. **Multi-level Filtering**:
+
+ - Filter parent items
+ - Filter subcomponents
+ - Handle item-specific logic
+ - Track match information
+
+2. **Match Information**:
+ - Track match reasons
+ - Handle partial matches
+ - Support highlighting
+ - Maintain match context
+
+## MarketplaceValidation
+
+The MarketplaceValidation component handles validation of marketplace sources and their configurations.
+
+### Responsibilities
+
+- Validating Git repository URLs for any domain
+- Validating source names and configurations
+- Detecting duplicate sources
+- Providing structured validation errors
+- Supporting multiple Git protocols
+
+### Implementation Details
+
+[/src/shared/MarketplaceValidation.ts](/src/shared/MarketplaceValidation.ts)
+
+### Key Algorithms
+
+#### URL Validation
+
+The URL validation system supports:
+
+1. **Protocol Validation**:
+
+ - HTTPS URLs
+ - SSH URLs
+ - Git protocol URLs
+ - Custom domains and ports
+
+2. **Domain Validation**:
+
+ - Any valid domain name
+ - IP addresses
+ - Localhost for testing
+ - Internal company domains
+
+3. **Path Validation**:
+ - Username/organization
+ - Repository name
+ - Optional .git suffix
+ - Subpath support
+
+## MarketplaceViewStateManager
+
+The MarketplaceViewStateManager manages frontend state and synchronization with the backend.
+
+### Responsibilities
+
+- Managing frontend state transitions
+- Handling message processing
+- Managing timeouts and retries
+- Coordinating with backend state
+- Providing state change subscriptions
+- Managing source modification tracking
+- Handling filtering and sorting
+
+### Implementation Details
+
+[/webview-ui/src/components/marketplace/MarketplaceViewStateManager.ts](/webview-ui/src/components/marketplace/MarketplaceViewStateManager.ts)
+
+## Component Integration
+
+The components work together through well-defined interfaces:
+
+### Data Flow
+
+1. **Repository Operations**:
+
+ - MarketplaceManager validates sources with MarketplaceValidation
+ - MarketplaceManager coordinates with GitFetcher
+ - GitFetcher manages repository state
+ - MetadataScanner processes repository content
+ - Results flow back to MarketplaceManager
+
+2. **State Management**:
+
+ - MarketplaceManager maintains backend state
+ - ViewStateManager handles UI state transitions
+ - ViewStateManager processes messages
+ - State changes notify subscribers
+ - Components react to state changes
+ - Timeout protection ensures responsiveness
+
+3. **User Interactions**:
+ - UI events trigger state updates
+ - ViewStateManager processes changes
+ - Changes propagate to backend
+ - Results update UI state
+
+## Performance Optimizations
+
+The system includes several optimizations:
+
+1. **Concurrent Operations**:
+
+ - Operation queueing
+ - Source locking
+ - Parallel processing where safe
+ - Resource management
+
+2. **Efficient Caching**:
+
+ - Multi-level cache
+ - Cache invalidation
+ - Lazy loading
+ - Cache cleanup
+
+3. **Smart Filtering**:
+
+ - Optimized algorithms
+ - Match tracking
+ - Incremental updates
+ - Result caching
+
+4. **State Management**:
+ - Minimal updates
+ - State normalization
+ - Change batching
+ - Update optimization
+
+---
+
+**Previous**: [Marketplace Architecture](./01-architecture.md) | **Next**: [Data Structures](./03-data-structures.md)
diff --git a/cline_docs/marketplace/implementation/03-data-structures.md b/cline_docs/marketplace/implementation/03-data-structures.md
new file mode 100644
index 0000000000..f1db63a498
--- /dev/null
+++ b/cline_docs/marketplace/implementation/03-data-structures.md
@@ -0,0 +1,218 @@
+# Data Structures
+
+This document details the key data structures used in the Marketplace, including their definitions, relationships, and usage patterns.
+
+## Item Types
+
+The Marketplace uses a type system to categorize different kinds of items:
+
+### MarketplaceItemType Enumeration
+
+[/src/services/marketplace/types.ts](/src/services/marketplace/types.ts)
+
+These types represent the different kinds of components that can be managed by the Marketplace:
+
+1. **mode**: AI assistant personalities with specialized capabilities
+2. **prompt**: Pre-configured instructions for specific tasks
+3. **mcp**: Model Context Protocol servers that provide additional functionality
+4. **package**: Collections of items (multiple modes, mcps,..., like `roo-commander`)
+
+## Core Data Structures
+
+### MarketplaceRepository
+
+```typescript
+/**
+ * Represents a repository with its metadata and items
+ */
+export interface MarketplaceRepository {
+ metadata: RepositoryMetadata
+ items: MarketplaceItem[]
+ url: string
+ defaultBranch: string
+ error?: string
+}
+```
+
+This interface represents a complete repository:
+
+- **metadata**: The repository metadata
+- **items**: Array of items in the repository
+- **url**: The URL to the repository
+- **defaultBranch**: The default Git branch (e.g., "main")
+- **error**: Optional error message if there was a problem
+
+### MarketplaceItem
+
+[/src/services/marketplace/types.ts](/src/services/marketplace/types.ts)
+
+Key changes:
+
+- Added **defaultBranch** field for Git branch tracking
+- Enhanced **matchInfo** structure for better filtering
+- Improved subcomponent handling
+
+### MatchInfo
+
+[/src/services/marketplace/types.ts](/src/services/marketplace/types.ts)
+
+Enhanced match tracking:
+
+- Added **typeMatch** for component type filtering
+- More detailed match reasons
+- Support for subcomponent matching
+
+## State Management Structures
+
+### ValidationError
+
+[/src/shared/MarketplaceValidation.ts](/src/shared/MarketplaceValidation.ts)
+
+Used for structured validation errors:
+
+- **field**: The field that failed validation (e.g., "url", "name")
+- **message**: Human-readable error message
+
+### ViewState
+
+[/webview-ui/src/components/marketplace/MarketplaceViewStateManager.ts](/webview-ui/src/components/marketplace/MarketplaceViewStateManager.ts)
+
+Manages UI state:
+
+- **allItems**: All available items
+- **displayItems**: Currently filtered/displayed items
+- **isFetching**: Loading state indicator
+- **activeTab**: Current view tab
+- **refreshingUrls**: Sources being refreshed
+- **sources**: Marketplace sources
+- **filters**: Active filters
+- **sortConfig**: Sort configuration
+
+### ViewStateTransition
+
+[/webview-ui/src/components/marketplace/MarketplaceViewStateManager.ts](/webview-ui/src/components/marketplace/MarketplaceViewStateManager.ts)
+
+Defines state transitions:
+
+- Operation types
+- Optional payloads
+- Type-safe transitions
+
+### Filters
+
+[/webview-ui/src/components/marketplace/MarketplaceViewStateManager.ts](/webview-ui/src/components/marketplace/MarketplaceViewStateManager.ts)
+
+Enhanced filtering:
+
+- Component type filtering
+- Text search
+- Tag-based filtering
+
+## Metadata Interfaces
+
+### BaseMetadata
+
+[/src/services/marketplace/types.ts](/src/services/marketplace/types.ts)
+
+Common metadata properties:
+
+- **name**: Display name
+- **description**: Detailed explanation
+- **version**: Semantic version
+- **tags**: Optional keywords
+
+### ComponentMetadata
+
+[/src/services/marketplace/types.ts](/src/services/marketplace/types.ts)
+
+Added:
+
+- **type** field for item component type
+
+### PackageMetadata
+
+[/src/services/marketplace/types.ts](/src/services/marketplace/types.ts)
+
+Enhanced with:
+
+- Subcomponent tracking
+
+## Source Management
+
+### MarketplaceSource
+
+[/src/services/marketplace/types.ts](/src/services/marketplace/types.ts)
+
+## Message Structures
+
+> TBA
+
+## Data Validation
+
+### Metadata Validation
+
+[/src/services/marketplace/schemas.ts](/src/services/marketplace/schemas.ts)
+
+### URL Validation
+
+[/src/shared/MarketplaceValidation.ts](/src/shared/MarketplaceValidation.ts)
+
+Supports:
+
+- Any valid domain name
+- Multiple Git protocols
+- Optional .git suffix
+- Subpath components
+
+## Data Flow
+
+The Marketplace transforms data through several stages:
+
+1. **Repository Level**:
+
+ - Clone/pull Git repositories
+ - Parse metadata files
+ - Build component hierarchy
+
+2. **Cache Level**:
+
+ - Store repository data
+ - Track timestamps
+ - Handle expiration
+
+3. **View Level**:
+ - Apply filters
+ - Sort items
+ - Track matches
+ - Manage UI state
+
+## Data Relationships
+
+### Component Hierarchy
+
+```
+Repository
+├── Metadata
+└── Items
+ ├── Package
+ │ ├── Mode
+ │ ├── MCP
+ │ └── Prompt
+ └── Standalone Components (Modes, MCP, Prompts)
+```
+
+### State Flow
+
+```
+Git Repository → Cache → Marketplace → ViewState → UI
+```
+
+### Filter Chain
+
+```
+Raw Items → Type Filter → Search Filter → Tag Filter → Sorted Results
+```
+
+---
+
+**Previous**: [Core Components](./02-core-components.md) | **Next**: [Search and Filter Implementation](./04-search-and-filter.md)
diff --git a/cline_docs/marketplace/implementation/04-search-and-filter.md b/cline_docs/marketplace/implementation/04-search-and-filter.md
new file mode 100644
index 0000000000..9d06976eee
--- /dev/null
+++ b/cline_docs/marketplace/implementation/04-search-and-filter.md
@@ -0,0 +1,105 @@
+# Search and Filter Implementation
+
+This document details the implementation of search and filtering functionality in the Marketplace, including algorithms, optimization techniques, and performance considerations.
+
+## Core Filter System
+
+The Marketplace implements a comprehensive filtering system that handles multiple filter types, concurrent operations, and detailed match tracking.
+
+### Filter Implementation
+
+[/src/services/marketplace/MarketplaceManager.ts](/src/services/marketplace/MarketplaceManager.ts)
+
+## Sort System
+
+The Marketplace implements flexible sorting with subcomponent support:
+
+[/src/services/marketplace/MarketplaceManager.ts](/src/services/marketplace/MarketplaceManager.ts)
+
+## State Management Integration
+
+The filtering system integrates with the state management through state transitions:
+
+[/webview-ui/src/components/marketplace/MarketplaceViewStateManager.ts](/webview-ui/src/components/marketplace/MarketplaceViewStateManager.ts)
+
+## Performance Optimizations
+
+### Concurrent Operation Handling
+
+[/src/services/marketplace/MarketplaceManager.ts](/src/services/marketplace/MarketplaceManager.ts)
+
+### Filter Optimizations
+
+1. **Early Termination**:
+
+ - Returns as soon as any field matches
+ - Avoids unnecessary checks
+ - Handles empty filters efficiently
+
+2. **Efficient String Operations**:
+
+ - Normalizes text once
+ - Uses native string methods
+ - Avoids regex for simple matches
+
+3. **State Management**:
+ - State transitions for predictable updates
+ - Subscriber pattern for state changes
+ - Separation of all items and display items
+ - Backend-driven filtering
+ - Optimistic UI updates
+ - Efficient state synchronization
+
+## Testing Strategy
+
+```typescript
+describe("Filter System", () => {
+ describe("Match Tracking", () => {
+ it("should track type matches", () => {
+ const result = filterItems([testItem], { type: "mode" })
+ expect(result[0].matchInfo.matchReason.typeMatch).toBe(true)
+ })
+
+ it("should track subcomponent matches", () => {
+ const result = filterItems([testPack], { search: "test" })
+ const subItem = result[0].items![0]
+ expect(subItem.matchInfo.matched).toBe(true)
+ })
+ })
+
+ describe("Sort System", () => {
+ it("should sort subcomponents", () => {
+ const result = sortItems([testPack], "name", "asc", true)
+ expect(result[0].items).toBeSorted((a, b) => a.metadata.name.localeCompare(b.metadata.name))
+ })
+ })
+})
+```
+
+## Error Handling
+
+The system includes robust error handling:
+
+1. **Filter Errors**:
+
+ - Invalid filter types
+ - Malformed search terms
+ - Missing metadata
+
+2. **Sort Errors**:
+
+ - Invalid sort fields
+ - Missing sort values
+ - Type mismatches
+
+3. **State Errors**:
+ - Invalid state transitions
+ - Message handling errors
+ - State synchronization issues
+ - Timeout handling
+ - Source modification tracking
+ - Filter validation errors
+
+---
+
+**Previous**: [Data Structures](./03-data-structures.md) | **Next**: [UI Component Design](./05-ui-components.md)
diff --git a/cline_docs/marketplace/implementation/05-ui-components.md b/cline_docs/marketplace/implementation/05-ui-components.md
new file mode 100644
index 0000000000..435c2f77ce
--- /dev/null
+++ b/cline_docs/marketplace/implementation/05-ui-components.md
@@ -0,0 +1,398 @@
+# UI Component Design
+
+This document details the design and implementation of the Marketplace's UI components, including their structure, styling, interactions, and accessibility features.
+
+## MarketplaceView
+
+The MarketplaceView is the main container component that manages the overall marketplace interface.
+
+### Component Structure
+
+[/webview-ui/src/components/marketplace/MarketplaceView.tsx](/webview-ui/src/components/marketplace/MarketplaceView.tsx)
+
+### State Management Integration
+
+The component uses the MarketplaceViewStateManager through the useStateManager hook:
+
+```tsx
+const [state, manager] = useStateManager()
+```
+
+Key features:
+
+- Manages tab state (browse/sources)
+- Handles source configuration
+- Coordinates filtering and sorting
+- Manages loading states
+- Handles source validation
+
+## MarketplaceItemCard
+
+The MarketplaceItemCard is the primary component for displaying item information in the UI.
+
+### Component Structure
+
+[/webview-ui/src/components/marketplace/components/MarketplaceItemCard.tsx](/webview-ui/src/components/marketplace/components/MarketplaceItemCard.tsx)
+
+### Design Considerations
+
+1. **Visual Hierarchy**:
+
+ - Clear distinction between header, content, and footer
+ - Type badge stands out with color coding
+ - Important information is emphasized with typography
+
+2. **Interactive Elements**:
+
+ - Tags are clickable for filtering
+ - External link button for source access
+ - Expandable details section for subcomponents
+
+3. **Information Density**:
+
+ - Balanced display of essential information
+ - Optional elements only shown when available
+ - Expandable section for additional details
+
+4. **VSCode Integration**:
+ - Uses VSCode theme variables for colors
+ - Matches VSCode UI patterns
+ - Integrates with VSCode messaging system
+
+## ExpandableSection
+
+The ExpandableSection component provides a collapsible container for content that doesn't need to be visible at all times.
+
+### Component Structure
+
+[/webview-ui/src/components/marketplace/components/ExpandableSection.tsx](/webview-ui/src/components/marketplace/components/ExpandableSection.tsx)
+
+### Design Considerations
+
+1. **Animation**:
+
+ - Smooth height transition for expand/collapse
+ - Opacity change for better visual feedback
+ - Chevron icon rotation for state indication
+
+2. **Accessibility**:
+
+ - Proper ARIA attributes for screen readers
+ - Keyboard navigation support
+ - Clear visual indication of interactive state
+
+3. **Flexibility**:
+
+ - Accepts any content as children
+ - Optional badge for additional information
+ - Customizable through className prop
+
+4. **State Management**:
+ - Internal state for expanded/collapsed
+ - Can be controlled through defaultExpanded prop
+ - Preserves state during component lifecycle
+
+## TypeGroup
+
+The TypeGroup component displays a collection of items of the same type, with special handling for search matches.
+
+### Component Structure
+
+[/webview-ui/src/components/marketplace/components/TypeGroup.tsx](/webview-ui/src/components/marketplace/components/TypeGroup.tsx)
+
+### Design Considerations
+
+1. **List Presentation**:
+
+ - Ordered list with automatic numbering
+ - Clear type heading for context
+ - Consistent spacing for readability
+
+2. **Search Match Highlighting**:
+
+ - Visual distinction for matching items
+ - "match" badge for quick identification
+ - Color change for matched text
+
+3. **Information Display**:
+
+ - Name and description clearly separated
+ - Tooltip shows path information on hover
+ - Truncation for very long descriptions
+
+4. **Empty State Handling**:
+ - Returns null when no items are present
+ - Avoids rendering empty containers
+ - Prevents unnecessary UI elements
+
+## Source Configuration Components
+
+The Marketplace includes components for managing item sources.
+
+[/webview-ui/src/components/marketplace/MarketplaceView.tsx](/webview-ui/src/components/marketplace/MarketplaceView.tsx)
+
+## Filter Components
+
+The Marketplace includes components for filtering and searching.
+
+[/webview-ui/src/components/marketplace/MarketplaceView.tsx](/webview-ui/src/components/marketplace/MarketplaceView.tsx)
+
+### TypeFilterGroup
+
+[/webview-ui/src/components/marketplace/MarketplaceView.tsx](/webview-ui/src/components/marketplace/MarketplaceView.tsx)
+
+### TagFilterGroup
+
+[/webview-ui/src/components/marketplace/MarketplaceView.tsx](/webview-ui/src/components/marketplace/MarketplaceView.tsx)
+
+## Styling Approach
+
+The Marketplace UI uses a combination of Tailwind CSS and VSCode theme variables for styling.
+
+## Responsive Design
+
+The Marketplace UI is designed to work across different viewport sizes:
+
+## Accessibility Features
+
+The Marketplace UI includes several accessibility features:
+
+### Keyboard Navigation
+
+```tsx
+// Example of keyboard navigation support
+
+```
+
+### Screen Reader Support
+
+```tsx
+// Example of screen reader support
+
+
+
+ {/* Details content */}
+
+
+```
+
+### Focus Management
+
+```tsx
+// Example of focus management
+const buttonRef = useRef(null)
+
+useEffect(() => {
+ if (isOpen && buttonRef.current) {
+ buttonRef.current.focus()
+ }
+}, [isOpen])
+
+return (
+
+)
+```
+
+### Color Contrast
+
+The UI ensures sufficient color contrast for all text:
+
+- Text uses VSCode theme variables that maintain proper contrast
+- Interactive elements have clear focus states
+- Color is not the only means of conveying information
+
+## Animation and Transitions
+
+The Marketplace UI uses subtle animations to enhance the user experience:
+
+### Expand/Collapse Animation
+
+```tsx
+// Example of expand/collapse animation
+
+ {children}
+
+```
+
+### Hover Effects
+
+```tsx
+// Example of hover effects
+
+```
+
+### Loading States
+
+```tsx
+// Example of loading state animation
+
+
+ Loading items...
+
+```
+
+## Error Handling in UI
+
+The Marketplace UI includes graceful error handling:
+
+### Error States
+
+```tsx
+// Example of error state display
+const ErrorDisplay: React.FC<{ error: string; retry: () => void }> = ({ error, retry }) => {
+ return (
+
+
+
+
Error loading items
+
+
{error}
+
+
+ )
+}
+```
+
+### Empty States
+
+```tsx
+// Example of empty state display
+const EmptyState: React.FC<{ message: string }> = ({ message }) => {
+ return (
+
+ )
+}
+```
+
+2. **Use the Layout in the Main UI**:
+
+```tsx
+
+ }
+ content={
+
+ {filteredItems.map((item) => (
+
+ ))}
+
+ }
+ footer={
{`Showing ${filteredItems.length} of ${items.length} packages`}
}
+/>
+```
+
+## Extending Backend Functionality
+
+The Marketplace backend can be extended with new functionality:
+
+### Custom Source Providers
+
+To add support for new source types:
+
+1. **Create a Source Provider Interface**:
+
+```typescript
+interface SourceProvider {
+ type: string
+ canHandle(url: string): boolean
+ fetchItems(url: string): Promise
+}
+```
+
+2. **Implement a Custom Provider**:
+
+```typescript
+class CustomSourceProvider implements SourceProvider {
+ type = "custom"
+
+ canHandle(url: string): boolean {
+ return url.startsWith("custom://")
+ }
+
+ async fetchItems(url: string): Promise {
+ // Your custom implementation
+ // Fetch items from your custom source
+ return items
+ }
+}
+```
+
+3. **Register the Provider**:
+
+```typescript
+// In your extension code
+const registerSourceProviders = (marketplace: MarketplaceManager) => {
+ marketplace.registerSourceProvider(new CustomSourceProvider())
+}
+```
+
+### Custom Metadata Processors
+
+To add support for custom metadata formats:
+
+1. **Create a Metadata Processor Interface**:
+
+```typescript
+interface MetadataProcessor {
+ canProcess(filePath: string): boolean
+ process(filePath: string, content: string): Promise
+}
+```
+
+2. **Implement a Custom Processor**:
+
+```typescript
+class CustomMetadataProcessor implements MetadataProcessor {
+ canProcess(filePath: string): boolean {
+ return filePath.endsWith(".custom")
+ }
+
+ async process(filePath: string, content: string): Promise {
+ // Your custom processing logic
+ return processedMetadata
+ }
+}
+```
+
+3. **Register the Processor**:
+
+```typescript
+// In your extension code
+const registerMetadataProcessors = (metadataScanner: MetadataScanner) => {
+ metadataScanner.registerProcessor(new CustomMetadataProcessor())
+}
+```
+
+### Custom Message Handlers
+
+To add support for custom messages:
+
+1. **Extend the Message Handler**:
+
+```typescript
+// In your extension code
+const extendMessageHandler = () => {
+ const originalHandler = handleMarketplaceMessages
+
+ return async (message: any, marketplace: MarketplaceManager) => {
+ // Handle custom messages
+ if (message.type === "yourCustomMessage") {
+ // Your custom message handling
+ return {
+ type: "yourCustomResponse",
+ data: {
+ /* response data */
+ },
+ }
+ }
+
+ // Fall back to the original handler
+ return originalHandler(message, marketplace)
+ }
+}
+```
+
+2. **Register the Extended Handler**:
+
+```typescript
+// In your extension code
+const customMessageHandler = extendMessageHandler()
+context.subscriptions.push(
+ vscode.commands.registerCommand("marketplace.handleMessage", (message) => {
+ return customMessageHandler(message, marketplace)
+ }),
+)
+```
+
+## Integration with Other Systems
+
+The Marketplace can be integrated with other systems:
+
+### Integration with External APIs
+
+To integrate with external APIs:
+
+1. **Create an API Client**:
+
+```typescript
+class ExternalApiClient {
+ private baseUrl: string
+
+ constructor(baseUrl: string) {
+ this.baseUrl = baseUrl
+ }
+
+ async fetchPackages(): Promise {
+ const response = await fetch(`${this.baseUrl}/packages`)
+ const data = await response.json()
+
+ // Transform API data to MarketplaceItem format
+ return data.map((item) => ({
+ name: item.name,
+ description: item.description,
+ type: item.type,
+ url: item.url,
+ repoUrl: item.repository_url,
+ // Map other fields
+ }))
+ }
+}
+```
+
+2. **Create a Source Provider for the API**:
+
+```typescript
+class ApiSourceProvider implements SourceProvider {
+ private apiClient: ExternalApiClient
+
+ constructor(apiUrl: string) {
+ this.apiClient = new ExternalApiClient(apiUrl)
+ }
+
+ type = "api"
+
+ canHandle(url: string): boolean {
+ return url.startsWith("api://")
+ }
+
+ async fetchItems(url: string): Promise {
+ return this.apiClient.fetchPackages()
+ }
+}
+```
+
+3. **Register the API Provider**:
+
+```typescript
+// In your extension code
+const registerApiProvider = (marketplace: MarketplaceManager) => {
+ marketplace.registerSourceProvider(new ApiSourceProvider("https://your-api.example.com"))
+}
+```
+
+### Integration with Authentication Systems
+
+To integrate with authentication systems:
+
+1. **Create an Authentication Provider**:
+
+```typescript
+class AuthProvider {
+ private token: string | null = null
+
+ async login(): Promise {
+ // Your authentication logic
+ this.token = "your-auth-token"
+ return true
+ }
+
+ async getToken(): Promise {
+ if (!this.token) {
+ await this.login()
+ }
+ return this.token
+ }
+
+ isAuthenticated(): boolean {
+ return !!this.token
+ }
+}
+```
+
+2. **Use Authentication in API Requests**:
+
+```typescript
+class AuthenticatedApiClient extends ExternalApiClient {
+ private authProvider: AuthProvider
+
+ constructor(baseUrl: string, authProvider: AuthProvider) {
+ super(baseUrl)
+ this.authProvider = authProvider
+ }
+
+ async fetchPackages(): Promise {
+ const token = await this.authProvider.getToken()
+
+ if (!token) {
+ throw new Error("Authentication required")
+ }
+
+ const response = await fetch(`${this.baseUrl}/packages`, {
+ headers: {
+ Authorization: `Bearer ${token}`,
+ },
+ })
+
+ // Process response as before
+ }
+}
+```
+
+### Integration with Local Development Tools
+
+To integrate with local development tools:
+
+1. **Create a Local Development Provider**:
+
+```typescript
+class LocalDevProvider {
+ private workspacePath: string
+
+ constructor(workspacePath: string) {
+ this.workspacePath = workspacePath
+ }
+
+ async createLocalPackage(template: string, name: string): Promise {
+ const targetPath = path.join(this.workspacePath, name)
+
+ // Create directory
+ await fs.promises.mkdir(targetPath, { recursive: true })
+
+ // Copy template files
+ // Your implementation
+
+ return targetPath
+ }
+
+ async buildLocalPackage(packagePath: string): Promise {
+ // Your build implementation
+ return true
+ }
+
+ async testLocalPackage(packagePath: string): Promise {
+ // Your test implementation
+ return true
+ }
+}
+```
+
+2. **Integrate with the Marketplace**:
+
+```typescript
+// In your extension code
+const registerLocalDevTools = (context: vscode.ExtensionContext) => {
+ const workspaceFolders = vscode.workspace.workspaceFolders
+
+ if (!workspaceFolders) {
+ return
+ }
+
+ const workspacePath = workspaceFolders[0].uri.fsPath
+ const localDevProvider = new LocalDevProvider(workspacePath)
+
+ // Register commands
+ context.subscriptions.push(
+ vscode.commands.registerCommand("marketplace.createLocal", async (template, name) => {
+ return localDevProvider.createLocalPackage(template, name)
+ }),
+
+ vscode.commands.registerCommand("marketplace.buildLocal", async (packagePath) => {
+ return localDevProvider.buildLocalPackage(packagePath)
+ }),
+
+ vscode.commands.registerCommand("marketplace.testLocal", async (packagePath) => {
+ return localDevProvider.testLocalPackage(packagePath)
+ }),
+ )
+}
+```
+
+## Best Practices for Extensions
+
+When extending the Marketplace, follow these best practices:
+
+### Maintainable Code
+
+1. **Follow the Existing Patterns**:
+
+ - Use similar naming conventions
+ - Follow the same code structure
+ - Maintain consistent error handling
+
+2. **Document Your Extensions**:
+
+ - Add JSDoc comments to functions and classes
+ - Explain the purpose of your extensions
+ - Document any configuration options
+
+3. **Write Tests**:
+ - Add unit tests for new functionality
+ - Update integration tests as needed
+ - Ensure test coverage remains high
+
+### Performance Considerations
+
+1. **Lazy Loading**:
+
+ - Load data only when needed
+ - Defer expensive operations
+ - Use pagination for large datasets
+
+2. **Efficient Data Processing**:
+
+ - Minimize data transformations
+ - Use memoization for expensive calculations
+ - Batch operations when possible
+
+3. **UI Responsiveness**:
+ - Keep the UI responsive during operations
+ - Show loading indicators for async operations
+ - Use debouncing for frequent events
+
+### Compatibility
+
+1. **VSCode API Compatibility**:
+
+ - Use stable VSCode API features
+ - Handle API version differences
+ - Test with multiple VSCode versions
+
+2. **Cross-Platform Support**:
+
+ - Test on Windows, macOS, and Linux
+ - Use path.join for file paths
+ - Handle file system differences
+
+3. **Theme Compatibility**:
+ - Use VSCode theme variables
+ - Test with light and dark themes
+ - Support high contrast mode
+
+---
+
+**Previous**: [Testing Strategy](./06-testing-strategy.md)
diff --git a/cline_docs/marketplace/user-guide/01-introduction.md b/cline_docs/marketplace/user-guide/01-introduction.md
new file mode 100644
index 0000000000..4fecb58d2e
--- /dev/null
+++ b/cline_docs/marketplace/user-guide/01-introduction.md
@@ -0,0 +1,58 @@
+# Introduction to Marketplace
+
+## Overview and Purpose
+
+The Marketplace is a powerful feature in Roo Code that allows you to discover, browse, and utilize various items to enhance your development experience. It serves as a centralized hub for accessing:
+
+- **Modes**: Specialized AI assistants with different capabilities
+- **MCP Servers**: Model Context Protocol servers that provide additional functionality
+- **Prompts**: Pre-configured instructions for specific tasks
+- **Packages**: Collections of related components
+
+The Marketplace simplifies the process of extending Roo Code's capabilities by providing a user-friendly interface to find, filter, and add new components to your environment.
+
+## Key Features and Capabilities
+
+### Component Discovery
+
+- Browse a curated collection of components
+- View detailed information about each component
+- Explore subcomponents within packages
+
+### Search and Filter
+
+- Search by name and description
+- Filter by component type (mode, MCP server, etc.)
+- Use tags to find related components
+- Combine search and filters for precise results
+
+### Component Details
+
+- View comprehensive information about each component
+- See version information
+- Access source repositories directly
+- Explore subcomponents organized by type
+
+### Item Management
+
+- Add new components to your environment
+- Manage custom item sources
+- Create and contribute your own packages
+
+## How to Access the Marketplace
+
+The Marketplace can be accessed through the Roo Code extension in VS Code:
+
+1. Open VS Code with the Roo Code extension installed
+2. Click on the Roo Code icon in the activity bar
+3. Select "Marketplace" from the available options
+
+Alternatively, you can use the Command Palette:
+
+1. Press `Ctrl+Shift+P` (Windows/Linux) or `Cmd+Shift+P` (Mac) to open the Command Palette
+2. Type "Roo Code: Open Marketplace"
+3. Press Enter to open the Marketplace
+
+---
+
+**Next**: [Browsing items](./02-browsing-items.md)
diff --git a/cline_docs/marketplace/user-guide/02-browsing-items.md b/cline_docs/marketplace/user-guide/02-browsing-items.md
new file mode 100644
index 0000000000..5a98f51fff
--- /dev/null
+++ b/cline_docs/marketplace/user-guide/02-browsing-items.md
@@ -0,0 +1,148 @@
+# Browsing
+
+## Understanding the Marketplace Interface
+
+The Marketplace interface is designed to provide a clean, intuitive experience for discovering and exploring available components. The main interface consists of several key areas:
+
+### Main Sections
+
+1. **Navigation Tabs**
+
+ - **Browse**: View all available marketplace items
+ - **Sources**: Manage Marketplace sources
+
+2. **Filter Panel**
+
+ - Type filters (Modes, MCP Servers, Packages, etc.)
+ - Search box
+ - Tag filters
+
+3. **Results Area**
+ - Marketplace items displaying component information
+ - Sorting options
+
+### Interface Layout
+
+```
+┌─────────────────────────────────────────────────────────┐
+│ [Browse] [Sources] │
+├─────────────────────────────────────────────────────────┤
+│ FILTERS │
+│ Types: [] Mode [] MCP Server [] Package [] Prompt │
+│ Search: [ ] │
+│ Tags: [Tag cloud] │
+├─────────────────────────────────────────────────────────┤
+│ MARKETPLACE Items │
+│ ┌─────────────────────────────────────────────────┐ │
+│ │ Name [Type] │ │
+│ │ by Author │ │
+│ │ │ │
+│ │ Description text... │ │
+│ │ │ │
+│ │ [Tags] [Tags] [Tags] │ │
+│ │ │ │
+│ │ v1.0.0 Apr 12, 2025 [View] │ │
+│ └─────────────────────────────────────────────────┘ │
+│ │
+│ ┌─────────────────────────────────────────────────┐ │
+│ │ Another Item [Type] │ │
+│ │ ... │ │
+└─────────────────────────────────────────────────────────┘
+```
+
+## Marketplace Item and Information Displayed
+
+Each item in the Marketplace is represented by a card that contains essential information about the component:
+
+### Card Elements
+
+1. **Header Section**
+
+ - **Name**: The name of the component
+ - **Author**: The creator or maintainer of the component (if available)
+ - **Type Badge**: Visual indicator of the component type (Mode, MCP Server, etc.)
+
+2. **Description**
+
+ - A brief overview of the component's purpose and functionality
+
+3. **Tags**
+
+ - Clickable tags that categorize the component
+ - Can be used for filtering similar components
+
+4. **Metadata**
+
+ - **Version**: The current version of the component (if available)
+ - **Last Updated**: When the component was last modified (if available)
+
+5. **Actions**
+
+ - **View**: Button to access the component's source repository or documentation
+
+6. **Details Section** (expandable)
+ - Shows subcomponents grouped by type
+ - Displays additional information when expanded
+
+### Example Item
+
+```
+┌─────────────────────────────────────────────────────┐
+│ Data Platform Package [Package] │
+│ by Roo Team │
+│ │
+│ A comprehensive data processing and analysis │
+│ package with tools for ETL, visualization, and ML. │
+│ │
+│ [data] [analytics] [machine-learning] │
+│ │
+│ v2.1.0 Apr 10, 2025 [View] │
+│ │
+│ ▼ Component Details │
+│ MCP Servers: │
+│ 1. Data Validator - Validates data formats │
+│ 2. ML Predictor - Makes predictions on data │
+│ │
+│ Modes: │
+│ 1. Data Analyst - Helps with data analysis │
+│ 2. ETL Engineer - Assists with data pipelines │
+└─────────────────────────────────────────────────────┘
+```
+
+## Navigating Between Items
+
+The Marketplace provides several ways to navigate through the available items:
+
+### Navigation Methods
+
+1. **Scrolling**
+
+ - Scroll through the list of item cards to browse all available components
+
+2. **Filtering**
+
+ - Use the filter panel to narrow down the displayed items
+ - Click on type filters to show only specific component types
+ - Enter search terms to find items by name or description
+ - Click on tags to filter by specific categories
+
+3. **Sorting**
+
+ - Sort pacitemskages by name or last updated date
+ - Toggle between ascending and descending order
+
+4. **Tab Navigation**
+ - Switch between "Browse" and "Sources" tabs to manage Marketplace sources
+
+### Keyboard Navigation
+
+For accessibility and efficiency, the Marketplace supports keyboard navigation:
+
+- **Tab**: Move focus between interactive elements
+- **Space/Enter**: Activate buttons or toggle filters
+- **Arrow Keys**: Navigate between items
+- **Escape**: Close expanded details or clear filters
+
+---
+
+**Previous**: [Introduction to Marketplace](./01-introduction.md) | **Next**: [Searching and Filtering](./03-searching-and-filtering.md)
diff --git a/cline_docs/marketplace/user-guide/03-searching-and-filtering.md b/cline_docs/marketplace/user-guide/03-searching-and-filtering.md
new file mode 100644
index 0000000000..e63e92f2fc
--- /dev/null
+++ b/cline_docs/marketplace/user-guide/03-searching-and-filtering.md
@@ -0,0 +1,140 @@
+# Searching and Filtering
+
+The Marketplace provides powerful search and filtering capabilities to help you quickly find the components you need. This guide explains how to effectively use these features to narrow down your search results.
+
+## Using the Search Functionality
+
+The search box allows you to find components by matching text in various fields:
+
+### What Gets Searched
+
+When you enter a search term, the Marketplace looks for matches in:
+
+1. **Item Name**: The primary identifier of the item
+2. **Description**: The detailed explanation of the item's purpose
+3. **Subcomponent Names and Descriptions**: Text within nested items
+
+### Search Features
+
+- **Case Insensitive**: Searches ignore letter case for easier matching
+- **Whitespace Insensitive**: Extra spaces are normalized in the search
+- **Partial Matching**: Finds results that contain your search term anywhere in the text
+- **Instant Results**: Results update as you type
+- **Match Highlighting**: Matching subcomponents are highlighted and expanded automatically
+
+### Search Implementation
+
+The search uses a simple string contains match that is case and whitespace insensitive. This means:
+
+- "Data" will match "data", "DATA", "Data", etc.
+- "machine learning" will match "Machine Learning", "machine-learning", etc.
+- Partial words will match: "valid" will match "validation", "validator", etc.
+
+### Search Tips
+
+- Use specific, distinctive terms to narrow results
+- Try different variations if you don't find what you're looking for
+- Search for technology names or specific functionality
+- Look for highlighted "match" indicators in expanded details sections
+
+### Example Searches
+
+| Search Term | Will Find |
+| ------------------ | --------------------------------------------------------------------------- |
+| "data" | Items with "data" in their name, description, or subcomponents |
+| "validator" | Items that include validation functionality or have validator subcomponents |
+| "machine learning" | Items related to machine learning technology |
+
+## Filtering by Item Type
+
+The type filter allows you to focus on specific categories of items:
+
+### Available Type Filters
+
+- **Mode**: AI assistant personalities with specialized capabilities
+- **MCP Server**: Model Context Protocol servers that provide additional functionality
+- **Package**: Collections of related items
+- **Prompt**: Pre-configured instructions for specific tasks
+
+### Using Type Filters
+
+1. Click on a type checkbox to show only items of that type
+2. Select multiple types to show items that match any of the selected types
+3. Clear all type filters to show all items again
+
+When filtering by type, packages are handled specially:
+
+- A package will be included if it matches the selected type
+- A package will also be included if it contains any subcomponents matching the selected type
+- When viewing a package that was included due to its subcomponents, the matching subcomponents will be highlighted
+
+### Type Filter Behavior
+
+- Type filters apply to both the primary item type and it's subcomponents
+- Packages are included if they contain subcomponents matching the selected type
+- The type is displayed as a badge on each item card
+- Type filtering can be combined with search terms and tag filters
+
+## Using Tags for Filtering
+
+Tags provide a way to filter items by category, technology, or purpose:
+
+### Tag Functionality
+
+- Tags appear as clickable buttons on item cards
+- Clicking a tag activates it as a filter
+- Active tag filters are highlighted
+- Items must have at least one of the selected tags to be displayed
+
+### Finding and Using Tags
+
+1. Browse through item cards to discover available tags
+2. Click on a tag to filter for items with that tag
+3. Click on additional tags to expand your filter (items with any of the selected tags will be shown)
+4. Click on an active tag to deactivate it
+
+### Common Tags
+
+- Technology areas: "data", "web", "security", "ai"
+- Programming languages: "python", "javascript", "typescript"
+- Functionality: "testing", "documentation", "analysis"
+- Domains: "finance", "healthcare", "education"
+
+## Combining Search and Filters
+
+For the most precise results, you can combine search terms, type filters, and tag filters:
+
+### How Combined Filtering Works
+
+1. **AND Logic Between Filter Types**: Items must match the search term AND the selected types AND have at least one of the selected tags
+2. **OR Logic Within Tag Filters**: Items must have at least one of the selected tags
+
+### Combined Filter Examples
+
+| Search Term | Type Filter | Tag Filter | Will Find |
+| --------------- | ----------- | ----------------------- | ---------------------------------------------------- |
+| "data" | MCP Server | "analytics" | MCP Servers related to data analytics |
+| "test" | Mode | "automation", "quality" | Test automation or quality-focused modes |
+| "visualization" | Package | "dashboard", "chart" | Packages for creating dashboards or charts |
+| "" | Mode | "" | All modes and packages containing mode subcomponents |
+
+### Clearing Filters
+
+To reset your search and start over:
+
+1. Clear the search box
+2. Uncheck all type filters
+3. Deactivate all tag filters by clicking on them
+
+### Filter Status Indicators
+
+The Marketplace provides visual feedback about your current filters:
+
+- Active type filters are checked
+- Active tag filters are highlighted
+- The search box shows your current search term
+- Result counts may be displayed to show how many items match your filters
+
+---
+
+**Previous**: [Browsing Items](./02-browsing-items.md) | **Next**: [Working with Package Details](./04-working-with-details.md)
diff --git a/cline_docs/marketplace/user-guide/04-working-with-details.md b/cline_docs/marketplace/user-guide/04-working-with-details.md
new file mode 100644
index 0000000000..ac831c52af
--- /dev/null
+++ b/cline_docs/marketplace/user-guide/04-working-with-details.md
@@ -0,0 +1,143 @@
+# Working with Package Details
+
+Marketplace items often contain multiple items organized in a hierarchical structure; these items are referred to as "Packages" and must have a type of `package`. The items organized within a package are referred to as "subitems" and have all the same metadata properties of regular items. This guide explains how to work with the details section of package cards to explore and understand the elements within each package.
+
+## Expanding Package Details
+
+Most packages in the Marketplace contain subcomponents that are hidden by default to keep the interface clean. You can expand these details to see what's inside each package:
+
+### How to Expand Details
+
+1. Look for the "Component Details" section at the bottom of a package card
+2. Click on the section header or the chevron icon (▶) to expand it
+3. The section will animate open, revealing the components inside the package
+4. Click again to collapse the section when you're done
+
+### Automatic Expansion
+
+The details section will expand automatically when:
+
+- Your search term matches text in a subcomponent
+- This is the only condition for automatic expansion
+
+### Details Section Badge
+
+The details section may display a badge with additional information:
+
+- **Match count**: When your search term matches subcomponents, a badge shows how many matches were found (e.g., "3 matches")
+- This helps you quickly identify which packages contain relevant subcomponents
+
+## Understanding Component Types
+
+Components within packages are grouped by their type to make them easier to find and understand:
+
+### Common Component Types
+
+1. **Modes**
+
+ - AI assistant personalities with specialized capabilities
+ - Examples: Code Mode, Architect Mode, Debug Mode
+
+2. **MCP Servers**
+
+ - Model Context Protocol servers that provide additional functionality
+ - Examples: File Analyzer, Data Validator, Image Generator
+
+3. **Prompts**
+
+ - Pre-configured instructions for specific tasks
+ - Examples: Code Review, Documentation Generator, Test Case Creator
+
+4. **Packages**
+ - Nested collections of related components
+ - Can contain any of the other component types
+
+### Type Presentation
+
+Each type section in the details view includes:
+
+- A header with the type name (pluralized, e.g., "MCP Servers")
+- A numbered list of components of that type
+- Each component's name and description
+
+## Viewing Subcomponents
+
+The details section organizes subcomponents in a clear, structured format:
+
+### Subcomponent List Format
+
+```
+Component Details
+ Type Name:
+ 1. Component Name - Description text goes here
+ 2. Another Component - Its description
+
+ Another Type:
+ 1. First Component - Description
+ 2. Second Component - Description
+```
+
+### Subcomponent Information
+
+Each subcomponent in the list displays:
+
+1. **Number**: Sequential number within its type group
+2. **Name**: The name of the subcomponent
+3. **Description**: A brief explanation of the subcomponent's purpose (if available)
+4. **Match Indicator**: A "match" badge appears next to items that match your search term
+
+### Navigating Subcomponents
+
+- Scroll within the details section to see all subcomponents
+- Components are grouped by type, making it easier to find specific functionality
+- Long descriptions may be truncated with an ellipsis (...) to save space (limited to 100 characters)
+
+## Matching Search Terms in Subcomponents
+
+One of the most powerful features of the Marketplace is the ability to search within subcomponents:
+
+### How Subcomponent Matching Works
+
+1. Enter a search term in the search box
+2. The Marketplace searches through all subcomponent names and descriptions
+3. Packages with matching subcomponents remain visible in the results
+4. The details section automatically expands for packages with matches
+5. Matching subcomponents are highlighted and marked with a "match" badge
+
+### Visual Indicators for Matches
+
+When a subcomponent matches your search:
+
+- The component name is highlighted in a different color
+- A "match" badge appears next to the component
+- The details section automatically expands
+- A badge on the details section header shows the number of matches
+
+### Search Implementation
+
+The search uses a simple string contains match that is case-insensitive:
+
+- "validator" will match "Data Validator", "Validator Tool", etc.
+- "valid" will match "validation" or "validator"
+- validator will not match "validation"
+- The search will match any part of the name or description that contains the exact search term
+
+### Example Scenario
+
+If you search for "validator":
+
+1. Packages containing components with "validator" in their name or description remain visible
+2. The details section expands automatically for packages with matching subcomponents
+3. Components like "Data Validator" or those with "validator" in their description are highlighted
+4. A badge might show "2 matches" if two subcomponents match your search term
+
+### Benefits of Subcomponent Matching
+
+- Find functionality buried deep within packages
+- Discover relationships between components
+- Identify packages that contain specific tools or capabilities
+- Locate similar components across different packages
+
+---
+
+**Previous**: [Searching and Filtering](./03-searching-and-filtering.md) | **Next**: [Adding Packages](./05-adding-packages.md)
diff --git a/cline_docs/marketplace/user-guide/05-adding-packages.md b/cline_docs/marketplace/user-guide/05-adding-packages.md
new file mode 100644
index 0000000000..55ebd13e37
--- /dev/null
+++ b/cline_docs/marketplace/user-guide/05-adding-packages.md
@@ -0,0 +1,226 @@
+## Item Structure, Metadata, and Features
+
+### Overview
+
+- Every component on the registry is an `item`.
+- An `item` can be of type: `mcp`, `mode`, `prompt`, `package`
+- Each item apart from `package` is a singular object, i.e: one mode, one mcp server.
+- A `package` contains multiple other `item`s
+ - All internal sub-items of a `package` is contained in the binary on the `package` item metadata itself.
+- Each `item` requires specific metadata files and follows a consistent directory structure.
+
+### Directory Structure
+
+The `registry` structure could be the root or placed in a `registry` directory of any `git` repository, a sample structure for a registry is:
+
+```
+registry/
+├── metadata.en.yml # Required metadata for the registry
+│
+├── modes/ # `mode` items
+│ └── a-mode-name/
+│ └── metadata.en.yml
+├── mcps/ # `mcp` items
+├── prompts/ # `prompt` items
+│
+└── packages/ # `package` items
+ └── a-package-name/
+ ├── metadata.en.yml # Required metadata
+ ├── metadata.fr.yml # Optional localized metadata (French)
+ ├── modes/ # `a-package-name`'s internal `mode` items
+ │ └── my-mode/
+ │ └── metadata.en.yml
+ ├── mcps/ # `a-package-name`'s internal `mcp` items
+ │ └── my-server/
+ │ └── metadata.en.yml
+ └── prompts/ # `a-package-name`'s internal `prompt` items
+ └── my-prompt/
+ └── metadata.en.yml
+```
+
+### Metadata File Format
+
+Metadata files use YAML format and must include specific fields:
+
+#### `registry`:
+
+```yaml
+name: "My Registry"
+description: "A concise description for your registry"
+version: "0.0.0"
+author: "your name" # optional
+authorUrl: "http://your.profile.url/" # optional
+```
+
+#### `item`:
+
+```yaml
+name: "My Package"
+description: "A concise description for your package"
+version: "0.0.0"
+type: "package" # One of: package, mode, mcp, prompt
+sourceUrl: "https://url.to/source-repository" # Optional
+binaryUrl: "https://url.to/binary.zip"
+binaryHash: "SHA256-of-binary"
+binarySource: "https://proof.of/source" # Optional, proof-of-source for the binary (tag/hash reference, build job, etc)
+tags:
+ - tag1
+ - tag2
+author: "your name" # optional
+authorUrl: "http://your.profile.url/" # optional
+```
+
+### Localization Support
+
+You can provide metadata in multiple languages by using locale-specific files:
+
+**Important Notes on Localization:**
+
+- Only files with the pattern `metadata.{locale}.yml` are supported
+- The Marketplace will display metadata in the user's locale if available
+- If the user's locale is not available, it will fall back to English
+- The English locale (`metadata.en.yml`) is required as a fallback
+- Files without a locale code (e.g., just `metadata.yml`) are not supported
+
+### Configurable Support
+
+Powered with [**`Roo Rocket`**](https://github.com/NamesMT/roo-rocket), the registry supports configurable items like:
+
+- `mcp` with access token inputs.
+- `mode` / `prompt` with feature flags.
+- And further customizations that a creator can imagine.
+ - E.g: a `package` could prompt you for the location of its context folder.
+
+## Contributing Process
+
+To contribute your package to the official repository, follow these steps:
+
+### 1. Fork the Repository
+
+1. Visit the official Roo Code Packages repository: [https://github.com/RooVetGit/Roo-Code-Marketplace](https://github.com/RooVetGit/Roo-Code-Marketplace)
+2. Click the "Fork" button in the top-right corner
+3. This creates your own copy of the repository where you can make changes
+
+### 2. Clone Your Fork
+
+Clone your forked repository to your local machine:
+
+```bash
+git clone https://github.com/YOUR-USERNAME/Roo-Code-Marketplace.git
+cd Roo-Code-Marketplace
+```
+
+### 3. Create Your Item
+
+1. Create a new directory for your item with an appropriate name
+2. Add the required metadata files (and subitem directories for `package`)
+3. Follow the structure and format described above
+4. Add `sourceUrl` that points to a repository or post with info/document for the item.
+
+Example of creating a simple package:
+
+```bash
+mkdir -p my-package/modes/my-mode
+touch my-package/metadata.en.yml
+touch my-package/README.md
+touch my-package/modes/my-mode/metadata.en.yml
+```
+
+### 4. Test Your Package
+
+Before submitting, test your package by adding your fork as a custom source in the Marketplace:
+
+1. In VS Code, open the Marketplace
+2. Go to the "Settings" tab
+3. Click "Add Source"
+4. Enter your fork's URL (e.g., `https://github.com/YOUR-USERNAME/Roo-Code-Marketplace`)
+5. Click "Add"
+6. Verify that your package appears and functions correctly
+
+### 5. Commit and Push Your Changes
+
+Once you're satisfied with your package:
+
+```bash
+git add .
+git commit -m "Add my-package with mode component"
+git push origin main
+```
+
+### 6. Create a Pull Request
+
+1. Go to the original repository: [https://github.com/RooVetGit/Roo-Code-Marketplace](https://github.com/RooVetGit/Roo-Code-Marketplace)
+2. Click "Pull Requests" and then "New Pull Request"
+3. Click "Compare across forks"
+4. Select your fork as the head repository
+5. Click "Create Pull Request"
+6. Provide a clear title and description of your package
+7. Submit the pull request
+
+### 7. Review Process
+
+After submitting your pull request:
+
+1. Maintainers will review your package
+2. They may request changes or improvements
+3. Once approved, your package will be merged into the main repository
+4. Your package will be available to all users of the Marketplace
+
+## Best Practices
+
+- **Clear Documentation**: Include detailed documentation in your README.md
+- **Descriptive Metadata**: Write clear, informative descriptions
+- **Appropriate Tags**: Use relevant tags to make your package discoverable
+- **Testing**: Thoroughly test your package before submitting
+- **Localization**: Consider providing metadata in multiple languages
+- **Semantic Versioning**: Follow semantic versioning for version numbers
+- **Consistent Naming**: Use clear, descriptive names for components
+
+## Example package metadatas
+
+### Data Science Toolkit
+
+Here's an example of a data science package:
+
+**data-science-toolkit/metadata.en.yml**:
+
+```yaml
+name: "Data Science Toolkit"
+description: "A comprehensive collection of tools for data science workflows"
+version: "1.0.0"
+type: "package"
+tags:
+ - data
+ - science
+ - analysis
+ - visualization
+ - machine learning
+```
+
+**data-science-toolkit/modes/data-scientist-mode/metadata.en.yml**:
+
+```yaml
+name: "Data Scientist Mode"
+description: "A specialized mode for data science tasks"
+version: "1.0.0"
+type: "mode"
+tags:
+ - data
+ - science
+ - analysis
+```
+
+**data-science-toolkit/prompts/data-cleaning/metadata.en.yml**:
+
+```yaml
+name: "Data Cleaning Prompt"
+description: "A prompt for cleaning and preprocessing datasets"
+version: "1.0.0"
+type: "prompt"
+tags:
+ - data
+ - cleaning
+ - preprocessing
+```
+
+**Previous**: [Working with Package Details](./04-working-with-details.md) | **Next**: [Adding Custom Sources](./06-adding-custom-sources.md)
diff --git a/cline_docs/marketplace/user-guide/06-adding-custom-sources.md b/cline_docs/marketplace/user-guide/06-adding-custom-sources.md
new file mode 100644
index 0000000000..7baf639631
--- /dev/null
+++ b/cline_docs/marketplace/user-guide/06-adding-custom-sources.md
@@ -0,0 +1,152 @@
+# Adding Custom Marketplace Sources
+
+The Marketplace allows you to extend its functionality by adding custom sources. This guide explains how to set up and manage your own Marktplace repositories to access additional components beyond the default offerings.
+
+## Setting up a Marketplace Source Repository
+
+A Marketplace source repository is a Git repository that contains Marketplace items organized in a specific structure. You can create your own repository to host custom packages:
+
+### Repository Requirements
+
+1. **Proper Structure**: The repository must follow the required directory structure
+2. **Valid Metadata**: Each package must include properly formatted metadata files
+3. **Git Repository**: The source must be a Git repository accessible via HTTPS
+
+### Building your registry repository
+
+#### Start from a sample registry repository
+
+Check the branches of the [**rm-samples**](https://github.com/NamesMT/rm-samples) repository here.
+
+#### Creating a New Repository
+
+1. Create a new repository on GitHub, GitLab, or another Git hosting service
+2. Initialize the repository with a README.md file
+3. Clone the repository to your local machine:
+
+```bash
+git clone https://github.com/your-username/your-registry-repo.git
+cd your-registry-repo
+```
+
+4. Create the basic registry structure:
+
+```bash
+mkdir -p packages modes mcps prompts
+touch metadata.en.yml
+```
+
+5. Add repository metadata to `metadata.en.yml`:
+
+```yaml
+name: "Your Repository Name"
+description: "A collection of custom packages for Roo Code"
+version: "1.0.0"
+```
+
+6. Commit and push the initial structure:
+
+```bash
+git add .
+git commit -m "Initialize package repository structure"
+git push origin main
+```
+
+## Adding Sources to Roo Code
+
+Once you have a properly structured source repository, you can add it to your Roo Code Marketplace as a source:
+
+### Default Package Source
+
+Roo Code comes with a default package source:
+
+- URL: `https://github.com/RooVetGit/Roo-Code-Marketplace`
+- This source is enabled by default, and anytime all sources have been deleted.
+
+### Adding a New Source
+
+1. Open VS Code with the Roo Code extension
+2. Navigate to the Marketplace
+3. Switch to the "Sources" tab
+4. Click the "Add Source" button
+5. Enter the repository URL:
+ - Format: `https://github.com/username/repository.git`
+ - Example: `https://github.com/your-username/your-registry-repo.git`
+6. Click "Add" to save the source
+
+### Managing Sources
+
+The "Sources" tab provides several options for managing your registry sources:
+
+1. **Remove**: Delete a source from your configuration
+2. **Refresh**: Update the item list from a source - this is forced git clone/pull to override local caching of data
+
+### Source Caching and Refreshing
+
+Marketplace sources are cached to improve performance:
+
+- **Cache Duration**: Sources are cached for 1 hour (3600000 ms)
+- **Force Refresh**: To force an immediate refresh of a source:
+ 1. Go to the "Sources" tab
+ 2. Click the "Refresh" button next to the source you want to update
+ 3. This will bypass the cache and fetch the latest data from the repository
+
+### Troubleshooting Sources
+
+If a source isn't loading properly:
+
+1. Check that the repository URL is correct
+2. Ensure the repository follows the required structure
+3. Look for error messages in the Marketplace interface
+4. Try refreshing the sources list
+5. Disable and re-enable the source
+
+## Creating Private Sources
+
+For team or organization use, you might want to create private sources:
+
+### Private Repository Setup
+
+1. Create a private repository on your Git hosting service
+2. Follow the same structure requirements as public repositories
+3. Set up appropriate access controls for your team members
+
+### Authentication Options
+
+To access private repositories, you may need to:
+
+1. Configure Git credentials on your system
+2. Use a personal access token with appropriate permissions
+3. Set up SSH keys for authentication
+
+### Organization Best Practices
+
+For teams and organizations:
+
+1. Designate maintainers responsible for the source
+2. Establish quality standards for contributed items and packages
+3. Create a review process for new additions
+4. Document usage guidelines for team members
+5. Consider implementing versioning for your items and packages
+
+## Using Multiple Sources
+
+The Marketplace supports multiple sources simultaneously:
+
+### Benefits of Multiple Sources
+
+- Access components from different providers
+- Separate internal and external components
+- Test new work before contributing them to the main repository
+- Create specialized sources for different projects or teams
+
+### Source Management Strategy
+
+1. Keep the default source enabled for core components
+2. Add specialized sources for specific needs
+3. Create a personal source for testing and development
+4. Refresh sources after you've pushed changes to them to get the latest items
+
+---
+
+**Previous**: [Adding Packages](./05-adding-packages.md) | **Next**: [Marketplace Architecture](../implementation/01-architecture.md)
diff --git a/cline_docs/bedrock/bedrock-cache-strategy-documentation.md b/docs/bedrock/bedrock-cache-strategy-documentation.md
similarity index 100%
rename from cline_docs/bedrock/bedrock-cache-strategy-documentation.md
rename to docs/bedrock/bedrock-cache-strategy-documentation.md
diff --git a/cline_docs/bedrock/model-identification.md b/docs/bedrock/model-identification.md
similarity index 100%
rename from cline_docs/bedrock/model-identification.md
rename to docs/bedrock/model-identification.md
diff --git a/cline_docs/settings.md b/docs/settings.md
similarity index 100%
rename from cline_docs/settings.md
rename to docs/settings.md
diff --git a/e2e/.vscode-test.mjs b/e2e/.vscode-test.mjs
index ccc8b495ea..c83f12c4bb 100644
--- a/e2e/.vscode-test.mjs
+++ b/e2e/.vscode-test.mjs
@@ -2,18 +2,15 @@
* See: https://code.visualstudio.com/api/working-with-extensions/testing-extension
*/
-import { defineConfig } from '@vscode/test-cli';
+import { defineConfig } from "@vscode/test-cli"
export default defineConfig({
- label: 'integrationTest',
- files: 'out/suite/**/*.test.js',
- workspaceFolder: '.',
+ label: "integrationTest",
+ files: "out/suite/**/*.test.js",
+ workspaceFolder: ".",
mocha: {
- ui: 'tdd',
+ ui: "tdd",
timeout: 60000,
},
- launchArgs: [
- '--enable-proposed-api=RooVeterinaryInc.roo-cline',
- '--disable-extensions'
- ]
-});
+ launchArgs: ["--enable-proposed-api=RooVeterinaryInc.roo-cline", "--disable-extensions"],
+})
diff --git a/e2e/package-lock.json b/e2e/package-lock.json
index 278df120c2..bb6a34c082 100644
--- a/e2e/package-lock.json
+++ b/e2e/package-lock.json
@@ -8,11 +8,12 @@
"name": "e2e",
"version": "0.1.0",
"devDependencies": {
+ "@roo-code/types": "^1.12.0",
"@types/mocha": "^10.0.10",
- "@vscode/test-cli": "^0.0.9",
+ "@vscode/test-cli": "^0.0.10",
"@vscode/test-electron": "^2.4.0",
"mocha": "^11.1.0",
- "typescript": "^5.4.5"
+ "typescript": "5.8.3"
}
},
"node_modules/@bcoe/v8-coverage": {
@@ -89,6 +90,16 @@
"node": ">=14"
}
},
+ "node_modules/@roo-code/types": {
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@roo-code/types/-/types-1.12.0.tgz",
+ "integrity": "sha512-djdZ4lzsiOc+umX357JvcSwRlAMm05P+8DU58IFyZERmEh8wkm4TglDuaaRVGtQSHw9YGFikqfruLtZSEb7zJQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "zod": "^3.24.4"
+ }
+ },
"node_modules/@types/istanbul-lib-coverage": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz",
@@ -104,9 +115,9 @@
"license": "MIT"
},
"node_modules/@vscode/test-cli": {
- "version": "0.0.9",
- "resolved": "https://registry.npmjs.org/@vscode/test-cli/-/test-cli-0.0.9.tgz",
- "integrity": "sha512-vsl5/ueE3Jf0f6XzB0ECHHMsd5A0Yu6StElb8a+XsubZW7kHNAOw4Y3TSSuDzKEpLnJ92nbMy1Zl+KLGCE6NaA==",
+ "version": "0.0.10",
+ "resolved": "https://registry.npmjs.org/@vscode/test-cli/-/test-cli-0.0.10.tgz",
+ "integrity": "sha512-B0mMH4ia+MOOtwNiLi79XhA+MLmUItIC8FckEuKrVAVriIuSWjt7vv4+bF8qVFiNFe4QRfzPaIZk39FZGWEwHA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -318,16 +329,16 @@
}
},
"node_modules/@vscode/test-electron": {
- "version": "2.4.1",
- "resolved": "https://registry.npmjs.org/@vscode/test-electron/-/test-electron-2.4.1.tgz",
- "integrity": "sha512-Gc6EdaLANdktQ1t+zozoBVRynfIsMKMc94Svu1QreOBC8y76x4tvaK32TljrLi1LI2+PK58sDVbL7ALdqf3VRQ==",
+ "version": "2.5.2",
+ "resolved": "https://registry.npmjs.org/@vscode/test-electron/-/test-electron-2.5.2.tgz",
+ "integrity": "sha512-8ukpxv4wYe0iWMRQU18jhzJOHkeGKbnw7xWRX3Zw1WJA4cEKbHcmmLPdPrPtL6rhDcrlCZN+xKRpv09n4gRHYg==",
"dev": true,
"license": "MIT",
"dependencies": {
"http-proxy-agent": "^7.0.2",
"https-proxy-agent": "^7.0.5",
"jszip": "^3.10.1",
- "ora": "^7.0.1",
+ "ora": "^8.1.0",
"semver": "^7.6.2"
},
"engines": {
@@ -411,27 +422,6 @@
"dev": true,
"license": "MIT"
},
- "node_modules/base64-js": {
- "version": "1.5.1",
- "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
- "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
- "dev": true,
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/feross"
- },
- {
- "type": "patreon",
- "url": "https://www.patreon.com/feross"
- },
- {
- "type": "consulting",
- "url": "https://feross.org/support"
- }
- ],
- "license": "MIT"
- },
"node_modules/binary-extensions": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
@@ -445,33 +435,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/bl": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/bl/-/bl-5.1.0.tgz",
- "integrity": "sha512-tv1ZJHLfTDnXE6tMHv73YgSJaWR2AFuPwMntBe7XL/GBFHnT0CLnsHMogfk5+GzCDC5ZWarSCYaIGATZt9dNsQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "buffer": "^6.0.3",
- "inherits": "^2.0.4",
- "readable-stream": "^3.4.0"
- }
- },
- "node_modules/bl/node_modules/readable-stream": {
- "version": "3.6.2",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
- "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "inherits": "^2.0.3",
- "string_decoder": "^1.1.1",
- "util-deprecate": "^1.0.1"
- },
- "engines": {
- "node": ">= 6"
- }
- },
"node_modules/brace-expansion": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
@@ -502,31 +465,6 @@
"dev": true,
"license": "ISC"
},
- "node_modules/buffer": {
- "version": "6.0.3",
- "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
- "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
- "dev": true,
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/feross"
- },
- {
- "type": "patreon",
- "url": "https://www.patreon.com/feross"
- },
- {
- "type": "consulting",
- "url": "https://feross.org/support"
- }
- ],
- "license": "MIT",
- "dependencies": {
- "base64-js": "^1.3.1",
- "ieee754": "^1.2.1"
- }
- },
"node_modules/c8": {
"version": "9.1.0",
"resolved": "https://registry.npmjs.org/c8/-/c8-9.1.0.tgz",
@@ -622,16 +560,16 @@
}
},
"node_modules/cli-cursor": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz",
- "integrity": "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==",
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz",
+ "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "restore-cursor": "^4.0.0"
+ "restore-cursor": "^5.0.0"
},
"engines": {
- "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ "node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
@@ -965,6 +903,19 @@
"node": "6.* || 8.* || >= 10.*"
}
},
+ "node_modules/get-east-asian-width": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.3.0.tgz",
+ "integrity": "sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/glob": {
"version": "10.4.5",
"resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz",
@@ -1061,27 +1012,6 @@
"node": ">= 14"
}
},
- "node_modules/ieee754": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
- "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
- "dev": true,
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/feross"
- },
- {
- "type": "patreon",
- "url": "https://www.patreon.com/feross"
- },
- {
- "type": "consulting",
- "url": "https://feross.org/support"
- }
- ],
- "license": "BSD-3-Clause"
- },
"node_modules/immediate": {
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
@@ -1374,14 +1304,17 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/mimic-fn": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
- "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
+ "node_modules/mimic-function": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz",
+ "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==",
"dev": true,
"license": "MIT",
"engines": {
- "node": ">=6"
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/minimatch": {
@@ -1411,15 +1344,14 @@
}
},
"node_modules/mocha": {
- "version": "11.1.0",
- "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.1.0.tgz",
- "integrity": "sha512-8uJR5RTC2NgpY3GrYcgpZrsEd9zKbPDpob1RezyR2upGHRQtHWofmzTMzTMSV6dru3tj5Ukt0+Vnq1qhFEEwAg==",
+ "version": "11.2.2",
+ "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.2.2.tgz",
+ "integrity": "sha512-VlSBxrPYHK4YNOEbFdkCxHQbZMoNzBkoPprqtZRW6311EUF/DlSxoycE2e/2NtRk4WKkIXzyrXDTrlikJMWgbw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "ansi-colors": "^4.1.3",
"browser-stdout": "^1.3.1",
- "chokidar": "^3.5.3",
+ "chokidar": "^4.0.1",
"debug": "^4.3.5",
"diff": "^5.2.0",
"escape-string-regexp": "^4.0.0",
@@ -1430,6 +1362,7 @@
"log-symbols": "^4.1.0",
"minimatch": "^5.1.6",
"ms": "^2.1.3",
+ "picocolors": "^1.1.1",
"serialize-javascript": "^6.0.2",
"strip-json-comments": "^3.1.1",
"supports-color": "^8.1.1",
@@ -1446,6 +1379,22 @@
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
+ "node_modules/mocha/node_modules/chokidar": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz",
+ "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "readdirp": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 14.16.0"
+ },
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ }
+ },
"node_modules/mocha/node_modules/minimatch": {
"version": "5.1.6",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
@@ -1459,6 +1408,20 @@
"node": ">=10"
}
},
+ "node_modules/mocha/node_modules/readdirp": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz",
+ "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 14.18.0"
+ },
+ "funding": {
+ "type": "individual",
+ "url": "https://paulmillr.com/funding/"
+ }
+ },
"node_modules/mocha/node_modules/supports-color": {
"version": "8.1.1",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
@@ -1503,40 +1466,40 @@
}
},
"node_modules/onetime": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
- "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz",
+ "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "mimic-fn": "^2.1.0"
+ "mimic-function": "^5.0.0"
},
"engines": {
- "node": ">=6"
+ "node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/ora": {
- "version": "7.0.1",
- "resolved": "https://registry.npmjs.org/ora/-/ora-7.0.1.tgz",
- "integrity": "sha512-0TUxTiFJWv+JnjWm4o9yvuskpEJLXTcng8MJuKd+SzAzp2o+OP3HWqNhB4OdJRt1Vsd9/mR0oyaEYlOnL7XIRw==",
+ "version": "8.2.0",
+ "resolved": "https://registry.npmjs.org/ora/-/ora-8.2.0.tgz",
+ "integrity": "sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw==",
"dev": true,
"license": "MIT",
"dependencies": {
"chalk": "^5.3.0",
- "cli-cursor": "^4.0.0",
- "cli-spinners": "^2.9.0",
+ "cli-cursor": "^5.0.0",
+ "cli-spinners": "^2.9.2",
"is-interactive": "^2.0.0",
- "is-unicode-supported": "^1.3.0",
- "log-symbols": "^5.1.0",
- "stdin-discarder": "^0.1.0",
- "string-width": "^6.1.0",
+ "is-unicode-supported": "^2.0.0",
+ "log-symbols": "^6.0.0",
+ "stdin-discarder": "^0.2.2",
+ "string-width": "^7.2.0",
"strip-ansi": "^7.1.0"
},
"engines": {
- "node": ">=16"
+ "node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
@@ -1563,28 +1526,41 @@
"license": "MIT"
},
"node_modules/ora/node_modules/is-unicode-supported": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz",
- "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==",
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz",
+ "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==",
"dev": true,
"license": "MIT",
"engines": {
- "node": ">=12"
+ "node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/ora/node_modules/log-symbols": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-5.1.0.tgz",
- "integrity": "sha512-l0x2DvrW294C9uDCoQe1VSU4gf529FkSZ6leBl4TiqZH/e+0R7hSfHQBNut2mNygDgHwvYHfFLn6Oxb3VWj2rA==",
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-6.0.0.tgz",
+ "integrity": "sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "chalk": "^5.0.0",
- "is-unicode-supported": "^1.1.0"
+ "chalk": "^5.3.0",
+ "is-unicode-supported": "^1.3.0"
},
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/ora/node_modules/log-symbols/node_modules/is-unicode-supported": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz",
+ "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==",
+ "dev": true,
+ "license": "MIT",
"engines": {
"node": ">=12"
},
@@ -1593,18 +1569,18 @@
}
},
"node_modules/ora/node_modules/string-width": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-6.1.0.tgz",
- "integrity": "sha512-k01swCJAgQmuADB0YIc+7TuatfNvTBVOoaUWJjTB9R4VJzR5vNWzf5t42ESVZFPS8xTySF7CAdV4t/aaIm3UnQ==",
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
+ "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "eastasianwidth": "^0.2.0",
- "emoji-regex": "^10.2.1",
- "strip-ansi": "^7.0.1"
+ "emoji-regex": "^10.3.0",
+ "get-east-asian-width": "^1.0.0",
+ "strip-ansi": "^7.1.0"
},
"engines": {
- "node": ">=16"
+ "node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
@@ -1703,6 +1679,13 @@
"url": "https://github.com/sponsors/isaacs"
}
},
+ "node_modules/picocolors": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+ "dev": true,
+ "license": "ISC"
+ },
"node_modules/picomatch": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
@@ -1773,29 +1756,22 @@
}
},
"node_modules/restore-cursor": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz",
- "integrity": "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==",
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz",
+ "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "onetime": "^5.1.0",
- "signal-exit": "^3.0.2"
+ "onetime": "^7.0.0",
+ "signal-exit": "^4.1.0"
},
"engines": {
- "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ "node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/restore-cursor/node_modules/signal-exit": {
- "version": "3.0.7",
- "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
- "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
- "dev": true,
- "license": "ISC"
- },
"node_modules/safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
@@ -1870,16 +1846,13 @@
}
},
"node_modules/stdin-discarder": {
- "version": "0.1.0",
- "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.1.0.tgz",
- "integrity": "sha512-xhV7w8S+bUwlPTb4bAOUQhv8/cSS5offJuX8GQGq32ONF0ZtDWKfkdomM3HMRA+LhX6um/FZ0COqlwsjD53LeQ==",
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.2.2.tgz",
+ "integrity": "sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==",
"dev": true,
"license": "MIT",
- "dependencies": {
- "bl": "^5.0.0"
- },
"engines": {
- "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ "node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
@@ -2110,9 +2083,9 @@
}
},
"node_modules/typescript": {
- "version": "5.8.2",
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz",
- "integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==",
+ "version": "5.8.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
+ "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
"dev": true,
"license": "Apache-2.0",
"bin": {
@@ -2382,6 +2355,16 @@
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
+ },
+ "node_modules/zod": {
+ "version": "3.25.8",
+ "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.8.tgz",
+ "integrity": "sha512-iJPWX8HoZ2VE21VrhHGU9jVo/kVDUQyqM9vF0MxDhW/fp2sAl1eVwGJgiYZdHGiMwQJImXIW80lKk0MnfDxqiQ==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/colinhacks"
+ }
}
}
}
diff --git a/e2e/package.json b/e2e/package.json
index aec42f93f1..22d76db6b5 100644
--- a/e2e/package.json
+++ b/e2e/package.json
@@ -3,19 +3,19 @@
"version": "0.1.0",
"private": true,
"scripts": {
- "lint": "eslint src/**/*.ts",
+ "lint": "eslint src/**/*.ts --max-warnings=0",
"check-types": "tsc --noEmit",
"test": "npm run build && npx dotenvx run -f .env.local -- node ./out/runTest.js",
"ci": "npm run vscode-test && npm run test",
"build": "rimraf out && tsc -p tsconfig.json",
"vscode-test": "cd .. && npm run vscode-test"
},
- "dependencies": {},
"devDependencies": {
+ "@roo-code/types": "^1.12.0",
"@types/mocha": "^10.0.10",
- "@vscode/test-cli": "^0.0.9",
+ "@vscode/test-cli": "^0.0.10",
"@vscode/test-electron": "^2.4.0",
"mocha": "^11.1.0",
- "typescript": "^5.4.5"
+ "typescript": "5.8.3"
}
}
diff --git a/e2e/src/suite/extension.test.ts b/e2e/src/suite/extension.test.ts
index 1f44824610..54544a2627 100644
--- a/e2e/src/suite/extension.test.ts
+++ b/e2e/src/suite/extension.test.ts
@@ -1,24 +1,47 @@
import * as assert from "assert"
import * as vscode from "vscode"
+import { Package } from "@roo-code/types"
+
suite("Roo Code Extension", () => {
test("Commands should be registered", async () => {
const expectedCommands = [
- "roo-cline.plusButtonClicked",
- "roo-cline.mcpButtonClicked",
- "roo-cline.historyButtonClicked",
- "roo-cline.popoutButtonClicked",
- "roo-cline.settingsButtonClicked",
- "roo-cline.openInNewTab",
- "roo-cline.explainCode",
- "roo-cline.fixCode",
- "roo-cline.improveCode",
+ "SidebarProvider.open",
+ "SidebarProvider.focus",
+ "SidebarProvider.resetViewLocation",
+ "SidebarProvider.toggleVisibility",
+ "SidebarProvider.removeView",
+ "activationCompleted",
+ "plusButtonClicked",
+ "mcpButtonClicked",
+ "promptsButtonClicked",
+ "popoutButtonClicked",
+ "openInNewTab",
+ "settingsButtonClicked",
+ "historyButtonClicked",
+ "showHumanRelayDialog",
+ "registerHumanRelayCallback",
+ "unregisterHumanRelayCallback",
+ "handleHumanRelayResponse",
+ "newTask",
+ "setCustomStoragePath",
+ "focusInput",
+ "acceptInput",
+ "explainCode",
+ "fixCode",
+ "improveCode",
+ "addToContext",
+ "terminalAddToContext",
+ "terminalFixCommand",
+ "terminalExplainCommand",
]
- const commands = await vscode.commands.getCommands(true)
+ const commands = new Set(
+ (await vscode.commands.getCommands(true)).filter((cmd) => cmd.startsWith(Package.name)),
+ )
- for (const cmd of expectedCommands) {
- assert.ok(commands.includes(cmd), `Command ${cmd} should be registered`)
+ for (const command of expectedCommands) {
+ assert.ok(commands.has(`${Package.name}.${command}`), `Command ${command} should be registered`)
}
})
})
diff --git a/e2e/src/suite/index.ts b/e2e/src/suite/index.ts
index 1a3e265662..5331ef37c3 100644
--- a/e2e/src/suite/index.ts
+++ b/e2e/src/suite/index.ts
@@ -3,7 +3,7 @@ import Mocha from "mocha"
import { glob } from "glob"
import * as vscode from "vscode"
-import type { RooCodeAPI } from "../../../src/exports/roo-code"
+import { type RooCodeAPI, Package } from "@roo-code/types"
import { waitFor } from "./utils"
@@ -12,7 +12,7 @@ declare global {
}
export async function run() {
- const extension = vscode.extensions.getExtension("RooVeterinaryInc.roo-cline")
+ const extension = vscode.extensions.getExtension(`${Package.publisher}.${Package.name}`)
if (!extension) {
throw new Error("Extension not found")
@@ -26,7 +26,7 @@ export async function run() {
openRouterModelId: "google/gemini-2.0-flash-001",
})
- await vscode.commands.executeCommand("roo-cline.SidebarProvider.focus")
+ await vscode.commands.executeCommand(`${Package.name}.SidebarProvider.focus`)
await waitFor(() => api.isReady())
// Expose the API to the tests.
diff --git a/e2e/src/suite/modes.test.ts b/e2e/src/suite/modes.test.ts
index f5cd2141f3..286ab2ce8c 100644
--- a/e2e/src/suite/modes.test.ts
+++ b/e2e/src/suite/modes.test.ts
@@ -1,6 +1,6 @@
import * as assert from "assert"
-import type { ClineMessage } from "../../../src/exports/roo-code"
+import type { ClineMessage } from "@roo-code/types"
import { waitUntilCompleted } from "./utils"
diff --git a/e2e/src/suite/subtasks.test.ts b/e2e/src/suite/subtasks.test.ts
index 513b4c218e..b5ff033d12 100644
--- a/e2e/src/suite/subtasks.test.ts
+++ b/e2e/src/suite/subtasks.test.ts
@@ -1,10 +1,10 @@
import * as assert from "assert"
-import type { ClineMessage } from "../../../src/exports/roo-code"
+import type { ClineMessage } from "@roo-code/types"
import { sleep, waitFor, waitUntilCompleted } from "./utils"
-suite("Roo Code Subtasks", () => {
+suite.skip("Roo Code Subtasks", () => {
test("Should handle subtask cancellation and resumption correctly", async () => {
const api = globalThis.api
@@ -17,18 +17,17 @@ suite("Roo Code Subtasks", () => {
}
})
- await api.setConfiguration({
- mode: "ask",
- alwaysAllowModeSwitch: true,
- alwaysAllowSubtasks: true,
- autoApprovalEnabled: true,
- enableCheckpoints: false,
- })
-
const childPrompt = "You are a calculator. Respond only with numbers. What is the square root of 9?"
// Start a parent task that will create a subtask.
const parentTaskId = await api.startNewTask({
+ configuration: {
+ mode: "ask",
+ alwaysAllowModeSwitch: true,
+ alwaysAllowSubtasks: true,
+ autoApprovalEnabled: true,
+ enableCheckpoints: false,
+ },
text:
"You are the parent task. " +
`Create a subtask by using the new_task tool with the message '${childPrompt}'.` +
diff --git a/e2e/src/suite/task.test.ts b/e2e/src/suite/task.test.ts
index 434d1fcc4f..e97c3b4f1e 100644
--- a/e2e/src/suite/task.test.ts
+++ b/e2e/src/suite/task.test.ts
@@ -1,6 +1,6 @@
import * as assert from "assert"
-import type { ClineMessage } from "../../../src/exports/roo-code"
+import type { ClineMessage } from "@roo-code/types"
import { waitUntilCompleted } from "./utils"
diff --git a/e2e/src/suite/utils.ts b/e2e/src/suite/utils.ts
index 784d299820..d41fa9e8ed 100644
--- a/e2e/src/suite/utils.ts
+++ b/e2e/src/suite/utils.ts
@@ -1,4 +1,4 @@
-import type { RooCodeAPI } from "../../../src/exports/roo-code"
+import type { RooCodeAPI } from "@roo-code/types"
type WaitForOptions = {
timeout?: number
diff --git a/esbuild.js b/esbuild.js
index c6a555f5a5..2b684ea248 100644
--- a/esbuild.js
+++ b/esbuild.js
@@ -32,41 +32,40 @@ const copyWasmFiles = {
const nodeModulesDir = path.join(__dirname, "node_modules")
const distDir = path.join(__dirname, "dist")
- // tiktoken
+ // tiktoken WASM file
fs.copyFileSync(
- path.join(nodeModulesDir, "tiktoken", "tiktoken_bg.wasm"),
+ path.join(nodeModulesDir, "tiktoken", "lite", "tiktoken_bg.wasm"),
path.join(distDir, "tiktoken_bg.wasm"),
)
- // tree-sitter WASM
+ // Also copy to the workers directory
+ fs.mkdirSync(path.join(distDir, "workers"), { recursive: true })
+ fs.copyFileSync(
+ path.join(nodeModulesDir, "tiktoken", "lite", "tiktoken_bg.wasm"),
+ path.join(distDir, "workers", "tiktoken_bg.wasm"),
+ )
+
+ // Main tree-sitter WASM file
fs.copyFileSync(
path.join(nodeModulesDir, "web-tree-sitter", "tree-sitter.wasm"),
path.join(distDir, "tree-sitter.wasm"),
)
- // language-specific tree-sitter WASMs
- const languageWasmDir = path.join(nodeModulesDir, "tree-sitter-wasms", "out")
- const languages = [
- "typescript",
- "tsx",
- "python",
- "rust",
- "javascript",
- "go",
- "cpp",
- "c",
- "c_sharp",
- "ruby",
- "java",
- "php",
- "swift",
- "kotlin",
- ]
-
- languages.forEach((lang) => {
- const filename = `tree-sitter-${lang}.wasm`
- fs.copyFileSync(path.join(languageWasmDir, filename), path.join(distDir, filename))
- })
+ // Copy language-specific WASM files
+ const languageWasmDir = path.join(__dirname, "node_modules", "tree-sitter-wasms", "out")
+
+ // Dynamically read all WASM files from the directory instead of using a hardcoded list
+ if (fs.existsSync(languageWasmDir)) {
+ const wasmFiles = fs.readdirSync(languageWasmDir).filter((file) => file.endsWith(".wasm"))
+
+ console.log(`Copying ${wasmFiles.length} tree-sitter WASM files to dist directory`)
+
+ wasmFiles.forEach((filename) => {
+ fs.copyFileSync(path.join(languageWasmDir, filename), path.join(distDir, filename))
+ })
+ } else {
+ console.warn(`Tree-sitter WASM directory not found: ${languageWasmDir}`)
+ }
})
},
}
@@ -181,7 +180,7 @@ const extensionConfig = {
{
name: "alias-plugin",
setup(build) {
- build.onResolve({ filter: /^pkce-challenge$/ }, (args) => {
+ build.onResolve({ filter: /^pkce-challenge$/ }, (_args) => {
return { path: require.resolve("pkce-challenge/dist/index.browser.js") }
})
},
@@ -195,22 +194,31 @@ const extensionConfig = {
external: ["vscode"],
}
+const workerConfig = {
+ bundle: true,
+ minify: production,
+ sourcemap: !production,
+ logLevel: "silent",
+ entryPoints: ["src/workers/countTokens.ts"],
+ format: "cjs",
+ sourcesContent: false,
+ platform: "node",
+ outdir: "dist/workers",
+}
+
async function main() {
- const extensionCtx = await esbuild.context(extensionConfig)
+ const [extensionCtx, workerCtx] = await Promise.all([
+ esbuild.context(extensionConfig),
+ esbuild.context(workerConfig),
+ ])
if (watch) {
- // Start the esbuild watcher
- await extensionCtx.watch()
-
- // Copy and watch locale files
- console.log("Copying locale files initially...")
+ await Promise.all([extensionCtx.watch(), workerCtx.watch()])
copyLocaleFiles()
-
- // Set up the watcher for locale files
setupLocaleWatcher()
} else {
- await extensionCtx.rebuild()
- await extensionCtx.dispose()
+ await Promise.all([extensionCtx.rebuild(), workerCtx.rebuild()])
+ await Promise.all([extensionCtx.dispose(), workerCtx.dispose()])
}
}
diff --git a/evals/README.md b/evals/README.md
index 9f343a6e09..b55a75dcff 100644
--- a/evals/README.md
+++ b/evals/README.md
@@ -7,7 +7,7 @@ NOTE: This is MacOS only for now!
Clone the Roo Code repo:
```sh
-git clone https://github.com/RooVetGit/Roo-Code.git
+git clone https://github.com/RooCodeInc/Roo-Code.git
cd Roo-Code
```
diff --git a/evals/apps/cli/src/index.ts b/evals/apps/cli/src/index.ts
index 6b287042b0..3bd71c86a7 100644
--- a/evals/apps/cli/src/index.ts
+++ b/evals/apps/cli/src/index.ts
@@ -178,6 +178,15 @@ const runExercise = async ({ run, task, server }: { run: Run; task: Task; server
const workspacePath = path.resolve(exercisesPath, language, exercise)
const taskSocketPath = path.resolve(dirname, `${dirname}/task-${task.id}.sock`)
+ // Inject foot gun system prompt if present
+ if (process.env.FOOTGUN_SYSTEM_PROMPT) {
+ const rooDir = path.join(workspacePath, ".roo")
+ if (!fs.existsSync(rooDir)) {
+ fs.mkdirSync(rooDir, { recursive: true })
+ }
+ fs.writeFileSync(path.join(rooDir, "system-prompt-code"), process.env.FOOTGUN_SYSTEM_PROMPT)
+ }
+
// If debugging:
// Use --wait --log trace or --verbose.
// Don't await execa and store result as subprocess.
diff --git a/evals/apps/web/package.json b/evals/apps/web/package.json
index d52770bbb5..d7b5ca6aed 100644
--- a/evals/apps/web/package.json
+++ b/evals/apps/web/package.json
@@ -31,7 +31,7 @@
"clsx": "^2.1.1",
"cmdk": "^1.1.0",
"fuzzysort": "^3.1.0",
- "lucide-react": "^0.479.0",
+ "lucide-react": "^0.510.0",
"next": "15.2.2",
"next-themes": "^0.4.6",
"p-map": "^7.0.3",
diff --git a/evals/apps/web/src/app/runs/new/new-run.tsx b/evals/apps/web/src/app/runs/new/new-run.tsx
index 71b7422ff3..54dd553b82 100644
--- a/evals/apps/web/src/app/runs/new/new-run.tsx
+++ b/evals/apps/web/src/app/runs/new/new-run.tsx
@@ -8,6 +8,7 @@ import { zodResolver } from "@hookform/resolvers/zod"
import fuzzysort from "fuzzysort"
import { toast } from "sonner"
import { X, Rocket, Check, ChevronsUpDown, HardDriveUpload, CircleCheck } from "lucide-react"
+import { Dialog, DialogContent, DialogTitle, DialogFooter } from "@/components/ui/dialog"
import { globalSettingsSchema, providerSettingsSchema, rooCodeDefaults } from "@evals/types"
@@ -83,6 +84,10 @@ export function NewRun() {
const [model, suite, settings] = watch(["model", "suite", "settings", "concurrency"])
+ const [systemPromptDialogOpen, setSystemPromptDialogOpen] = useState(false)
+ const [systemPrompt, setSystemPrompt] = useState("")
+ const systemPromptRef = useRef(null)
+
const onSubmit = useCallback(
async (values: FormValues) => {
try {
@@ -97,13 +102,13 @@ export function NewRun() {
values.settings = { ...(values.settings || {}), openRouterModelId }
}
- const { id } = await createRun(values)
+ const { id } = await createRun({ ...values, systemPrompt })
router.push(`/runs/${id}`)
} catch (e) {
toast.error(e instanceof Error ? e.message : "An unknown error occurred.")
}
},
- [mode, model, models.data, router],
+ [mode, model, models.data, router, systemPrompt],
)
const onFilterModels = useCallback(
@@ -313,6 +318,10 @@ export function NewRun() {
)}
+
+
+# Contribuir a Roo Code
-## Decidir en què treballar
+Roo Code és un projecte impulsat per la comunitat i valorem molt cada contribució. Per simplificar la col·laboració, treballem amb un enfoc [Issue-First](#enfoc-issue-first), que significa que tots els [Pull Requests (PRs)](#enviar-un-pull-request) han d'estar primer vinculats a una Issue de GitHub. Si us plau, llegeix aquesta guia amb atenció.
-Buscant una bona primera contribució? Consulteu les incidències a la secció "Issue [Unassigned]" del nostre [Projecte de Github de Roo Code](https://github.com/orgs/RooVetGit/projects/1). Aquestes estan específicament seleccionades per a nous col·laboradors i àrees on ens encantaria rebre ajuda!
+## Taula de continguts
-També donem la benvinguda a contribucions a la nostra [documentació](https://docs.roocode.com/)! Ja sigui corregint errors tipogràfics, millorant guies existents o creant nou contingut educatiu - ens encantaria construir un repositori de recursos impulsat per la comunitat que ajudi a tothom a aprofitar al màxim Roo Code. Podeu fer clic a "Editar aquesta pàgina" a qualsevol pàgina per arribar ràpidament al lloc correcte a Github per editar el fitxer, o podeu anar directament a https://github.com/RooVetGit/Roo-Code-Docs.
+- [Abans de contribuir](#abans-de-contribuir)
+- [Trobar i planificar la teva contribució](#trobar-i-planificar-la-teva-contribució)
+- [Procés de desenvolupament i enviament](#procés-de-desenvolupament-i-enviament)
+- [Legal](#legal)
-Si esteu planejant treballar en una funcionalitat més gran, si us plau creeu primer una [sol·licitud de funcionalitat](https://github.com/RooVetGit/Roo-Code/discussions/categories/feature-requests?discussions_q=is%3Aopen+category%3A%22Feature+Requests%22+sort%3Atop) perquè puguem discutir si s'alinea amb la visió de Roo Code. També podeu consultar el nostre [Full de Ruta del Projecte](#full-de-ruta-del-projecte) a continuació per veure si la vostra idea s'ajusta a la nostra direcció estratègica.
+## Abans de contribuir
-## Full de Ruta del Projecte
+### 1. Codi de conducta
-Roo Code té un full de ruta de desenvolupament clar que guia les nostres prioritats i direcció futura. Entendre el nostre full de ruta us pot ajudar a:
+Tots els col·laboradors han de complir el nostre [Codi de conducta](./CODE_OF_CONDUCT.md).
-- Alinear les vostres contribucions amb els objectius del projecte
-- Identificar àrees on la vostra experiència seria més valuosa
-- Entendre el context darrere de certes decisions de disseny
-- Trobar inspiració per a noves funcionalitats que donin suport a la nostra visió
+### 2. Fulla de ruta del projecte
-El nostre full de ruta actual se centra en sis pilars clau:
+La nostra fulla de ruta orienta la direcció del projecte. Alinea les teves contribucions amb aquests objectius clau:
-### Suport de Proveïdors
+### Fiabilitat primer
-Aspirem a donar suport a tants proveïdors com sigui possible:
+- Garantir que l'edició de diferències i l'execució de comandes siguin consistentment fiables
+- Reduir els punts de fricció que desanimen l'ús regular
+- Garantir un funcionament fluid en tots els idiomes i plataformes
+- Ampliar el suport robust per a una àmplia varietat de proveïdors i models d'IA
-- Suport més versàtil per a "OpenAI Compatible"
-- xAI, Microsoft Azure AI, Alibaba Cloud Qwen, IBM Watsonx, Together AI, DeepInfra, Fireworks AI, Cohere, Perplexity AI, FriendliAI, Replicate
-- Suport millorat per a Ollama i LM Studio
+### Experiència d'usuari millorada
-### Suport de Models
+- Simplificar la interfície d'usuari per a més claredat i intuïció
+- Millorar contínuament el flux de treball per satisfer les altes expectatives dels desenvolupadors
-Volem que Roo funcioni tan bé com sigui possible amb tants models com sigui possible, inclosos els models locals:
+### Lideratge en rendiment dels agents
-- Suport de models locals a través de prompts de sistema personalitzats i fluxos de treball
-- Avaluacions de rendiment i casos de prova
+- Establir punts de referència d'avaluació (evals) complets per mesurar la productivitat real
+- Facilitar que tothom pugui executar i interpretar aquestes avaluacions fàcilment
+- Proporcionar millores que demostrin increments clars en les puntuacions d'avaluació
-### Suport de Sistemes
+Esmenta la relació amb aquestes àrees als teus PRs.
-Volem que Roo funcioni bé a l'ordinador de tothom:
+### 3. Uneix-te a la comunitat Roo Code
-- Integració de terminal multiplataforma
-- Suport sòlid i consistent per a Mac, Windows i Linux
+- **Principal:** Uneix-te al nostre [Discord](https://discord.gg/roocode) i envia un DM a **Hannes Rudolph (`hrudolph`)**.
+- **Alternativa:** Els col·laboradors experimentats poden participar directament via [GitHub Projects](https://github.com/orgs/RooCodeInc/projects/1).
-### Documentació
+## Trobar i planificar la teva contribució
-Volem documentació completa i accessible per a tots els usuaris i col·laboradors:
+### Tipus de contribucions
-- Guies d'usuari i tutorials ampliats
-- Documentació clara de l'API
-- Millor orientació per als col·laboradors
-- Recursos de documentació multilingües
-- Exemples interactius i mostres de codi
+- **Correcció d'errors:** Solucionar problemes en el codi.
+- **Noves funcionalitats:** Afegir noves capacitats.
+- **Documentació:** Millorar guies i claredat.
-### Estabilitat
+### Enfoc Issue-First
-Volem reduir significativament el nombre d'errors i augmentar les proves automatitzades:
+Totes les contribucions han de començar amb una Issue de GitHub.
-- Interruptor de registre de depuració
-- Botó de còpia "Informació de Màquina/Tasca" per enviar amb sol·licituds d'error/suport
+- **Revisar issues existents:** Cerca a [GitHub Issues](https://github.com/RooCodeInc/Roo-Code/issues).
+- **Crear una issue:** Utilitza les plantilles adequades:
+ - **Errors:** Plantilla "Bug Report".
+ - **Funcionalitats:** Plantilla "Detailed Feature Proposal". Es requereix aprovació abans de començar.
+- **Reclamar issues:** Comenta i espera l'assignació oficial.
-### Internacionalització
+**Els PRs sense issues aprovades poden ser tancats.**
-Volem que Roo parli l'idioma de tothom:
+### Decidir en què treballar
-- 我们希望 Roo Code 说每个人的语言
-- Queremos que Roo Code hable el idioma de todos
-- हम चाहते हैं कि Roo Code हर किसी की भाषा बोले
-- نريد أن يتحدث Roo Code لغة الجميع
+- Consulta el [Projecte GitHub](https://github.com/orgs/RooCodeInc/projects/1) per trobar "Good First Issues" no assignades.
+- Per a documentació, visita [Roo Code Docs](https://github.com/RooCodeInc/Roo-Code-Docs).
-Donem especialment la benvinguda a contribucions que avancin els nostres objectius del full de ruta. Si esteu treballant en alguna cosa que s'alinea amb aquests pilars, si us plau mencioneu-ho a la descripció del vostre PR.
+### Informar d'errors
-## Configuració de desenvolupament
+- Comprova primer els informes existents.
+- Crea nous informes d'errors utilitzant la [plantilla "Bug Report"](https://github.com/RooCodeInc/Roo-Code/issues/new/choose).
+- **Vulnerabilitats de seguretat:** Informa de manera privada via [security advisories](https://github.com/RooCodeInc/Roo-Code/security/advisories/new).
-1. **Cloneu** el repositori:
+## Procés de desenvolupament i enviament
-```sh
-git clone https://github.com/RooVetGit/Roo-Code.git
-```
+### Configuració de desenvolupament
-2. **Instal·leu les dependències**:
+1. **Fork & Clona:**
-```sh
-npm run install:all
```
-
-3. **Inicieu la vista web (aplicació Vite/React amb HMR)**:
-
-```sh
-npm run dev
+git clone https://github.com/EL_TEU_USUARI/Roo-Code.git
```
-4. **Depuració**:
- Premeu `F5` (o **Execució** → **Inicia la depuració**) a VSCode per obrir una nova sessió amb Roo Code carregat.
-
-Els canvis a la vista web apareixeran immediatament. Els canvis a l'extensió principal requeriran reiniciar l'amfitrió de l'extensió.
-
-Alternativament, podeu crear un .vsix i instal·lar-lo directament a VSCode:
+2. **Instal·la dependències:**
-```sh
-npm run build
```
-
-Apareixerà un fitxer `.vsix` al directori `bin/` que es pot instal·lar amb:
-
-```sh
-code --install-extension bin/roo-cline-.vsix
+npm run install:all
```
-## Escriure i enviar codi
-
-Qualsevol persona pot contribuir amb codi a Roo Code, però us demanem que seguiu aquestes directrius per assegurar que les vostres contribucions puguin ser integrades sense problemes:
-
-1. **Mantingueu les Pull Requests enfocades**
-
- - Limiteu les PR a una sola funcionalitat o correcció d'error
- - Dividiu els canvis més grans en PR més petites i relacionades
- - Dividiu els canvis en commits lògics que puguin ser revisats independentment
-
-2. **Qualitat del codi**
+3. **Depuració:** Obre amb VS Code (`F5`).
- - Totes les PR han de passar les comprovacions de CI que inclouen tant anàlisi com formatació
- - Solucioneu qualsevol advertència o error d'ESLint abans d'enviar
- - Responeu a tots els comentaris d'Ellipsis, la nostra eina automatitzada de revisió de codi
- - Seguiu les millors pràctiques de TypeScript i mantingueu la seguretat de tipus
+### Guia per escriure codi
-3. **Proves**
+- Un PR centrat per funcionalitat o correcció.
+- Segueix les millors pràctiques d'ESLint i TypeScript.
+- Escriu missatges de commit clars i descriptius que facin referència a issues (ex: `Fixes #123`).
+- Proporciona proves completes (`npm test`).
+- Rebaseja a la branca `main` més recent abans d'enviar.
- - Afegiu proves per a noves funcionalitats
- - Executeu `npm test` per assegurar que totes les proves passin
- - Actualitzeu les proves existents si els vostres canvis les afecten
- - Incloeu tant proves unitàries com proves d'integració quan sigui apropiat
+### Enviar un Pull Request
-4. **Directrius de commits**
+- Comença com a **PR en esborrany** si busques feedback primerenc.
+- Descriu clarament els teus canvis seguint la Plantilla de Pull Request.
+- Proporciona captures de pantalla/vídeos per a canvis d'UI.
+- Indica si es necessiten actualitzacions de documentació.
- - Escriviu missatges de commit clars i descriptius
- - Feu referència a incidències rellevants als commits utilitzant #número-incidència
+### Política de Pull Request
-5. **Abans d'enviar**
+- Ha de fer referència a issues preaprovades i assignades.
+- Els PRs que no segueixen la política poden ser tancats.
+- Els PRs han de passar els tests de CI, alinear-se amb la fulla de ruta i tenir documentació clara.
- - Rebaseu la vostra branca sobre l'última main
- - Assegureu-vos que la vostra branca es construeix amb èxit
- - Comproveu doblement que totes les proves passen
- - Reviseu els vostres canvis per qualsevol codi de depuració o registres de consola
+### Procés de revisió
-6. **Descripció de la Pull Request**
- - Descriviu clarament què fan els vostres canvis
- - Incloeu passos per provar els canvis
- - Enumereu qualsevol canvi important
- - Afegiu captures de pantalla per a canvis d'interfície d'usuari
+- **Triatge diari:** Comprovacions ràpides pels mantenidors.
+- **Revisió setmanal detallada:** Avaluació exhaustiva.
+- **Itera ràpidament** en base al feedback.
-## Acord de contribució
+## Legal
-En enviar una pull request, accepteu que les vostres contribucions estaran sota la mateixa llicència que el projecte ([Apache 2.0](../LICENSE)).
+En enviar un pull request, acceptes que les teves contribucions es llicenciïn sota la Llicència Apache 2.0, d'acord amb la llicència de Roo Code.
diff --git a/locales/ca/README.md b/locales/ca/README.md
index eec221b2c8..9a90edf26a 100644
--- a/locales/ca/README.md
+++ b/locales/ca/README.md
@@ -1,7 +1,7 @@
+# Beitrag zu Roo Code
-## Entscheiden, woran Sie arbeiten möchten
+Roo Code ist ein Community-getriebenes Projekt, und wir schätzen jeden Beitrag sehr. Für eine reibungslose Zusammenarbeit arbeiten wir nach dem [Issue-First-Ansatz](#issue-first-ansatz), was bedeutet, dass alle [Pull Requests (PRs)](#einen-pull-request-einreichen) zuerst mit einem GitHub Issue verknüpft werden müssen. Bitte lies diesen Leitfaden sorgfältig durch.
-Suchst du nach einem guten ersten Beitrag? Schau dir Issues im Abschnitt "Issue [Unassigned]" unseres [Roo Code Issues](https://github.com/orgs/RooVetGit/projects/1) Github-Projekts an. Diese sind speziell für neue Mitwirkende und Bereiche ausgewählt, in denen wir Hilfe gebrauchen könnten!
+## Inhaltsverzeichnis
-Wir begrüßen auch Beiträge zu unserer [Dokumentation](https://docs.roocode.com/)! Ob du Tippfehler korrigierst, bestehende Anleitungen verbesserst oder neue Bildungsinhalte erstellst - wir würden gerne ein Community-geführtes Repository von Ressourcen aufbauen, das jedem hilft, das Beste aus Roo Code herauszuholen. Du kannst auf jeder Seite auf "Edit this page" klicken, um schnell zur richtigen Stelle in Github zu gelangen, um die Datei zu bearbeiten, oder du kannst direkt zu https://github.com/RooVetGit/Roo-Code-Docs gehen.
+- [Bevor du beiträgst](#bevor-du-beiträgst)
+- [Beitrag finden & planen](#beitrag-finden--planen)
+- [Entwicklung & Einreichung](#entwicklung--einreichung)
+- [Rechtliches](#rechtliches)
-Wenn du an einer größeren Funktion arbeiten möchtest, erstelle bitte zuerst eine [Funktionsanfrage](https://github.com/RooVetGit/Roo-Code/discussions/categories/feature-requests?discussions_q=is%3Aopen+category%3A%22Feature+Requests%22+sort%3Atop), damit wir diskutieren können, ob sie mit der Vision von Roo Code übereinstimmt. Du kannst auch unseren [Projekt-Fahrplan](#projekt-fahrplan) unten überprüfen, um zu sehen, ob deine Idee mit unserer strategischen Ausrichtung übereinstimmt.
+## Bevor du beiträgst
-## Projekt-Fahrplan
+### 1. Verhaltenskodex
-Roo Code hat einen klaren Entwicklungsfahrplan, der unsere Prioritäten und zukünftige Richtung leitet. Das Verständnis unseres Fahrplans kann dir helfen:
+Alle Mitwirkenden müssen sich an unseren [Verhaltenskodex](./CODE_OF_CONDUCT.md) halten.
-- Deine Beiträge mit den Projektzielen abzustimmen
-- Bereiche zu identifizieren, in denen deine Expertise am wertvollsten wäre
-- Den Kontext hinter bestimmten Designentscheidungen zu verstehen
-- Inspiration für neue Funktionen zu finden, die unsere Vision unterstützen
+### 2. Projekt-Roadmap
-Unser aktueller Fahrplan konzentriert sich auf sechs Schlüsselsäulen:
+Unsere Roadmap gibt die Richtung des Projekts vor. Richte deine Beiträge an diesen Schlüsselzielen aus:
-### Provider-Unterstützung
+### Zuverlässigkeit an erster Stelle
-Wir möchten so viele Provider wie möglich gut unterstützen:
+- Sicherstellen, dass Diff-Bearbeitung und Befehlsausführung durchgängig zuverlässig sind.
+- Reibungspunkte reduzieren, die von der regelmäßigen Nutzung abhalten.
+- Reibungslosen Betrieb in allen Sprachen und auf allen Plattformen garantieren.
+- Robuste Unterstützung für eine Vielzahl von KI-Anbietern und -Modellen ausbauen.
-- Vielseitigere "OpenAI Compatible" Unterstützung
-- xAI, Microsoft Azure AI, Alibaba Cloud Qwen, IBM Watsonx, Together AI, DeepInfra, Fireworks AI, Cohere, Perplexity AI, FriendliAI, Replicate
-- Verbesserte Unterstützung für Ollama und LM Studio
+### Verbesserte Benutzererfahrung
-### Modell-Unterstützung
+- Die Benutzeroberfläche für mehr Klarheit und Intuitivität optimieren.
+- Den Workflow kontinuierlich verbessern, um den hohen Erwartungen gerecht zu werden, die Entwickler an täglich genutzte Tools stellen.
-Wir wollen, dass Roo mit so vielen Modellen wie möglich gut funktioniert, einschließlich lokaler Modelle:
+### Führend bei der Agentenleistung
-- Lokale Modellunterstützung durch benutzerdefiniertes System-Prompting und Workflows
-- Benchmark-Evaluierungen und Testfälle
+- Umfassende Evaluierungsmaßstäbe (Evals) etablieren, um die Produktivität in der realen Welt zu messen.
+- Es für jeden einfach machen, diese Evals durchzuführen und zu interpretieren.
+- Verbesserungen liefern, die klare Steigerungen der Eval-Ergebnisse zeigen.
-### System-Unterstützung
+Erwähne die Ausrichtung an diesen Bereichen in deinen PRs.
-Wir wollen, dass Roo auf jedem Computer gut läuft:
+### 3. Werde Teil der Roo Code Community
-- Plattformübergreifende Terminal-Integration
-- Starke und konsistente Unterstützung für Mac, Windows und Linux
+- **Hauptweg:** Tritt unserem [Discord](https://discord.gg/roocode) bei und schreibe eine DM an **Hannes Rudolph (`hrudolph`)**.
+- **Alternative:** Erfahrene Mitwirkende können sich direkt über [GitHub Projects](https://github.com/orgs/RooCodeInc/projects/1) beteiligen.
-### Dokumentation
+## Beitrag finden & planen
-Wir wollen umfassende, zugängliche Dokumentation für alle Benutzer und Mitwirkenden:
+### Beitragsarten
-- Erweiterte Benutzerhandbücher und Tutorials
-- Klare API-Dokumentation
-- Bessere Anleitung für Mitwirkende
-- Mehrsprachige Dokumentationsressourcen
-- Interaktive Beispiele und Codebeispiele
+- **Bugfixes:** Fehler im Code beheben.
+- **Neue Features:** Neue Funktionen hinzufügen.
+- **Dokumentation:** Anleitungen verbessern und klarer gestalten.
-### Stabilität
+### Issue-First-Ansatz
-Wir wollen die Anzahl der Fehler deutlich reduzieren und die automatisierte Testabdeckung erhöhen:
+Alle Beiträge müssen mit einem GitHub Issue beginnen.
-- Debug-Logging-Schalter
-- "Maschinen-/Aufgabeninformationen" Kopier-Button zum Einsenden mit Fehler-/Support-Anfragen
+- **Bestehende Issues prüfen**: Durchsuche die [GitHub Issues](https://github.com/RooCodeInc/Roo-Code/issues).
+- **Issue erstellen**: Nutze die passenden Vorlagen:
+ - **Bugs:** "Bug Report"-Vorlage.
+ - **Features:** "Detailed Feature Proposal"-Vorlage. Vor dem Start ist eine Genehmigung erforderlich.
+- **Issues beanspruchen**: Kommentiere und warte auf die offizielle Zuweisung.
-### Internationalisierung
+**PRs ohne genehmigte Issues können geschlossen werden.**
-Wir wollen, dass Roo die Sprache aller spricht:
+### Was soll ich machen?
-- 我们希望 Roo Code 说每个人的语言
-- Queremos que Roo Code hable el idioma de todos
-- हम चाहते हैं कि Roo Code हर किसी की भाषा बोले
-- نريد أن يتحدث Roo Code لغة الجميع
+- Schau im [GitHub Project](https://github.com/orgs/RooCodeInc/projects/1) nach nicht zugewiesenen "Good First Issues".
+- Für Dokumentation besuche das [Roo Code Docs](https://github.com/RooCodeInc/Roo-Code-Docs) Repository.
-Wir begrüßen besonders Beiträge, die unsere Fahrplanziele voranbringen. Wenn du an etwas arbeitest, das mit diesen Säulen übereinstimmt, erwähne es bitte in deiner PR-Beschreibung.
+### Bugs melden
-## Entwicklungs-Setup
+- Prüfe zuerst, ob der Bug bereits gemeldet wurde.
+- Erstelle neue Bug-Reports mit der ["Bug Report"-Vorlage](https://github.com/RooCodeInc/Roo-Code/issues/new/choose).
+- **Sicherheitslücken:** Melde diese privat über [Security Advisories](https://github.com/RooCodeInc/Roo-Code/security/advisories/new).
-1. **Klone** das Repository:
+## Entwicklung & Einreichung
-```sh
-git clone https://github.com/RooVetGit/Roo-Code.git
-```
+### Entwicklungs-Setup
-2. **Installiere Abhängigkeiten**:
+1. **Fork & Clone:**
-```sh
-npm run install:all
```
-
-3. **Starte die Webansicht (Vite/React-App mit HMR)**:
-
-```sh
-npm run dev
+git clone https://github.com/DEIN_USERNAME/Roo-Code.git
```
-4. **Debugging**:
- Drücke `F5` (oder **Ausführen** → **Debugging starten**) in VSCode, um eine neue Sitzung mit geladenem Roo Code zu öffnen.
-
-Änderungen an der Webansicht erscheinen sofort. Änderungen an der Kern-Erweiterung erfordern einen Neustart des Erweiterungs-Hosts.
-
-Alternativ kannst du eine .vsix-Datei erstellen und direkt in VSCode installieren:
+2. **Abhängigkeiten installieren:**
-```sh
-npm run build
```
-
-Eine `.vsix`-Datei erscheint im `bin/`-Verzeichnis, die mit folgendem Befehl installiert werden kann:
-
-```sh
-code --install-extension bin/roo-cline-.vsix
+npm run install:all
```
-## Code schreiben und einreichen
-
-Jeder kann Code zu Roo Code beitragen, aber wir bitten dich, diese Richtlinien zu befolgen, um sicherzustellen, dass deine Beiträge reibungslos integriert werden können:
-
-1. **Halten Sie Pull Requests fokussiert**
-
- - Beschränke PRs auf eine einzelne Funktion oder Fehlerbehebung
- - Teile größere Änderungen in kleinere, zusammenhängende PRs auf
- - Unterteile Änderungen in logische Commits, die unabhängig überprüft werden können
-
-2. **Codequalität**
+3. **Debugging:** Öffne mit VS Code (`F5`).
- - Alle PRs müssen CI-Prüfungen bestehen, die sowohl Linting als auch Formatierung umfassen
- - Behebe alle ESLint-Warnungen oder -Fehler vor dem Einreichen
- - Reagiere auf alle Rückmeldungen von Ellipsis, unserem automatisierten Code-Review-Tool
- - Folge TypeScript-Best-Practices und halte die Typsicherheit aufrecht
+### Code-Richtlinien
-3. **Testen**
+- Ein fokussierter PR pro Feature oder Fix.
+- Folge den ESLint und TypeScript Best Practices.
+- Schreibe klare, beschreibende Commits, die auf Issues verweisen (z.B. `Fixes #123`).
+- Liefere gründliche Tests (`npm test`).
+- Rebase auf den neuesten `main`-Branch vor dem Einreichen.
- - Füge Tests für neue Funktionen hinzu
- - Führe `npm test` aus, um sicherzustellen, dass alle Tests bestanden werden
- - Aktualisiere bestehende Tests, wenn deine Änderungen diese beeinflussen
- - Schließe sowohl Unit-Tests als auch Integrationstests ein, wo angemessen
+### Einen Pull Request einreichen
-4. **Commit-Richtlinien**
+- Beginne als **Draft PR**, wenn du frühes Feedback suchst.
+- Beschreibe deine Änderungen klar und folge der Pull Request Vorlage.
+- Stelle Screenshots/Videos für UI-Änderungen bereit.
+- Gib an, ob Dokumentationsaktualisierungen notwendig sind.
- - Schreibe klare, beschreibende Commit-Nachrichten
- - Verweise auf relevante Issues in Commits mit #issue-nummer
+### Pull Request Richtlinie
-5. **Vor dem Einreichen**
+- Muss auf vorab genehmigte, zugewiesene Issues verweisen.
+- PRs ohne Einhaltung der Richtlinie können geschlossen werden.
+- PRs sollten CI-Tests bestehen, zur Roadmap passen und klare Dokumentation haben.
- - Rebase deinen Branch auf den neuesten main-Branch
- - Stelle sicher, dass dein Branch erfolgreich baut
- - Überprüfe erneut, dass alle Tests bestanden werden
- - Prüfe deine Änderungen auf Debug-Code oder Konsolenausgaben
+### Review-Prozess
-6. **Pull Request Beschreibung**
- - Beschreibe klar, was deine Änderungen bewirken
- - Füge Schritte zum Testen der Änderungen hinzu
- - Liste alle Breaking Changes auf
- - Füge Screenshots für UI-Änderungen hinzu
+- **Tägliche Triage:** Schnelle Prüfungen durch Maintainer.
+- **Wöchentliche Tiefenprüfung:** Umfassende Bewertung.
+- **Zeitnah auf Feedback reagieren** und entsprechend iterieren.
-## Beitragsvereinbarung
+## Rechtliches
-Durch das Einreichen eines Pull Requests stimmst du zu, dass deine Beiträge unter derselben Lizenz wie das Projekt ([Apache 2.0](../LICENSE)) lizenziert werden.
+Mit deinem Beitrag erklärst du dich damit einverstanden, dass deine Beiträge unter der Apache 2.0 Lizenz lizenziert werden, konsistent mit der Lizenzierung von Roo Code.
diff --git a/locales/de/README.md b/locales/de/README.md
index 722e149768..dc4a911bdc 100644
--- a/locales/de/README.md
+++ b/locales/de/README.md
@@ -1,7 +1,7 @@
+# Contribuir a Roo Code
-## Decidir en qué trabajar
+Roo Code es un proyecto impulsado por la comunidad, y valoramos profundamente cada contribución. Para agilizar la colaboración, operamos con un enfoque [Issue-First](#enfoque-issue-first), lo que significa que todos los [Pull Requests (PRs)](#enviar-un-pull-request) deben estar vinculados primero a un Issue de GitHub. Por favor, revisa esta guía cuidadosamente.
-¿Buscas una buena primera contribución? Revisa los issues en la sección "Issue [Unassigned]" de nuestro [Proyecto GitHub de Roo Code](https://github.com/orgs/RooVetGit/projects/1). ¡Estos están específicamente seleccionados para nuevos colaboradores y áreas donde nos encantaría recibir ayuda!
+## Tabla de Contenidos
-¡También damos la bienvenida a contribuciones a nuestra [documentación](https://docs.roocode.com/)! Ya sea arreglando errores tipográficos, mejorando guías existentes o creando nuevo contenido educativo - nos encantaría construir un repositorio de recursos impulsado por la comunidad que ayude a todos a sacar el máximo provecho de Roo Code. Puedes hacer clic en "Edit this page" en cualquier página para llegar rápidamente al lugar correcto en Github para editar el archivo, o puedes ir directamente a https://github.com/RooVetGit/Roo-Code-Docs.
+- [Antes de Contribuir](#antes-de-contribuir)
+- [Encontrar y Planificar tu Contribución](#encontrar-y-planificar-tu-contribución)
+- [Proceso de Desarrollo y Envío](#proceso-de-desarrollo-y-envío)
+- [Legal](#legal)
-Si estás planeando trabajar en una función más grande, por favor crea una [solicitud de función](https://github.com/RooVetGit/Roo-Code/discussions/categories/feature-requests?discussions_q=is%3Aopen+category%3A%22Feature+Requests%22+sort%3Atop) primero para que podamos discutir si se alinea con la visión de Roo Code. También puedes consultar nuestra [Hoja de Ruta del Proyecto](#hoja-de-ruta-del-proyecto) a continuación para ver si tu idea encaja con nuestra dirección estratégica.
+## Antes de Contribuir
-## Hoja de Ruta del Proyecto
+### 1. Código de Conducta
-Roo Code tiene una hoja de ruta de desarrollo clara que guía nuestras prioridades y dirección futura. Entender nuestra hoja de ruta puede ayudarte a:
+Todos los colaboradores deben adherirse a nuestro [Código de Conducta](./CODE_OF_CONDUCT.md).
-- Alinear tus contribuciones con los objetivos del proyecto
-- Identificar áreas donde tu experiencia sería más valiosa
-- Entender el contexto detrás de ciertas decisiones de diseño
-- Encontrar inspiración para nuevas funciones que apoyen nuestra visión
+### 2. Hoja de Ruta del Proyecto
-Nuestra hoja de ruta actual se centra en seis pilares clave:
+Nuestra hoja de ruta guía la dirección del proyecto. Alinea tus contribuciones con estos objetivos clave:
-### Soporte de Proveedores
+### Confiabilidad Primero
-Nuestro objetivo es dar soporte a tantos proveedores como sea posible:
+- Garantizar que la edición de diferencias y la ejecución de comandos sean consistentemente confiables.
+- Reducir los puntos de fricción que disuaden el uso regular.
+- Garantizar un funcionamiento fluido en todos los idiomas y plataformas.
+- Ampliar el soporte sólido para una amplia variedad de proveedores y modelos de IA.
-- Soporte más versátil para "OpenAI Compatible"
-- xAI, Microsoft Azure AI, Alibaba Cloud Qwen, IBM Watsonx, Together AI, DeepInfra, Fireworks AI, Cohere, Perplexity AI, FriendliAI, Replicate
-- Soporte mejorado para Ollama y LM Studio
+### Experiencia de Usuario Mejorada
-### Soporte de Modelos
+- Simplificar la interfaz de usuario para mayor claridad e intuitividad.
+- Mejorar continuamente el flujo de trabajo para satisfacer las altas expectativas que los desarrolladores tienen para herramientas de uso diario.
-Queremos que Roo funcione bien con tantos modelos como sea posible, incluidos los modelos locales:
+### Liderazgo en Rendimiento de Agentes
-- Soporte para modelos locales a través de system prompting personalizado y flujos de trabajo
-- Evaluaciones de benchmarking y casos de prueba
+- Establecer evaluaciones comparativas completas (evals) para medir la productividad en el mundo real.
+- Facilitar que todos puedan ejecutar e interpretar estas evaluaciones fácilmente.
+- Ofrecer mejoras que demuestren aumentos claros en las puntuaciones de evaluación.
-### Soporte de Sistemas
+Menciona la alineación con estas áreas en tus PRs.
-Queremos que Roo funcione bien en el ordenador de todos:
+### 3. Únete a la Comunidad Roo Code
-- Integración de terminal multiplataforma
-- Soporte sólido y consistente para Mac, Windows y Linux
+- **Principal:** Únete a nuestro [Discord](https://discord.gg/roocode) y envía un DM a **Hannes Rudolph (`hrudolph`)**.
+- **Alternativa:** Los colaboradores experimentados pueden participar directamente a través de [GitHub Projects](https://github.com/orgs/RooCodeInc/projects/1).
-### Documentación
+## Encontrar y Planificar tu Contribución
-Queremos una documentación completa y accesible para todos los usuarios y colaboradores:
+### Tipos de Contribuciones
-- Guías de usuario y tutoriales ampliados
-- Documentación clara de la API
-- Mejor orientación para colaboradores
-- Recursos de documentación multilingües
-- Ejemplos interactivos y muestras de código
+- **Corrección de errores:** Solucionar problemas en el código.
+- **Nuevas funciones:** Añadir funcionalidades.
+- **Documentación:** Mejorar guías y claridad.
-### Estabilidad
+### Enfoque Issue-First
-Queremos disminuir significativamente el número de errores y aumentar las pruebas automatizadas:
+Todas las contribuciones deben comenzar con un Issue de GitHub.
-- Interruptor de registro de depuración
-- Botón de copia de "Información de Máquina/Tarea" para enviar con solicitudes de soporte/errores
+- **Revisar issues existentes**: Busca en [GitHub Issues](https://github.com/RooCodeInc/Roo-Code/issues).
+- **Crear un issue**: Usa las plantillas apropiadas:
+ - **Errores:** Plantilla "Bug Report".
+ - **Funciones:** Plantilla "Detailed Feature Proposal". Se requiere aprobación antes de comenzar.
+- **Reclamar issues**: Comenta y espera la asignación oficial.
-### Internacionalización
+**Los PRs sin issues aprobados pueden ser cerrados.**
-Queremos que Roo hable el idioma de todos:
+### Decidir en Qué Trabajar
-- 我们希望 Roo Code 说每个人的语言
-- Queremos que Roo Code hable el idioma de todos
-- हम चाहते हैं कि Roo Code हर किसी की भाषा बोले
-- نريد أن يتحدث Roo Code لغة الجميع
+- Revisa el [Proyecto GitHub](https://github.com/orgs/RooCodeInc/projects/1) para "Good First Issues" no asignados.
+- Para documentación, visita [Roo Code Docs](https://github.com/RooCodeInc/Roo-Code-Docs).
-Damos especialmente la bienvenida a contribuciones que avancen nuestros objetivos de la hoja de ruta. Si estás trabajando en algo que se alinea con estos pilares, por favor menciónalo en la descripción de tu PR.
+### Reportar Errores
-## Configuración de desarrollo
+- Primero verifica si ya existen reportes.
+- Crea nuevos reportes de errores usando la [plantilla "Bug Report"](https://github.com/RooCodeInc/Roo-Code/issues/new/choose).
+- **Problemas de seguridad**: Reporta de forma privada a través de [security advisories](https://github.com/RooCodeInc/Roo-Code/security/advisories/new).
-1. **Clona** el repositorio:
+## Proceso de Desarrollo y Envío
-```sh
-git clone https://github.com/RooVetGit/Roo-Code.git
-```
+### Configuración de Desarrollo
-2. **Instala dependencias**:
+1. **Fork & Clona:**
-```sh
-npm run install:all
```
-
-3. **Inicia la vista web (aplicación Vite/React con HMR)**:
-
-```sh
-npm run dev
+git clone https://github.com/TU_USUARIO/Roo-Code.git
```
-4. **Depuración**:
- Presiona `F5` (o **Ejecutar** → **Iniciar depuración**) en VSCode para abrir una nueva sesión con Roo Code cargado.
-
-Los cambios en la vista web aparecerán inmediatamente. Los cambios en la extensión principal requerirán un reinicio del host de extensión.
-
-Alternativamente, puedes construir un archivo .vsix e instalarlo directamente en VSCode:
+2. **Instalar Dependencias:**
-```sh
-npm run build
```
-
-Un archivo `.vsix` aparecerá en el directorio `bin/` que puede ser instalado con:
-
-```sh
-code --install-extension bin/roo-cline-.vsix
+npm run install:all
```
-## Escribir y enviar código
-
-Cualquiera puede contribuir con código a Roo Code, pero te pedimos que sigas estas pautas para asegurar que tus contribuciones puedan integrarse sin problemas:
-
-1. **Mantén los Pull Requests enfocados**
-
- - Limita los PRs a una sola función o corrección de errores
- - Divide los cambios más grandes en PRs más pequeños y relacionados
- - Separa los cambios en commits lógicos que puedan revisarse independientemente
-
-2. **Calidad del código**
+3. **Depuración:** Abre con VS Code (`F5`).
- - Todos los PRs deben pasar las comprobaciones de CI que incluyen tanto linting como formateo
- - Soluciona cualquier advertencia o error de ESLint antes de enviar
- - Responde a todos los comentarios de Ellipsis, nuestra herramienta automatizada de revisión de código
- - Sigue las mejores prácticas de TypeScript y mantén la seguridad de tipos
+### Guía para Escribir Código
-3. **Pruebas**
+- Un PR enfocado por función o corrección.
+- Sigue las mejores prácticas de ESLint y TypeScript.
+- Escribe commits claros y descriptivos que referencien issues (ej., `Fixes #123`).
+- Proporciona pruebas exhaustivas (`npm test`).
+- Rebase sobre la última rama `main` antes de enviar.
- - Añade pruebas para nuevas funciones
- - Ejecuta `npm test` para asegurar que todas las pruebas pasen
- - Actualiza las pruebas existentes si tus cambios les afectan
- - Incluye tanto pruebas unitarias como de integración cuando sea apropiado
+### Enviar un Pull Request
-4. **Directrices para commits**
+- Comienza como **PR en Borrador** si buscas feedback temprano.
+- Describe claramente tus cambios siguiendo la Plantilla de Pull Request.
+- Proporciona capturas de pantalla/videos para cambios en la UI.
+- Indica si son necesarias actualizaciones de documentación.
- - Escribe mensajes de commit claros y descriptivos
- - Haz referencia a los issues relevantes en los commits usando #número-de-issue
+### Política de Pull Request
-5. **Antes de enviar**
+- Debe referenciar issues preaprobados y asignados.
+- Los PRs que no cumplan con la política pueden ser cerrados.
+- Los PRs deben pasar las pruebas de CI, alinearse con la hoja de ruta y tener documentación clara.
- - Haz rebase de tu rama sobre la última main
- - Asegúrate de que tu rama se construye correctamente
- - Comprueba que todas las pruebas están pasando
- - Revisa tus cambios para detectar código de depuración o logs de consola
+### Proceso de Revisión
-6. **Descripción del Pull Request**
- - Describe claramente lo que hacen tus cambios
- - Incluye pasos para probar los cambios
- - Enumera cualquier cambio que rompa la compatibilidad
- - Añade capturas de pantalla para cambios en la interfaz de usuario
+- **Triage Diario:** Revisiones rápidas por parte de los mantenedores.
+- **Revisión Semanal en Profundidad:** Evaluación integral.
+- **Itera rápidamente** basándote en el feedback.
-## Acuerdo de contribución
+## Legal
-Al enviar un pull request, aceptas que tus contribuciones serán licenciadas bajo la misma licencia que el proyecto ([Apache 2.0](../LICENSE)).
+Al contribuir, aceptas que tus contribuciones serán licenciadas bajo la Licencia Apache 2.0, consistente con la licencia de Roo Code.
diff --git a/locales/es/README.md b/locales/es/README.md
index 7edc9be831..09aa729849 100644
--- a/locales/es/README.md
+++ b/locales/es/README.md
@@ -1,7 +1,7 @@
+# Contribuer à Roo Code
-## Décider Sur Quoi Travailler
+Roo Code est un projet porté par la communauté, et chaque contribution compte beaucoup pour nous. Pour fluidifier la collaboration, nous fonctionnons selon une [approche Issue-First](#approche-issue-first), ce qui signifie que toutes les [Pull Requests (PRs)](#soumettre-une-pull-request) doivent d'abord être liées à un ticket GitHub. Lis attentivement ce guide.
-Vous cherchez une bonne première contribution ? Consultez les issues dans la section "Issue [Unassigned]" de notre [Projet Github Roo Code Issues](https://github.com/orgs/RooVetGit/projects/1). Celles-ci sont spécifiquement sélectionnées pour les nouveaux contributeurs et les domaines où nous aimerions recevoir de l'aide !
+## Table des matières
-Nous accueillons également les contributions à notre [documentation](https://docs.roocode.com/) ! Qu'il s'agisse de corriger des fautes de frappe, d'améliorer les guides existants ou de créer du nouveau contenu éducatif - nous aimerions construire un référentiel de ressources guidé par la communauté qui aide chacun à tirer le meilleur parti de Roo Code. Vous pouvez cliquer sur "Edit this page" sur n'importe quelle page pour accéder rapidement au bon endroit dans Github pour éditer le fichier, ou vous pouvez plonger directement dans https://github.com/RooVetGit/Roo-Code-Docs.
+- [Avant de contribuer](#avant-de-contribuer)
+- [Trouver et planifier ta contribution](#trouver-et-planifier-ta-contribution)
+- [Processus de développement et de soumission](#processus-de-développement-et-de-soumission)
+- [Légal](#légal)
-Si vous prévoyez de travailler sur une fonctionnalité plus importante, veuillez d'abord créer une [demande de fonctionnalité](https://github.com/RooVetGit/Roo-Code/discussions/categories/feature-requests?discussions_q=is%3Aopen+category%3A%22Feature+Requests%22+sort%3Atop) afin que nous puissions discuter si elle s'aligne avec la vision de Roo Code. Vous pouvez également consulter notre [Feuille de route du projet](#feuille-de-route-du-projet) ci-dessous pour voir si votre idée s'inscrit dans notre orientation stratégique.
+## Avant de contribuer
-## Feuille de route du projet
+### 1. Code de conduite
-Roo Code dispose d'une feuille de route de développement claire qui guide nos priorités et notre orientation future. Comprendre notre feuille de route peut vous aider à :
+Tous les contributeurs doivent respecter notre [Code de conduite](./CODE_OF_CONDUCT.md).
-- Aligner vos contributions avec les objectifs du projet
-- Identifier les domaines où votre expertise serait la plus précieuse
-- Comprendre le contexte derrière certaines décisions de conception
-- Trouver de l'inspiration pour de nouvelles fonctionnalités qui soutiennent notre vision
+### 2. Feuille de route du projet
-Notre feuille de route actuelle se concentre sur six piliers clés :
+Notre feuille de route guide la direction du projet. Aligne tes contributions avec ces objectifs clés :
-### Support des fournisseurs
+### Fiabilité avant tout
-Nous visons à prendre en charge autant de fournisseurs que possible :
+- Garantir que l'édition des différences et l'exécution des commandes soient toujours fiables.
+- Réduire les points de friction qui découragent l'utilisation régulière.
+- Assurer un fonctionnement fluide dans toutes les langues et sur toutes les plateformes.
+- Étendre le support robuste pour une grande variété de fournisseurs et de modèles d'IA.
-- Support plus polyvalent pour "OpenAI Compatible"
-- xAI, Microsoft Azure AI, Alibaba Cloud Qwen, IBM Watsonx, Together AI, DeepInfra, Fireworks AI, Cohere, Perplexity AI, FriendliAI, Replicate
-- Support amélioré pour Ollama et LM Studio
+### Expérience utilisateur améliorée
-### Support des modèles
+- Simplifier l'interface utilisateur pour plus de clarté et d'intuitivité.
+- Améliorer continuellement le flux de travail pour répondre aux attentes élevées des développeurs.
-Nous voulons que Roo fonctionne aussi bien que possible avec autant de modèles que possible, y compris les modèles locaux :
+### Leadership en performance des agents
-- Support des modèles locaux via des prompts système personnalisés et des flux de travail
-- Évaluations de benchmarking et cas de test
+- Établir des référentiels d'évaluation (evals) complets pour mesurer la productivité réelle.
+- Permettre à chacun d'exécuter et d'interpréter facilement ces évaluations.
+- Fournir des améliorations qui démontrent des augmentations claires dans les scores d'évaluation.
-### Support des systèmes
+Mentionne l'alignement avec ces domaines dans tes PRs.
-Nous voulons que Roo fonctionne bien sur l'ordinateur de chacun :
+### 3. Rejoindre la communauté Roo Code
-- Intégration de terminal multiplateforme
-- Support solide et cohérent pour Mac, Windows et Linux
+- **Principal :** Rejoins notre [Discord](https://discord.gg/roocode) et envoie un DM à **Hannes Rudolph (`hrudolph`)**.
+- **Alternative :** Les contributeurs expérimentés peuvent participer directement via [GitHub Projects](https://github.com/orgs/RooCodeInc/projects/1).
-### Documentation
+## Trouver et planifier ta contribution
-Nous voulons une documentation complète et accessible pour tous les utilisateurs et contributeurs :
+### Types de contributions
-- Guides utilisateur et tutoriels étendus
-- Documentation API claire
-- Meilleure orientation pour les contributeurs
-- Ressources de documentation multilingues
-- Exemples interactifs et échantillons de code
+- **Corrections de bugs :** Résoudre des problèmes dans le code.
+- **Nouvelles fonctionnalités :** Ajouter de nouvelles fonctions.
+- **Documentation :** Améliorer les guides et la clarté.
-### Stabilité
+### Approche Issue-First
-Nous voulons réduire considérablement le nombre de bugs et augmenter les tests automatisés :
+Toutes les contributions doivent commencer par un ticket GitHub.
-- Interrupteur de journalisation de débogage
-- Bouton de copie "Informations machine/tâche" pour l'envoi avec les demandes de support/bug
+- **Vérifier les tickets existants :** Cherche dans les [Issues GitHub](https://github.com/RooCodeInc/Roo-Code/issues).
+- **Créer un ticket :** Utilise les modèles appropriés :
+ - **Bugs :** Modèle "Bug Report".
+ - **Fonctionnalités :** Modèle "Detailed Feature Proposal". Approbation requise avant de commencer.
+- **Réclamer des tickets :** Commente et attends l'assignation officielle.
-### Internationalisation
+**Les PRs sans tickets approuvés peuvent être fermées.**
-Nous voulons que Roo parle la langue de tous :
+### Décider sur quoi travailler
-- 我们希望 Roo Code 说每个人的语言
-- Queremos que Roo Code hable el idioma de todos
-- हम चाहते हैं कि Roo Code हर किसी की भाषा बोले
-- نريد أن يتحدث Roo Code لغة الجميع
+- Consulte le [Projet GitHub](https://github.com/orgs/RooCodeInc/projects/1) pour les "Good First Issues" non assignés.
+- Pour la documentation, visite [Roo Code Docs](https://github.com/RooCodeInc/Roo-Code-Docs).
-Nous accueillons particulièrement les contributions qui font progresser nos objectifs de feuille de route. Si vous travaillez sur quelque chose qui s'aligne avec ces piliers, veuillez le mentionner dans la description de votre PR.
+### Signaler des bugs
-## Configuration de Développement
+- Vérifie d'abord les rapports existants.
+- Crée de nouveaux rapports de bugs avec le [modèle "Bug Report"](https://github.com/RooCodeInc/Roo-Code/issues/new/choose).
+- **Failles de sécurité :** Signale-les en privé via [security advisories](https://github.com/RooCodeInc/Roo-Code/security/advisories/new).
-1. **Clonez** le dépôt :
+## Processus de développement et de soumission
-```sh
-git clone https://github.com/RooVetGit/Roo-Code.git
-```
+### Configuration du développement
-2. **Installez les dépendances** :
+1. **Fork & Clone :**
-```sh
-npm run install:all
```
-
-3. **Démarrez la vue web (application Vite/React avec HMR)** :
-
-```sh
-npm run dev
+git clone https://github.com/TON_UTILISATEUR/Roo-Code.git
```
-4. **Débogage** :
- Appuyez sur `F5` (ou **Exécuter** → **Démarrer le débogage**) dans VSCode pour ouvrir une nouvelle session avec Roo Code chargé.
-
-Les modifications apportées à la vue web apparaîtront immédiatement. Les modifications apportées à l'extension principale nécessiteront un redémarrage de l'hôte d'extension.
-
-Vous pouvez également créer un fichier .vsix et l'installer directement dans VSCode :
+2. **Installer les dépendances :**
-```sh
-npm run build
```
-
-Un fichier `.vsix` apparaîtra dans le répertoire `bin/` qui peut être installé avec :
-
-```sh
-code --install-extension bin/roo-cline-.vsix
+npm run install:all
```
-## Écrire et Soumettre du Code
-
-Tout le monde peut contribuer avec du code à Roo Code, mais nous vous demandons de suivre ces directives pour vous assurer que vos contributions puissent être intégrées en douceur :
-
-1. **Gardez les Pull Requests Ciblées**
-
- - Limitez les PRs à une seule fonctionnalité ou correction de bug
- - Divisez les changements plus importants en PRs plus petites et liées
- - Divisez les changements en commits logiques qui peuvent être examinés indépendamment
-
-2. **Qualité du Code**
+3. **Débogage :** Ouvre avec VS Code (`F5`).
- - Toutes les PRs doivent passer les vérifications CI qui incluent à la fois le linting et le formatage
- - Résolvez toutes les alertes ou erreurs ESLint avant de soumettre
- - Répondez à tous les retours d'Ellipsis, notre outil automatisé de revue de code
- - Suivez les meilleures pratiques TypeScript et maintenez la sécurité des types
+### Guide d'écriture du code
-3. **Tests**
+- Une PR ciblée par fonctionnalité ou correction.
+- Suis les bonnes pratiques ESLint et TypeScript.
+- Écris des commits clairs et descriptifs référençant les tickets (ex : `Fixes #123`).
+- Fournis des tests complets (`npm test`).
+- Rebase sur la dernière branche `main` avant de soumettre.
- - Ajoutez des tests pour les nouvelles fonctionnalités
- - Exécutez `npm test` pour vous assurer que tous les tests passent
- - Mettez à jour les tests existants si vos changements les affectent
- - Incluez à la fois des tests unitaires et d'intégration lorsque c'est approprié
+### Soumettre une Pull Request
-4. **Directives pour les Commits**
+- Commence par un **brouillon de PR** si tu cherches un feedback précoce.
+- Décris clairement tes changements en suivant le modèle de Pull Request.
+- Fournis des captures d'écran/vidéos pour les changements d'interface.
+- Indique si des mises à jour de documentation sont nécessaires.
- - Écrivez des messages de commit clairs et descriptifs
- - Référencez les issues pertinentes dans les commits en utilisant #numéro-issue
+### Politique de Pull Request
-5. **Avant de Soumettre**
+- Doit référencer des tickets pré-approuvés et assignés.
+- Les PRs ne respectant pas cette politique peuvent être fermées.
+- Les PRs doivent passer les tests CI, s'aligner avec la feuille de route et avoir une documentation claire.
- - Rebasez votre branche sur la dernière main
- - Assurez-vous que votre branche se construit avec succès
- - Vérifiez à nouveau que tous les tests passent
- - Revoyez vos changements pour détecter tout code de débogage ou logs de console
+### Processus de revue
-6. **Description du Pull Request**
- - Décrivez clairement ce que font vos changements
- - Incluez des étapes pour tester les changements
- - Listez tous les changements incompatibles
- - Ajoutez des captures d'écran pour les changements d'interface utilisateur
+- **Triage quotidien :** Vérifications rapides par les mainteneurs.
+- **Revue hebdomadaire approfondie :** Évaluation complète.
+- **Itère rapidement** sur la base du feedback.
-## Accord de Contribution
+## Légal
-En soumettant une pull request, vous acceptez que vos contributions soient sous licence selon la même licence que le projet ([Apache 2.0](../LICENSE)).
+En contribuant, tu acceptes que tes contributions soient sous licence Apache 2.0, conformément à la licence de Roo Code.
diff --git a/locales/fr/README.md b/locales/fr/README.md
index 9be04dd323..f8c1743490 100644
--- a/locales/fr/README.md
+++ b/locales/fr/README.md
@@ -1,7 +1,7 @@
-
+
@@ -43,17 +43,17 @@
Que vous recherchiez un partenaire de codage flexible, un architecte système, ou des rôles spécialisés comme un ingénieur QA ou un chef de produit, Roo Code peut vous aider à développer des logiciels plus efficacement.
-Consultez le [CHANGELOG](../CHANGELOG.md) pour des mises à jour détaillées et des corrections.
+Consultez le [CHANGELOG](../../CHANGELOG.md) pour des mises à jour détaillées et des corrections.
---
-## 🎉 Roo Code 3.14 est sorti
+## 🎉 Roo Code 3.17 est sorti
-Roo Code 3.14 apporte de nouvelles fonctionnalités et améliorations basées sur vos commentaires !
+Roo Code 3.17 apporte de nouvelles fonctionnalités puissantes et des améliorations basées sur vos commentaires !
-- **Cache pour les prompts** - `gemini-2.5-pro-preview-03-25` prend maintenant en charge le cache des prompts dans le fournisseur Gemini (Vertex et OpenRouter bientôt disponibles).
-- **Outils d'édition améliorés** - Les outils `search_and_replace` et `insert_content` ont été améliorés et ne sont plus expérimentaux.
-- **Quantité d'autres améliorations** - Nombreuses corrections et optimisations dans toute l'extension.
+- **Mise en cache implicite pour Gemini** - Les appels API Gemini sont désormais automatiquement mis en cache, réduisant les coûts d'API.
+- **Sélection de mode plus intelligente** - Les définitions de mode peuvent maintenant inclure des indications sur quand chaque mode doit être utilisé, permettant une meilleure orchestration.
+- **Condensation intelligente du contexte** - Résume intelligemment l'historique des conversations lorsque le contexte est plein au lieu de le tronquer (activez-le dans Paramètres -> Expérimental).
---
@@ -116,7 +116,7 @@ Faites fonctionner Roo Code à votre manière avec :
- **Discord :** [Rejoignez notre serveur Discord](https://discord.gg/roocode) pour une aide en temps réel et des discussions
- **Reddit :** [Visitez notre subreddit](https://www.reddit.com/r/RooCode) pour partager des expériences et des astuces
-- **GitHub :** Signalez des [problèmes](https://github.com/RooVetGit/Roo-Code/issues) ou demandez de nouvelles [fonctionnalités](https://github.com/RooVetGit/Roo-Code/discussions/categories/feature-requests?discussions_q=is%3Aopen+category%3A%22Feature+Requests%22+sort%3Atop)
+- **GitHub :** Signalez des [problèmes](https://github.com/RooCodeInc/Roo-Code/issues) ou demandez de nouvelles [fonctionnalités](https://github.com/RooCodeInc/Roo-Code/discussions/categories/feature-requests?discussions_q=is%3Aopen+category%3A%22Feature+Requests%22+sort%3Atop)
---
@@ -125,7 +125,7 @@ Faites fonctionner Roo Code à votre manière avec :
1. **Clonez** le dépôt :
```sh
-git clone https://github.com/RooVetGit/Roo-Code.git
+git clone https://github.com/RooCodeInc/Roo-Code.git
```
2. **Installez les dépendances** :
@@ -178,31 +178,36 @@ Nous adorons les contributions de la communauté ! Commencez par lire notre [CON
Merci à tous nos contributeurs qui ont aidé à améliorer Roo Code !
-| mrubens| saoudrizwan| cte| samhvw8| daniel-lxs| a8trejo|
-|:---:|:---:|:---:|:---:|:---:|:---:|
-| ColemanRoo| stea9499| joemanley201| System233| hannesrudolph| jquanton|
-| nissa-seru| KJ7LNW| NyxJae| MuriloFP| d-oit| punkpeye|
-| Smartsheet-JB-Brown| monotykamary| wkordalski| feifei325| cannuri| lloydchang|
-| vigneshsubbiah16| qdaxb| Szpadel| Premshay| psv2522| diarmidmackenzie|
-| lupuletic| elianiva| olweraltuve| sachasayan| afshawnlotfi| pugazhendhi-m|
-| aheizi| RaySinner| PeterDaveHello| nbihan-mediware| dtrugman| emshvac|
-| kyle-apex| pdecat| zhangtony239| Lunchb0ne| arthurauffray| upamune|
-| StevenTCramer| sammcj| p12tic| gtaylor| aitoroses| yt3trees|
-| franekp| yongjer| vincentsong| vagadiya| teddyOOXX| eonghk|
-| taisukeoe| heyseth| ross| philfung| napter| mdp|
-| SplittyDev| Chenjiayuan195| jcbdev| GitlyHallows| bramburn| benzntech|
-| axkirillov| anton-otee| shoopapa| jwcraig| kinandan| kohii|
-| lightrabbit| olup| mecab| nevermorec| im47cn| hongzio|
-| dqroid| dairui1| bannzai| axmo| asychin| ashktn|
-| eltociear| PretzelVector| cdlliuy| student20880| shohei-ihaya| shaybc|
-| shariqriazz| seedlord| samir-nimbly| ronyblum| refactorthis| pokutuna|
-| philipnext| oprstchn| nobu007| mosleyit| moqimoqidea| mlopezr|
-| hesara| DeXtroTip| celestial-vault| linegel| snoyiatk| dbasclpy|
-| dleen| chadgauth| bogdan0083| Atlogit| atlasgong| andreastempsch|
-| QuinsZouls| alarno| adamwlarson| AMHesch| amittell| Yoshino-Yukitaro|
-| Yikai-Liao| vladstudio| NamesMT| tmsjngx0| tgfjt| maekawataiki|
-| samsilveira| 01Rian| Sarke| kvokka| marvijo-code| mamertofabian|
-| libertyteeth| shtse8| Jdo300| | | |
+
+| mrubens | saoudrizwan | cte | samhvw8 | daniel-lxs | a8trejo |
+| :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |
+| ColemanRoo | hannesrudolph | KJ7LNW | stea9499 | joemanley201 | System233 |
+| nissa-seru | jquanton | NyxJae | MuriloFP | d-oit | punkpeye |
+| wkordalski | Smartsheet-JB-Brown | monotykamary | elianiva | cannuri | feifei325 |
+| zhangtony239 | sachasayan | lloydchang | vigneshsubbiah16 | Szpadel | qdaxb |
+| lupuletic | Premshay | psv2522 | diarmidmackenzie | aheizi | olweraltuve |
+| jr | dtrugman | nbihan-mediware | PeterDaveHello | RaySinner | pugazhendhi-m |
+| afshawnlotfi | shariqriazz | pdecat | kyle-apex | emshvac | Lunchb0ne |
+| xyOz-dev | arthurauffray | upamune | StevenTCramer | sammcj | p12tic |
+| gtaylor | aitoroses | benzntech | ross | heyseth | taisukeoe |
+| dlab-anton | eonghk | teddyOOXX | vagadiya | vincentsong | yongjer |
+| ashktn | franekp | yt3trees | anton-otee | axkirillov | bramburn |
+| snoyiatk | GitlyHallows | jcbdev | Chenjiayuan195 | julionav | SplittyDev |
+| mdp | napter | philfung | im47cn | shoopapa | jwcraig |
+| kinandan | kohii | lightrabbit | olup | mecab | nevermorec |
+| hongzio | GOODBOY008 | dqroid | dairui1 | bannzai | axmo |
+| asychin | amittell | Yoshino-Yukitaro | Yikai-Liao | SmartManoj | PretzelVector |
+| zetaloop | cdlliuy | student20880 | shohei-ihaya | shaybc | seedlord |
+| samir-nimbly | ronyblum | robertheadley | refactorthis | pokutuna | philipnext |
+| oprstchn | nobu007 | mosleyit | moqimoqidea | mlopezr | zxdvd |
+| DeXtroTip | pfitz | celestial-vault | linegel | dbasclpy | Deon588 |
+| dleen | chadgauth | olearycrew | bogdan0083 | Atlogit | atlasgong |
+| andreastempsch | alasano | QuinsZouls | HadesArchitect | alarno | adamwlarson |
+| AMHesch | vladstudio | NamesMT | tmsjngx0 | tgfjt | maekawataiki |
+| samsilveira | mr-ryan-james | 01Rian | Sarke | kvokka | ecmasx |
+| marvijo-code | mamertofabian | monkeyDluffy6017 | libertyteeth | shtse8 | ksze |
+| Jdo300 | hesara | | | | |
+
## Licence
diff --git a/locales/hi/CODE_OF_CONDUCT.md b/locales/hi/CODE_OF_CONDUCT.md
index 4f1529c591..9d22f43944 100644
--- a/locales/hi/CODE_OF_CONDUCT.md
+++ b/locales/hi/CODE_OF_CONDUCT.md
@@ -1,3 +1,7 @@
+[English](../../CODE_OF_CONDUCT.md) • [Català](../ca/CODE_OF_CONDUCT.md) • [Deutsch](../de/CODE_OF_CONDUCT.md) • [Español](../es/CODE_OF_CONDUCT.md) • [Français](../fr/CODE_OF_CONDUCT.md) • हिंदी • [Italiano](../it/CODE_OF_CONDUCT.md) • [Nederlands](../nl/CODE_OF_CONDUCT.md) • [Русский](../ru/CODE_OF_CONDUCT.md)
+
+[日本語](../ja/CODE_OF_CONDUCT.md) • [한국어](../ko/CODE_OF_CONDUCT.md) • [Polski](../pl/CODE_OF_CONDUCT.md) • [Português (BR)](../pt-BR/CODE_OF_CONDUCT.md) • [Türkçe](../tr/CODE_OF_CONDUCT.md) • [Tiếng Việt](../vi/CODE_OF_CONDUCT.md) • [简体中文](../zh-CN/CODE_OF_CONDUCT.md) • [繁體中文](../zh-TW/CODE_OF_CONDUCT.md)
+
# योगदानकर्ता संधि आचार संहिता
## हमारी प्रतिज्ञा
diff --git a/locales/hi/CONTRIBUTING.md b/locales/hi/CONTRIBUTING.md
index 9f388c295f..17d62e1794 100644
--- a/locales/hi/CONTRIBUTING.md
+++ b/locales/hi/CONTRIBUTING.md
@@ -1,173 +1,129 @@
-# Roo Code में योगदान देना
+[English](../../CONTRIBUTING.md) • [Català](../ca/CONTRIBUTING.md) • [Deutsch](../de/CONTRIBUTING.md) • [Español](../es/CONTRIBUTING.md) • [Français](../fr/CONTRIBUTING.md) • हिंदी • [Italiano](../it/CONTRIBUTING.md) • [Nederlands](../nl/CONTRIBUTING.md) • [Русский](../ru/CONTRIBUTING.md)
-हम खुश हैं कि आप Roo Code में योगदान देने में रुचि रखते हैं। चाहे आप एक बग ठीक कर रहे हों, एक फीचर जोड़ रहे हों, या हमारे दस्तावेज़ों को सुधार रहे हों, हर योगदान Roo Code को अधिक स्मार्ट बनाता है! हमारे समुदाय को जीवंत और स्वागतयोग्य बनाए रखने के लिए, सभी सदस्यों को हमारे [आचार संहिता](CODE_OF_CONDUCT.md) का पालन करना चाहिए।
+[日本語](../ja/CONTRIBUTING.md) • [한국어](../ko/CONTRIBUTING.md) • [Polski](../pl/CONTRIBUTING.md) • [Português (BR)](../pt-BR/CONTRIBUTING.md) • [Türkçe](../tr/CONTRIBUTING.md) • [Tiếng Việt](../vi/CONTRIBUTING.md) • [简体中文](../zh-CN/CONTRIBUTING.md) • [繁體中文](../zh-TW/CONTRIBUTING.md)
-## हमारे समुदाय में शामिल हों
+# Roo Code में योगदान करें
-हम सभी योगदानकर्ताओं को हमारे [Discord समुदाय](https://discord.gg/roocode) में शामिल होने के लिए दृढ़ता से प्रोत्साहित करते हैं! हमारे Discord सर्वर का हिस्सा होने से आपको मदद मिलती है:
+Roo Code एक समुदाय-आधारित प्रोजेक्ट है और हम हर योगदान को बहुत महत्व देते हैं। सहयोग को सरल बनाने के लिए, हम [Issue-First](#issue-first-एप्रोच) पद्धति अपनाते हैं, जिसका अर्थ है कि सभी [Pull Requests (PRs)](#pull-request-सबमिट-करना) को पहले GitHub Issue से जोड़ना आवश्यक है। कृपया इस गाइड को ध्यान से पढ़ें।
-- अपने योगदान पर रीयल-टाइम मदद और मार्गदर्शन प्राप्त करें
-- अन्य योगदानकर्ताओं और कोर टीम के सदस्यों से जुड़ें
-- प्रोजेक्ट के विकास और प्राथमिकताओं से अपडेट रहें
-- ऐसी चर्चाओं में भाग लें जो Roo Code के भविष्य को आकार देती हैं
-- अन्य डेवलपर्स के साथ सहयोग के अवसर खोजें
+## विषय सूची
-## बग या समस्याओं की रिपोर्ट करना
+- [योगदान करने से पहले](#योगदान-करने-से-पहले)
+- [अपना योगदान ढूंढना और योजना बनाना](#अपना-योगदान-ढूंढना-और-योजना-बनाना)
+- [विकास और सबमिशन प्रक्रिया](#विकास-और-सबमिशन-प्रक्रिया)
+- [कानूनी](#कानूनी)
-बग रिपोर्ट हर किसी के लिए Roo Code को बेहतर बनाने में मदद करती हैं! नई समस्या बनाने से पहले, कृपया डुप्लिकेट से बचने के लिए [मौजूदा समस्याओं की खोज करें](https://github.com/RooVetGit/Roo-Code/issues)। जब आप बग की रिपोर्ट करने के लिए तैयार हों, तो हमारे [इश्यूज पेज](https://github.com/RooVetGit/Roo-Code/issues/new/choose) पर जाएं जहां आपको प्रासंगिक जानकारी भरने में मदद करने के लिए एक टेम्पलेट मिलेगा।
+## योगदान करने से पहले
-
+### 1. आचार संहिता
-## किस पर काम करना है यह तय करना
+सभी योगदानकर्ताओं को हमारी [आचार संहिता](./CODE_OF_CONDUCT.md) का पालन करना चाहिए।
-पहले योगदान के लिए एक अच्छा अवसर खोज रहे हैं? हमारे [Roo Code इश्यूज](https://github.com/orgs/RooVetGit/projects/1) Github प्रोजेक्ट के "Issue [Unassigned]" सेक्शन में इश्यूज देखें। ये विशेष रूप से नए योगदानकर्ताओं के लिए और ऐसे क्षेत्रों के लिए क्यूरेट किए गए हैं जहां हमें कुछ मदद की जरूरत होगी!
+### 2. प्रोजेक्ट रोडमैप
-हम अपने [दस्तावेज़ीकरण](https://docs.roocode.com/) में योगदान का भी स्वागत करते हैं! चाहे वह टाइपो ठीक करना हो, मौजूदा गाइड को सुधारना हो, या नई शैक्षिक सामग्री बनाना हो - हम संसाधनों का एक समुदाय-संचालित भंडार बनाना चाहते हैं जो हर किसी को Roo Code का अधिकतम उपयोग करने में मदद करे। आप फ़ाइल को संपादित करने के लिए किसी भी पृष्ठ पर "Edit this page" पर क्लिक कर सकते हैं या सीधे https://github.com/RooVetGit/Roo-Code-Docs में जा सकते हैं।
+हमारा रोडमैप प्रोजेक्ट की दिशा तय करता है। अपने योगदान को इन प्रमुख लक्ष्यों के साथ संरेखित करें:
-यदि आप एक बड़ी विशेषता पर काम करने की योजना बना रहे हैं, तो कृपया पहले एक [फीचर अनुरोध](https://github.com/RooVetGit/Roo-Code/discussions/categories/feature-requests?discussions_q=is%3Aopen+category%3A%22Feature+Requests%22+sort%3Atop) बनाएं ताकि हम चर्चा कर सकें कि क्या यह Roo Code के दृष्टिकोण के अनुरूप है। आप नीचे दिए गए हमारे [प्रोजेक्ट रोडमैप](#प्रोजेक्ट-रोडमैप) को भी देख सकते हैं यह जानने के लिए कि क्या आपका विचार हमारी रणनीतिक दिशा के अनुरूप है।
+### विश्वसनीयता पहले
-## प्रोजेक्ट रोडमैप
+- सुनिश्चित करें कि diff एडिटिंग और कमांड एक्जीक्यूशन लगातार विश्वसनीय हों
+- नियमित उपयोग को हतोत्साहित करने वाले फ्रिक्शन पॉइंट्स को कम करें
+- सभी भाषाओं और प्लेटफॉर्म्स पर सुचारू संचालन की गारंटी दें
+- विभिन्न AI प्रदाताओं और मॉडल्स के लिए मजबूत समर्थन का विस्तार करें
-Roo Code का एक स्पष्ट विकास रोडमैप है जो हमारी प्राथमिकताओं और भविष्य की दिशा का मार्गदर्शन करता है। हमारे रोडमैप को समझने से आपको मदद मिल सकती है:
+### बेहतर उपयोगकर्ता अनुभव
-- अपने योगदान को प्रोजेक्ट के लक्ष्यों के साथ संरेखित करना
-- ऐसे क्षेत्रों की पहचान करना जहां आपकी विशेषज्ञता सबसे मूल्यवान होगी
-- कुछ डिज़ाइन निर्णयों के पीछे के संदर्भ को समझना
-- नई विशेषताओं के लिए प्रेरणा पाना जो हमारे दृष्टिकोण का समर्थन करती हैं
+- स्पष्टता और सहजता के लिए UI/UX को सरल बनाएं
+- डेवलपर्स के उच्च अपेक्षाओं को पूरा करने के लिए वर्कफ़्लो में निरंतर सुधार करें
-हमारा वर्तमान रोडमैप छह प्रमुख स्तंभों पर केंद्रित है:
+### एजेंट प्रदर्शन में अग्रणी
-### प्रोवाइडर सपोर्ट
+- वास्तविक दुनिया की उत्पादकता को मापने के लिए व्यापक मूल्यांकन बेंचमार्क (evals) स्थापित करें
+- हर किसी के लिए इन मूल्यांकनों को आसानी से चलाना और समझना संभव बनाएं
+- ऐसे सुधार लाएं जो मूल्यांकन स्कोर में स्पष्ट वृद्धि दिखाएं
-हम जितने संभव हो सके उतने प्रोवाइडर्स को सपोर्ट करना चाहते हैं:
+अपने PR में इन क्षेत्रों से संबंधित कार्य का उल्लेख करें।
-- "OpenAI Compatible" के लिए अधिक बहुमुखी समर्थन
-- xAI, Microsoft Azure AI, Alibaba Cloud Qwen, IBM Watsonx, Together AI, DeepInfra, Fireworks AI, Cohere, Perplexity AI, FriendliAI, Replicate
-- Ollama और LM Studio के लिए बेहतर समर्थन
+### 3. Roo Code कम्युनिटी से जुड़ें
-### मॉडल सपोर्ट
+- **मुख्य तरीका:** हमारे [Discord](https://discord.gg/roocode) से जुड़ें और **Hannes Rudolph (`hrudolph`)** को DM भेजें।
+- **विकल्प:** अनुभवी योगदानकर्ता [GitHub Projects](https://github.com/orgs/RooCodeInc/projects/1) के माध्यम से सीधे भाग ले सकते हैं।
-हम चाहते हैं कि Roo जितना संभव हो उतने मॉडल पर अच्छी तरह से काम करे, जिसमें लोकल मॉडल भी शामिल हैं:
+## अपना योगदान ढूंढना और योजना बनाना
-- कस्टम सिस्टम प्रॉम्प्टिंग और वर्कफ़्लोज़ के माध्यम से लोकल मॉडल सपोर्ट
-- बेंचमार्किंग एवैल्युएशन और टेस्ट केस
+### योगदान के प्रकार
-### सिस्टम सपोर्ट
+- **बग फिक्स:** कोड की समस्याओं को हल करना।
+- **नई विशेषताएं:** नई कार्यक्षमता जोड़ना।
+- **डॉक्युमेंटेशन:** गाइड सुधारना और स्पष्टता बढ़ाना।
-हम चाहते हैं कि Roo हर किसी के कंप्यूटर पर अच्छी तरह से चले:
+### Issue-First एप्रोच
-- क्रॉस प्लेटफॉर्म टर्मिनल इंटीग्रेशन
-- Mac, Windows और Linux के लिए मजबूत और सुसंगत समर्थन
+हर योगदान GitHub Issue से शुरू होना चाहिए।
-### डॉक्युमेंटेशन
+- **मौजूदा Issues देखें:** [GitHub Issues](https://github.com/RooCodeInc/Roo-Code/issues) में खोजें।
+- **Issue बनाएं:** उपयुक्त टेम्पलेट का उपयोग करें:
+ - **बग:** "Bug Report" टेम्पलेट।
+ - **फीचर्स:** "Detailed Feature Proposal" टेम्पलेट। शुरू करने से पहले अनुमोदन आवश्यक है।
+- **Issue क्लेम करें:** कमेंट करें और आधिकारिक असाइनमेंट का इंतजार करें।
-हम सभी उपयोगकर्ताओं और योगदानकर्ताओं के लिए व्यापक, सुलभ दस्तावेज़ीकरण चाहते हैं:
+**अनुमोदित Issue के बिना PR बंद किए जा सकते हैं।**
-- विस्तारित उपयोगकर्ता गाइड और ट्यूटोरियल
-- स्पष्ट API दस्तावेज़ीकरण
-- योगदानकर्ताओं के लिए बेहतर मार्गदर्शन
-- बहुभाषी दस्तावेज़ीकरण संसाधन
-- इंटरैक्टिव उदाहरण और कोड सैंपल
+### क्या काम करें चुनना
-### स्थिरता
+- [GitHub प्रोजेक्ट](https://github.com/orgs/RooCodeInc/projects/1) में असाइन न किए गए "Good First Issues" देखें।
+- डॉक्युमेंटेशन के लिए, [Roo Code Docs](https://github.com/RooCodeInc/Roo-Code-Docs) देखें।
-हम बग की संख्या को काफी कम करना और स्वचालित परीक्षण को बढ़ाना चाहते हैं:
+### बग या समस्या रिपोर्ट करना
-- डीबग लॉगिंग स्विच
-- बग/सपोर्ट अनुरोधों के साथ भेजने के लिए "मशीन/टास्क इन्फॉर्मेशन" कॉपी बटन
+- पहले मौजूदा रिपोर्ट देखें।
+- ["Bug Report" टेम्पलेट](https://github.com/RooCodeInc/Roo-Code/issues/new/choose) का उपयोग करके नए बग रिपोर्ट बनाएं।
+- **सुरक्षा कमजोरियां:** [security advisories](https://github.com/RooCodeInc/Roo-Code/security/advisories/new) के माध्यम से निजी तौर पर रिपोर्ट करें।
-### अंतर्राष्ट्रीयकरण
+## विकास और सबमिशन प्रक्रिया
-हम चाहते हैं कि Roo हर किसी की भाषा बोले:
+### विकास सेटअप
-- 我们希望 Roo Code 说每个人的语言
-- Queremos que Roo Code hable el idioma de todos
-- हम चाहते हैं कि Roo Code हर किसी की भाषा बोले
-- نريد أن يتحدث Roo Code لغة الجميع
+1. **Fork & Clone:**
-हम विशेष रूप से उन योगदानों का स्वागत करते हैं जो हमारे रोडमैप लक्ष्यों को आगे बढ़ाते हैं। यदि आप कुछ ऐसा कर रहे हैं जो इन स्तंभों के अनुरूप है, तो कृपया अपने PR विवरण में इसका उल्लेख करें।
-
-## डेवलपमेंट सेटअप
-
-1. रिपो **क्लोन** करें:
-
-```sh
-git clone https://github.com/RooVetGit/Roo-Code.git
```
-
-2. **डिपेंडेंसीज इंस्टॉल** करें:
-
-```sh
-npm run install:all
-```
-
-3. **वेबव्यू शुरू करें (Vite/React ऐप HMR के साथ)**:
-
-```sh
-npm run dev
+git clone https://github.com/आपका_यूज़रनेम/Roo-Code.git
```
-4. **डिबग**:
- VSCode में `F5` दबाएं (या **Run** → **Start Debugging**) Roo Code लोड के साथ एक नया सेशन खोलने के लिए।
+2. **डिपेंडेंसी इंस्टॉल करें:**
-वेबव्यू में परिवर्तन तुरंत दिखाई देंगे। कोर एक्सटेंशन में परिवर्तनों के लिए एक्सटेंशन होस्ट को रीस्टार्ट करने की आवश्यकता होगी।
-
-वैकल्पिक रूप से आप .vsix बना सकते हैं और इसे सीधे VSCode में इंस्टॉल कर सकते हैं:
-
-```sh
-npm run build
```
-
-`bin/` डायरेक्टरी में एक `.vsix` फ़ाइल दिखाई देगी जिसे इस कमांड से इंस्टॉल किया जा सकता है:
-
-```sh
-code --install-extension bin/roo-cline-.vsix
+npm run install:all
```
-## कोड लिखना और सबमिट करना
-
-कोई भी Roo Code में कोड का योगदान दे सकता है, लेकिन हम आपसे अनुरोध करते हैं कि आप इन दिशानिर्देशों का पालन करें ताकि आपके योगदान को सुचारू रूप से एकीकृत किया जा सके:
-
-1. **पुल रिक्वेस्ट को फोकस्ड रखें**
-
- - PR को एक ही फीचर या बग फिक्स तक सीमित रखें
- - बड़े परिवर्तनों को छोटी, संबंधित PR में विभाजित करें
- - परिवर्तनों को तार्किक कमिट्स में तोड़ें जिन्हें स्वतंत्र रूप से समीक्षा की जा सके
-
-2. **कोड क्वालिटी**
+3. **डिबगिंग:** VS Code में `F5` दबाएं।
- - सभी PR को CI चेक पास करना चाहिए जिसमें लिंटिंग और फॉर्मेटिंग दोनों शामिल हैं
- - सबमिट करने से पहले किसी भी ESLint चेतावनी या त्रुटि को संबोधित करें
- - Ellipsis, हमारे स्वचालित कोड समीक्षा टूल से सभी फीडबैक का जवाब दें
- - TypeScript के बेस्ट प्रैक्टिस का पालन करें और टाइप सुरक्षा बनाए रखें
+### कोड लिखने के दिशा-निर्देश
-3. **टेस्टिंग**
+- प्रति फीचर या फिक्स एक फोकस्ड PR।
+- ESLint और TypeScript बेस्ट प्रैक्टिस का पालन करें।
+- स्पष्ट, वर्णनात्मक कमिट मैसेज लिखें जो Issues को रेफर करें (जैसे `Fixes #123`)।
+- पूर्ण टेस्टिंग प्रदान करें (`npm test`)।
+- सबमिट करने से पहले अपनी ब्रांच को नवीनतम `main` पर रीबेस करें।
- - नई विशेषताओं के लिए टेस्ट जोड़ें
- - यह सुनिश्चित करने के लिए `npm test` चलाएं कि सभी टेस्ट पास हों
- - यदि आपके परिवर्तन उन्हें प्रभावित करते हैं तो मौजूदा टेस्ट अपडेट करें
- - जहां उपयुक्त हो, यूनिट टेस्ट और इंटीग्रेशन टेस्ट दोनों शामिल करें
+### Pull Request सबमिट करना
-4. **कमिट दिशानिर्देश**
+- अगर आप शुरुआती फीडबैक चाहते हैं तो **ड्राफ्ट PR** से शुरू करें।
+- Pull Request टेम्पलेट का पालन करते हुए अपने परिवर्तनों का स्पष्ट वर्णन करें।
+- UI परिवर्तनों के लिए स्क्रीनशॉट/वीडियो प्रदान करें।
+- बताएं कि क्या डॉक्युमेंटेशन अपडेट आवश्यक हैं।
- - स्पष्ट, वर्णनात्मक कमिट संदेश लिखें
- - #issue-number का उपयोग करके कमिट्स में प्रासंगिक मुद्दों का संदर्भ दें
+### Pull Request नीति
-5. **सबमिट करने से पहले**
+- पूर्व-अनुमोदित और असाइन किए गए Issues का संदर्भ देना चाहिए।
+- नीति का पालन न करने वाले PR बंद किए जा सकते हैं।
+- PR को CI टेस्ट पास करना चाहिए, रोडमैप से मेल खाना चाहिए, और स्पष्ट डॉक्युमेंटेशन होनी चाहिए।
- - अपनी ब्रांच को लेटेस्ट मेन पर रीबेस करें
- - सुनिश्चित करें कि आपकी ब्रांच सफलतापूर्वक बिल्ड होती है
- - डबल-चेक करें कि सभी टेस्ट पास हो रहे हैं
- - अपने परिवर्तनों की समीक्षा करें किसी भी डिबगिंग कोड या कंसोल लॉग के लिए
+### समीक्षा प्रक्रिया
-6. **पुल रिक्वेस्ट विवरण**
- - स्पष्ट रूप से बताएं कि आपके परिवर्तन क्या करते हैं
- - परिवर्तनों का परीक्षण करने के लिए चरण शामिल करें
- - किसी भी ब्रेकिंग चेंज की सूची बनाएं
- - UI परिवर्तनों के लिए स्क्रीनशॉट जोड़ें
+- **दैनिक ट्रायज:** मेंटेनर्स द्वारा त्वरित जांच।
+- **साप्ताहिक गहन समीक्षा:** व्यापक मूल्यांकन।
+- **फीडबैक के आधार पर तेजी से सुधार** करें।
-## योगदान समझौता
+## कानूनी
-पुल रिक्वेस्ट सबमिट करके, आप सहमत होते हैं कि आपके योगदान को प्रोजेक्ट के समान लाइसेंस ([Apache 2.0](../LICENSE)) के तहत लाइसेंस दिया जाएगा।
+Pull Request सबमिट करके, आप सहमत होते हैं कि आपके योगदान Roo Code के लाइसेंसिंग के अनुरूप Apache 2.0 लाइसेंस के तहत लाइसेंस किए जाएंगे।
diff --git a/locales/hi/README.md b/locales/hi/README.md
index 2ec7584a75..0116b69fda 100644
--- a/locales/hi/README.md
+++ b/locales/hi/README.md
@@ -1,7 +1,7 @@
+# Contribuire a Roo Code
-## Decidere Su Cosa Lavorare
+Roo Code è un progetto guidato dalla community e apprezziamo molto ogni contributo. Per semplificare la collaborazione, operiamo secondo un approccio [Issue-First](#approccio-issue-first), il che significa che tutte le [Pull Request (PR)](#inviare-una-pull-request) devono prima essere collegate a una Issue GitHub. Ti preghiamo di leggere attentamente questa guida.
-Cerchi un buon primo contributo? Controlla i problemi nella sezione "Issue [Unassigned]" del nostro [Progetto Github di Roo Code](https://github.com/orgs/RooVetGit/projects/1). Questi sono specificamente selezionati per nuovi contributori e aree in cui ci piacerebbe avere un po' di aiuto!
+## Indice
-Accogliamo anche contributi alla nostra [documentazione](https://docs.roocode.com/)! Che si tratti di correggere errori di battitura, migliorare guide esistenti o creare nuovi contenuti educativi - ci piacerebbe costruire un repository di risorse guidato dalla comunità che aiuti tutti a ottenere il massimo da Roo Code. Puoi cliccare su "Edit this page" su qualsiasi pagina per arrivare rapidamente al punto giusto in Github per modificare il file, oppure puoi andare direttamente a https://github.com/RooVetGit/Roo-Code-Docs.
+- [Prima di contribuire](#prima-di-contribuire)
+- [Trovare e pianificare il tuo contributo](#trovare-e-pianificare-il-tuo-contributo)
+- [Processo di sviluppo e invio](#processo-di-sviluppo-e-invio)
+- [Legale](#legale)
-Se stai pianificando di lavorare su una funzionalità più grande, per favore crea prima una [richiesta di funzionalità](https://github.com/RooVetGit/Roo-Code/discussions/categories/feature-requests?discussions_q=is%3Aopen+category%3A%22Feature+Requests%22+sort%3Atop) così possiamo discutere se si allinea con la visione di Roo Code. Puoi anche consultare la nostra [Roadmap del Progetto](#roadmap-del-progetto) qui sotto per vedere se la tua idea si adatta alla nostra direzione strategica.
+## Prima di contribuire
-## Roadmap del Progetto
+### 1. Codice di condotta
-Roo Code ha una chiara roadmap di sviluppo che guida le nostre priorità e la direzione futura. Comprendere la nostra roadmap può aiutarti a:
+Tutti i collaboratori devono rispettare il nostro [Codice di condotta](./CODE_OF_CONDUCT.md).
-- Allineare i tuoi contributi con gli obiettivi del progetto
-- Identificare aree in cui la tua esperienza sarebbe più preziosa
-- Comprendere il contesto dietro certe decisioni di design
-- Trovare ispirazione per nuove funzionalità che supportino la nostra visione
+### 2. Roadmap del progetto
-La nostra roadmap attuale si concentra su sei pilastri chiave:
+La nostra roadmap guida la direzione del progetto. Allinea i tuoi contributi con questi obiettivi chiave:
-### Supporto Provider
+### Affidabilità prima di tutto
-Miriamo a supportare quanti più provider possibile:
+- Garantire che l'editing delle differenze e l'esecuzione dei comandi siano costantemente affidabili
+- Ridurre i punti di attrito che scoraggiano l'uso regolare
+- Garantire un funzionamento fluido in tutte le lingue e su tutte le piattaforme
+- Ampliare il supporto robusto per una vasta gamma di provider e modelli di IA
-- Supporto più versatile per "OpenAI Compatible"
-- xAI, Microsoft Azure AI, Alibaba Cloud Qwen, IBM Watsonx, Together AI, DeepInfra, Fireworks AI, Cohere, Perplexity AI, FriendliAI, Replicate
-- Supporto migliorato per Ollama e LM Studio
+### Esperienza utente migliorata
-### Supporto Modelli
+- Semplificare l'interfaccia utente per maggiore chiarezza e intuitività
+- Migliorare continuamente il flusso di lavoro per soddisfare le elevate aspettative degli sviluppatori
-Vogliamo che Roo funzioni al meglio su quanti più modelli possibile, inclusi i modelli locali:
+### Leadership nelle prestazioni degli agenti
-- Supporto per modelli locali attraverso prompt di sistema personalizzati e flussi di lavoro
-- Valutazioni di benchmark e casi di test
+- Stabilire parametri di valutazione completi (evals) per misurare la produttività nel mondo reale
+- Rendere facile per tutti eseguire e interpretare queste valutazioni
+- Fornire miglioramenti che dimostrino chiari aumenti nei punteggi di valutazione
-### Supporto Sistemi
+Menziona l'allineamento con queste aree nelle tue PR.
-Vogliamo che Roo funzioni bene sul computer di tutti:
+### 3. Unisciti alla community Roo Code
-- Integrazione del terminale multipiattaforma
-- Supporto forte e coerente per Mac, Windows e Linux
+- **Principale:** Unisciti al nostro [Discord](https://discord.gg/roocode) e invia un DM a **Hannes Rudolph (`hrudolph`)**.
+- **Alternativa:** I collaboratori esperti possono partecipare direttamente tramite [GitHub Projects](https://github.com/orgs/RooCodeInc/projects/1).
-### Documentazione
+## Trovare e pianificare il tuo contributo
-Vogliamo una documentazione completa e accessibile per tutti gli utenti e contributori:
+### Tipi di contributi
-- Guide utente e tutorial ampliati
-- Documentazione API chiara
-- Migliore orientamento per i contributori
-- Risorse di documentazione multilingue
-- Esempi interattivi e campioni di codice
+- **Correzione bug:** Risolvere problemi nel codice.
+- **Nuove funzionalità:** Aggiungere nuove funzionalità.
+- **Documentazione:** Migliorare guide e chiarezza.
-### Stabilità
+### Approccio Issue-First
-Vogliamo ridurre significativamente il numero di bug e aumentare i test automatizzati:
+Tutti i contributi devono iniziare con una Issue GitHub.
-- Interruttore di registrazione debug
-- Pulsante di copia "Informazioni Macchina/Attività" per l'invio con richieste di supporto/bug
+- **Verificare le issue esistenti:** Cerca su [GitHub Issues](https://github.com/RooCodeInc/Roo-Code/issues).
+- **Creare una issue:** Usa i template appropriati:
+ - **Bug:** Template "Bug Report".
+ - **Funzionalità:** Template "Detailed Feature Proposal". Approvazione richiesta prima di iniziare.
+- **Reclamare issue:** Commenta e attendi l'assegnazione ufficiale.
-### Internazionalizzazione
+**Le PR senza issue approvate potrebbero essere chiuse.**
-Vogliamo che Roo parli la lingua di tutti:
+### Decidere su cosa lavorare
-- 我们希望 Roo Code 说每个人的语言
-- Queremos que Roo Code hable el idioma de todos
-- हम चाहते हैं कि Roo Code हर किसी की भाषा बोले
-- نريد أن يتحدث Roo Code لغة الجميع
+- Controlla il [Progetto GitHub](https://github.com/orgs/RooCodeInc/projects/1) per "Good First Issues" non assegnate.
+- Per la documentazione, visita [Roo Code Docs](https://github.com/RooCodeInc/Roo-Code-Docs).
-Accogliamo particolarmente i contributi che fanno progredire gli obiettivi della nostra roadmap. Se stai lavorando su qualcosa che si allinea con questi pilastri, per favore menzionalo nella descrizione della tua PR.
+### Segnalare bug
-## Configurazione per lo Sviluppo
+- Controlla prima i report esistenti.
+- Crea nuovi report di bug usando il [template "Bug Report"](https://github.com/RooCodeInc/Roo-Code/issues/new/choose).
+- **Problemi di sicurezza:** Segnala privatamente tramite [security advisories](https://github.com/RooCodeInc/Roo-Code/security/advisories/new).
-1. **Clona** il repository:
+## Processo di sviluppo e invio
-```sh
-git clone https://github.com/RooVetGit/Roo-Code.git
-```
+### Configurazione dello sviluppo
-2. **Installa le dipendenze**:
+1. **Fork & Clona:**
-```sh
-npm run install:all
```
-
-3. **Avvia la webview (app Vite/React con HMR)**:
-
-```sh
-npm run dev
+git clone https://github.com/TUO_USERNAME/Roo-Code.git
```
-4. **Debug**:
- Premi `F5` (o **Run** → **Start Debugging**) in VSCode per aprire una nuova sessione con Roo Code caricato.
-
-Le modifiche alla webview appariranno immediatamente. Le modifiche all'estensione principale richiederanno un riavvio dell'host dell'estensione.
-
-In alternativa puoi creare un file .vsix e installarlo direttamente in VSCode:
+2. **Installa le dipendenze:**
-```sh
-npm run build
```
-
-Un file `.vsix` apparirà nella directory `bin/` che può essere installato con:
-
-```sh
-code --install-extension bin/roo-cline-.vsix
+npm run install:all
```
-## Scrivere e Inviare Codice
-
-Chiunque può contribuire con codice a Roo Code, ma ti chiediamo di seguire queste linee guida per assicurare che i tuoi contributi possano essere integrati senza problemi:
-
-1. **Mantieni le Pull Request Focalizzate**
-
- - Limita le PR a una singola funzionalità o correzione di bug
- - Suddividi i cambiamenti più grandi in PR più piccole e correlate
- - Suddividi i cambiamenti in commit logici che possono essere revisionati indipendentemente
-
-2. **Qualità del Codice**
+3. **Debug:** Apri con VS Code (`F5`).
- - Tutte le PR devono passare i controlli CI che includono sia linting che formattazione
- - Risolvi qualsiasi avviso o errore di ESLint prima di inviare
- - Rispondi a tutti i feedback da Ellipsis, il nostro strumento automatico di revisione del codice
- - Segui le migliori pratiche di TypeScript e mantieni la sicurezza dei tipi
+### Linee guida per scrivere codice
-3. **Testing**
+- Una PR focalizzata per funzionalità o correzione.
+- Segui le best practice di ESLint e TypeScript.
+- Scrivi commit chiari e descrittivi che fanno riferimento alle issue (es. `Fixes #123`).
+- Fornisci test approfonditi (`npm test`).
+- Fai rebase sul branch `main` più recente prima dell'invio.
- - Aggiungi test per le nuove funzionalità
- - Esegui `npm test` per assicurarti che tutti i test passino
- - Aggiorna i test esistenti se le tue modifiche li influenzano
- - Includi sia test unitari che test di integrazione dove appropriato
+### Inviare una Pull Request
-4. **Linee Guida per i Commit**
+- Inizia come **PR in bozza** se cerchi feedback anticipato.
+- Descrivi chiaramente le tue modifiche seguendo il Template di Pull Request.
+- Fornisci screenshot/video per modifiche UI.
+- Indica se sono necessari aggiornamenti alla documentazione.
- - Scrivi messaggi di commit chiari e descrittivi
- - Fai riferimento ai problemi rilevanti nei commit usando #numero-problema
+### Politica di Pull Request
-5. **Prima di Inviare**
+- Deve fare riferimento a issue pre-approvate e assegnate.
+- Le PR che non rispettano la politica potrebbero essere chiuse.
+- Le PR dovrebbero superare i test CI, allinearsi con la roadmap e avere documentazione chiara.
- - Fai il rebase del tuo branch sull'ultimo main
- - Assicurati che il tuo branch si costruisca con successo
- - Ricontrolla che tutti i test stiano passando
- - Rivedi le tue modifiche per qualsiasi codice di debug o log della console
+### Processo di revisione
-6. **Descrizione della Pull Request**
- - Descrivi chiaramente cosa fanno le tue modifiche
- - Includi passaggi per testare le modifiche
- - Elenca eventuali breaking changes
- - Aggiungi screenshot per modifiche UI
+- **Triage quotidiano:** Controlli rapidi da parte dei maintainer.
+- **Revisione settimanale approfondita:** Valutazione completa.
+- **Itera rapidamente** in base al feedback.
-## Accordo di Contribuzione
+## Legale
-Inviando una pull request, accetti che i tuoi contributi saranno concessi in licenza con la stessa licenza del progetto ([Apache 2.0](../LICENSE)).
+Inviando una pull request, accetti che i tuoi contributi siano concessi in licenza sotto la Licenza Apache 2.0, in linea con la licenza di Roo Code.
diff --git a/locales/it/README.md b/locales/it/README.md
index 3d04b2bfe6..8d8d2596a7 100644
--- a/locales/it/README.md
+++ b/locales/it/README.md
@@ -1,7 +1,7 @@
+### 1. 행동 강령
-## 작업할 내용 결정하기
+모든 기여자는 [행동 강령](./CODE_OF_CONDUCT.md)을 준수해야 합니다.
-첫 기여를 위한 좋은 시작점을 찾고 계신가요? 우리의 [Roo Code 이슈](https://github.com/orgs/RooVetGit/projects/1) Github 프로젝트의 "Issue [Unassigned]" 섹션에서 이슈를 확인하세요. 이러한 이슈들은 새로운 기여자와 우리가 도움을 필요로 하는 영역을 위해 특별히 선별되었습니다!
+### 2. 프로젝트 로드맵
-우리는 [문서](https://docs.roocode.com/)에 대한 기여도 환영합니다! 오타 수정, 기존 가이드 개선 또는 새로운 교육 콘텐츠 생성 등 - 모든 사람이 Roo Code를 최대한 활용할 수 있도록 도와주는 커뮤니티 기반 리소스 저장소를 구축하고 싶습니다. 모든
-페이지에서 "Edit this page"를 클릭하여 파일을 편집할 수 있는 Github의 적절한 위치로 빠르게 이동하거나, https://github.com/RooVetGit/Roo-Code-Docs에 직접 접근할 수 있습니다.
+로드맵은 프로젝트 방향을 안내합니다. 기여를 다음 핵심 목표에 맞추세요:
-더 큰 기능 작업을 계획하고 있다면, Roo Code의 비전과 일치하는지 논의할 수 있도록 먼저 [기능 요청](https://github.com/RooVetGit/Roo-Code/discussions/categories/feature-requests?discussions_q=is%3Aopen+category%3A%22Feature+Requests%22+sort%3Atop)을 생성해주세요. 또한 아이디어가 우리의 전략적 방향과 일치하는지 확인하기 위해 아래의 [프로젝트 로드맵](#프로젝트-로드맵)을 확인할 수도 있습니다.
+### 신뢰성 우선
-## 프로젝트 로드맵
+- diff 편집과 명령 실행의 일관된 신뢰성 보장
+- 정기적 사용을 방해하는 마찰점 감소
+- 모든 언어 환경과 플랫폼에서의 원활한 작동 보장
+- 다양한 AI 제공업체 및 모델에 대한 강력한 지원 확대
-Roo Code는 우리의 우선순위와 미래 방향을 안내하는 명확한 개발 로드맵을 가지고 있습니다. 우리의 로드맵을 이해하면 다음과 같은 도움을 받을 수 있습니다:
+### 향상된 사용자 경험
-- 프로젝트 목표에 맞게 기여 조정
-- 당신의 전문 지식이 가장 가치 있는 영역 식별
-- 특정 디자인 결정 배경 이해
-- 우리의 비전을 지원하는 새로운 기능에 대한 영감 찾기
+- 명확성과 직관성을 위한 UI/UX 간소화
+- 개발자들이 일상적으로 사용하는 도구에 기대하는 높은 기준을 충족하기 위한 지속적인 워크플로우 개선
-현재 로드맵은 여섯 가지 주요 기둥에 초점을 맞추고 있습니다:
+### 에이전트 성능 선도
-### 제공업체 지원
+- 실제 생산성을 측정하는 포괄적인 평가 기준(evals) 수립
+- 누구나 이러한 평가를 쉽게 실행하고 해석할 수 있도록 지원
+- 평가 점수의 명확한 향상을 보여주는 개선 제공
-가능한 한 많은 제공업체를 지원하는 것을 목표로 합니다:
+PR에서 이러한 영역과의 연관성을 언급하세요.
-- 더 다재다능한 "OpenAI 호환" 지원
-- xAI, Microsoft Azure AI, Alibaba Cloud Qwen, IBM Watsonx, Together AI, DeepInfra, Fireworks AI, Cohere, Perplexity AI, FriendliAI, Replicate
-- Ollama와 LM Studio에 대한 향상된 지원
+### 3. Roo Code 커뮤니티 참여
-### 모델 지원
+- **주요 방법:** [Discord](https://discord.gg/roocode)에 가입하고 **Hannes Rudolph (`hrudolph`)**에게 DM을 보내세요.
+- **대안:** 경험 많은 기여자는 [GitHub Projects](https://github.com/orgs/RooCodeInc/projects/1)를 통해 직접 참여할 수 있습니다.
-로컬 모델을 포함하여 가능한 한 많은 모델에서 Roo가 잘 작동하기를 원합니다:
+## 기여 내용 찾기 및 계획 세우기
-- 사용자 정의 시스템 프롬프팅 및 워크플로우를 통한 로컬 모델 지원
-- 벤치마킹 평가 및 테스트 케이스
+### 기여 유형
-### 시스템 지원
+- **버그 수정:** 코드 문제 해결.
+- **새 기능:** 기능 추가.
+- **문서화:** 가이드 개선 및 명확성 향상.
-Roo가 모든 사람의 컴퓨터에서 잘 작동하기를 원합니다:
+### Issue-First 접근법
-- 크로스 플랫폼 터미널 통합
-- Mac, Windows 및 Linux에 대한 강력하고 일관된 지원
+모든 기여는 GitHub Issue에서 시작해야 합니다.
-### 문서화
+- **기존 Issue 확인:** [GitHub Issues](https://github.com/RooCodeInc/Roo-Code/issues)를 검색하세요.
+- **Issue 생성:** 적절한 템플릿 사용:
+ - **버그:** "Bug Report" 템플릿.
+ - **기능:** "Detailed Feature Proposal" 템플릿. 시작 전 승인 필요.
+- **Issue 담당:** 댓글을 달고 공식 할당을 기다리세요.
-모든 사용자와 기여자를 위한 포괄적이고 접근 가능한 문서를 원합니다:
+**승인된 Issue 없는 PR은 닫힐 수 있습니다.**
-- 확장된 사용자 가이드 및 튜토리얼
-- 명확한 API 문서
-- 기여자를 위한 더 나은 가이드
-- 다국어 문서 리소스
-- 대화형 예제 및 코드 샘플
+### 작업 선택하기
-### 안정성
+- 할당되지 않은 "Good First Issues"를 [GitHub 프로젝트](https://github.com/orgs/RooCodeInc/projects/1)에서 확인하세요.
+- 문서 관련은 [Roo Code Docs](https://github.com/RooCodeInc/Roo-Code-Docs)를 참조하세요.
-버그 수를 크게 줄이고 자동화된 테스트를 증가시키고자 합니다:
+### 버그 신고
-- 디버그 로깅 스위치
-- 버그/지원 요청과 함께 보낼 수 있는 "기기/작업 정보" 복사 버튼
+- 먼저 기존 신고를 확인하세요.
+- ["Bug Report" 템플릿](https://github.com/RooCodeInc/Roo-Code/issues/new/choose)을 사용하여 새 버그를 신고하세요.
+- **보안 문제:** [security advisories](https://github.com/RooCodeInc/Roo-Code/security/advisories/new)를 통해 비공개로 신고하세요.
-### 국제화
+## 개발 및 제출 프로세스
-Roo가 모든 사람의 언어를 말하기를 원합니다:
+### 개발 환경 설정
-- 我们希望 Roo Code 说每个人的语言
-- Queremos que Roo Code hable el idioma de todos
-- हम चाहते हैं कि Roo Code हर किसी की भाषा बोले
-- نريد أن يتحدث Roo Code لغة الجميع
+1. **Fork & Clone:**
-우리는 특히 로드맵 목표를 발전시키는 기여를 환영합니다. 이러한 기둥에 맞는 작업을 하고 있다면, PR 설명에서 이를 언급해 주세요.
-
-## 개발 설정
-
-1. 저장소 **클론**:
-
-```sh
-git clone https://github.com/RooVetGit/Roo-Code.git
```
-
-2. **의존성 설치**:
-
-```sh
-npm run install:all
-```
-
-3. **웹뷰 시작(HMR이 있는 Vite/React 앱)**:
-
-```sh
-npm run dev
+git clone https://github.com/당신의_아이디/Roo-Code.git
```
-4. **디버깅**:
- VSCode에서 `F5`를 누르거나(**실행** → **디버깅 시작**) Roo Code가 로드된 새 세션을 엽니다.
+2. **의존성 설치:**
-웹뷰의 변경 사항은 즉시 나타납니다. 코어 확장에 대한 변경 사항은 확장 호스트를 다시 시작해야 합니다.
-
-또는 .vsix를 빌드하고 VSCode에 직접 설치할 수 있습니다:
-
-```sh
-npm run build
```
-
-`bin/` 디렉토리에 `.vsix` 파일이 나타나며 다음 명령으로 설치할 수 있습니다:
-
-```sh
-code --install-extension bin/roo-cline-.vsix
+npm run install:all
```
-## 코드 작성 및 제출
-
-누구나 Roo Code에 코드를 기여할 수 있지만, 기여가 원활하게 통합될 수 있도록 다음 지침을 따라주시기 바랍니다:
-
-1. **Pull Request 집중**
-
- - PR을 단일 기능 또는 버그 수정으로 제한
- - 더 큰 변경사항을 더 작고 관련된 PR로 분할
- - 독립적으로 검토할 수 있는 논리적인 커밋으로 변경사항 분할
-
-2. **코드 품질**
+3. **디버깅:** VS Code에서 `F5`를 눌러 실행하세요.
- - 모든 PR은 린팅 및 포맷팅을 포함한 CI 검사를 통과해야 함
- - 제출하기 전에 모든 ESLint 경고나 오류 해결
- - Ellipsis, 자동화된 코드 리뷰 도구의 모든 피드백에 응답
- - TypeScript 모범 사례를 따르고 타입 안전성 유지
+### 코드 작성 가이드라인
-3. **테스팅**
+- 하나의 기능 또는 수정당 하나의 집중된 PR.
+- ESLint와 TypeScript 모범 사례를 따르세요.
+- Issue를 참조하는 명확한 커밋 메시지를 작성하세요(예: `Fixes #123`).
+- 철저한 테스트를 제공하세요(`npm test`).
+- 제출 전 최신 `main` 브랜치에 리베이스하세요.
- - 새로운 기능에 대한 테스트 추가
- - 모든 테스트가 통과하는지 확인하기 위해 `npm test` 실행
- - 변경사항이 영향을 미치는 경우 기존 테스트 업데이트
- - 적절한 경우 단위 테스트와 통합 테스트 모두 포함
+### Pull Request 제출
-4. **커밋 가이드라인**
+- 초기 피드백을 원한다면 **드래프트 PR**로 시작하세요.
+- Pull Request 템플릿에 따라 변경 사항을 명확히 설명하세요.
+- UI 변경에 대한 스크린샷/동영상을 제공하세요.
+- 문서 업데이트가 필요한지 표시하세요.
- - 명확하고 설명적인 커밋 메시지 작성
- - #이슈-번호를 사용하여 커밋에서 관련 이슈 참조
+### Pull Request 정책
-5. **제출 전**
+- 사전 승인 및 할당된 Issue를 참조해야 합니다.
+- 정책을 준수하지 않는 PR은 닫힐 수 있습니다.
+- PR은 CI 테스트를 통과하고, 로드맵에 부합하며, 명확한 문서를 갖추어야 합니다.
- - 최신 main에 브랜치 리베이스
- - 브랜치가 성공적으로 빌드되는지 확인
- - 모든 테스트가 통과하는지 다시 확인
- - 디버깅 코드나 콘솔 로그가 있는지 변경사항 검토
+### 리뷰 프로세스
-6. **Pull Request 설명**
- - 변경사항이 무엇을 하는지 명확하게 설명
- - 변경사항을 테스트하는 단계 포함
- - 모든 주요 변경사항 나열
- - UI 변경사항에 대한 스크린샷 추가
+- **일일 분류:** 메인테이너의 빠른 검토.
+- **주간 심층 리뷰:** 종합적인 평가.
+- **피드백에 따라 신속히 반복**하세요.
-## 기여 동의
+## 법적 안내
-Pull request를 제출함으로써, 귀하의 기여는 프로젝트와 동일한 라이선스([Apache 2.0](../LICENSE))에 따라 라이선스가 부여된다는 데 동의합니다.
+기여함으로써, 귀하의 기여가 Roo Code의 라이선스와 일치하는 Apache 2.0 라이선스 하에 제공됨에 동의합니다.
diff --git a/locales/ko/README.md b/locales/ko/README.md
index 33a286e3b1..82a215f62c 100644
--- a/locales/ko/README.md
+++ b/locales/ko/README.md
@@ -1,7 +1,7 @@
-
+
@@ -43,17 +43,17 @@
유연한 코딩 파트너, 시스템 아키텍트, QA 엔지니어나 제품 관리자와 같은 전문화된 역할을 찾고 있든, Roo Code는 더 효율적으로 소프트웨어를 구축하는 데 도움이 될 수 있습니다.
-상세한 업데이트 및 수정 사항은 [CHANGELOG](../CHANGELOG.md)를 확인하세요.
+상세한 업데이트 및 수정 사항은 [CHANGELOG](../../CHANGELOG.md)를 확인하세요.
---
-## 🎉 Roo Code 3.14 출시
+## 🎉 Roo Code 3.17 출시
-Roo Code 3.14가 사용자 피드백을 바탕으로 새로운 기능과 개선 사항을 제공합니다!
+Roo Code 3.17이 사용자 피드백을 바탕으로 강력한 새로운 기능과 개선 사항을 제공합니다!
-- **프롬프트 캐싱** - `gemini-2.5-pro-preview-03-25`에서 이제 Gemini 제공자에서 프롬프트 캐싱을 지원합니다 (Vertex 및 OpenRouter 지원 예정).
-- **개선된 편집 도구** - `search_and_replace`와 `insert_content` 도구가 개선되어 실험 상태에서 정식 기능으로 전환되었습니다.
-- **수많은 기타 개선사항** - 확장 프로그램 전체에 걸친 다양한 수정 및 향상된 기능.
+- **Gemini용 암시적 캐싱** - Gemini API 호출이 이제 자동으로 캐시되어 API 비용이 절감됩니다.
+- **더 스마트한 모드 선택** - 모드 정의에 각 모드가 언제 사용되어야 하는지에 대한 지침을 포함할 수 있어 더 나은 오케스트레이션이 가능해졌습니다.
+- **지능형 컨텍스트 응축** - 컨텍스트가 가득 찼을 때 잘라내는 대신 대화 기록을 지능적으로 요약합니다(설정 -> 실험적 기능에서 활성화).
---
@@ -116,7 +116,7 @@ MCP는 무제한 커스텀 도구를 추가할 수 있게 하여 Roo Code의 기
- **Discord:** 실시간 도움과 토론을 위한 [Discord 서버 참여](https://discord.gg/roocode)
- **Reddit:** 경험과 팁을 공유하는 [서브레딧 방문](https://www.reddit.com/r/RooCode)
-- **GitHub:** [문제 보고](https://github.com/RooVetGit/Roo-Code/issues) 또는 [기능 요청](https://github.com/RooVetGit/Roo-Code/discussions/categories/feature-requests?discussions_q=is%3Aopen+category%3A%22Feature+Requests%22+sort%3Atop)
+- **GitHub:** [문제 보고](https://github.com/RooCodeInc/Roo-Code/issues) 또는 [기능 요청](https://github.com/RooCodeInc/Roo-Code/discussions/categories/feature-requests?discussions_q=is%3Aopen+category%3A%22Feature+Requests%22+sort%3Atop)
---
@@ -125,7 +125,7 @@ MCP는 무제한 커스텀 도구를 추가할 수 있게 하여 Roo Code의 기
1. 저장소 **클론**:
```sh
-git clone https://github.com/RooVetGit/Roo-Code.git
+git clone https://github.com/RooCodeInc/Roo-Code.git
```
2. **의존성 설치**:
@@ -178,31 +178,36 @@ code --install-extension bin/roo-cline-.vsix
Roo Code를 더 좋게 만드는 데 도움을 준 모든 기여자에게 감사드립니다!
-| mrubens| saoudrizwan| cte| samhvw8| daniel-lxs| a8trejo|
-|:---:|:---:|:---:|:---:|:---:|:---:|
-| ColemanRoo| stea9499| joemanley201| System233| hannesrudolph| jquanton|
-| nissa-seru| KJ7LNW| NyxJae| MuriloFP| d-oit| punkpeye|
-| Smartsheet-JB-Brown| monotykamary| wkordalski| feifei325| cannuri| lloydchang|
-| vigneshsubbiah16| qdaxb| Szpadel| Premshay| psv2522| diarmidmackenzie|
-| lupuletic| elianiva| olweraltuve| sachasayan| afshawnlotfi| pugazhendhi-m|
-| aheizi| RaySinner| PeterDaveHello| nbihan-mediware| dtrugman| emshvac|
-| kyle-apex| pdecat| zhangtony239| Lunchb0ne| arthurauffray| upamune|
-| StevenTCramer| sammcj| p12tic| gtaylor| aitoroses| yt3trees|
-| franekp| yongjer| vincentsong| vagadiya| teddyOOXX| eonghk|
-| taisukeoe| heyseth| ross| philfung| napter| mdp|
-| SplittyDev| Chenjiayuan195| jcbdev| GitlyHallows| bramburn| benzntech|
-| axkirillov| anton-otee| shoopapa| jwcraig| kinandan| kohii|
-| lightrabbit| olup| mecab| nevermorec| im47cn| hongzio|
-| dqroid| dairui1| bannzai| axmo| asychin| ashktn|
-| eltociear| PretzelVector| cdlliuy| student20880| shohei-ihaya| shaybc|
-| shariqriazz| seedlord| samir-nimbly| ronyblum| refactorthis| pokutuna|
-| philipnext| oprstchn| nobu007| mosleyit| moqimoqidea| mlopezr|
-| hesara| DeXtroTip| celestial-vault| linegel| snoyiatk| dbasclpy|
-| dleen| chadgauth| bogdan0083| Atlogit| atlasgong| andreastempsch|
-| QuinsZouls| alarno| adamwlarson| AMHesch| amittell| Yoshino-Yukitaro|
-| Yikai-Liao| vladstudio| NamesMT| tmsjngx0| tgfjt| maekawataiki|
-| samsilveira| 01Rian| Sarke| kvokka| marvijo-code| mamertofabian|
-| libertyteeth| shtse8| Jdo300| | | |
+
+| mrubens | saoudrizwan | cte | samhvw8 | daniel-lxs | a8trejo |
+| :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |
+| ColemanRoo | hannesrudolph | KJ7LNW | stea9499 | joemanley201 | System233 |
+| nissa-seru | jquanton | NyxJae | MuriloFP | d-oit | punkpeye |
+| wkordalski | Smartsheet-JB-Brown | monotykamary | elianiva | cannuri | feifei325 |
+| zhangtony239 | sachasayan | lloydchang | vigneshsubbiah16 | Szpadel | qdaxb |
+| lupuletic | Premshay | psv2522 | diarmidmackenzie | aheizi | olweraltuve |
+| jr | dtrugman | nbihan-mediware | PeterDaveHello | RaySinner | pugazhendhi-m |
+| afshawnlotfi | shariqriazz | pdecat | kyle-apex | emshvac | Lunchb0ne |
+| xyOz-dev | arthurauffray | upamune | StevenTCramer | sammcj | p12tic |
+| gtaylor | aitoroses | benzntech | ross | heyseth | taisukeoe |
+| dlab-anton | eonghk | teddyOOXX | vagadiya | vincentsong | yongjer |
+| ashktn | franekp | yt3trees | anton-otee | axkirillov | bramburn |
+| snoyiatk | GitlyHallows | jcbdev | Chenjiayuan195 | julionav | SplittyDev |
+| mdp | napter | philfung | im47cn | shoopapa | jwcraig |
+| kinandan | kohii | lightrabbit | olup | mecab | nevermorec |
+| hongzio | GOODBOY008 | dqroid | dairui1 | bannzai | axmo |
+| asychin | amittell | Yoshino-Yukitaro | Yikai-Liao | SmartManoj | PretzelVector |
+| zetaloop | cdlliuy | student20880 | shohei-ihaya | shaybc | seedlord |
+| samir-nimbly | ronyblum | robertheadley | refactorthis | pokutuna | philipnext |
+| oprstchn | nobu007 | mosleyit | moqimoqidea | mlopezr | zxdvd |
+| DeXtroTip | pfitz | celestial-vault | linegel | dbasclpy | Deon588 |
+| dleen | chadgauth | olearycrew | bogdan0083 | Atlogit | atlasgong |
+| andreastempsch | alasano | QuinsZouls | HadesArchitect | alarno | adamwlarson |
+| AMHesch | vladstudio | NamesMT | tmsjngx0 | tgfjt | maekawataiki |
+| samsilveira | mr-ryan-james | 01Rian | Sarke | kvokka | ecmasx |
+| marvijo-code | mamertofabian | monkeyDluffy6017 | libertyteeth | shtse8 | ksze |
+| Jdo300 | hesara | | | | |
+
## 라이선스
diff --git a/locales/nl/CODE_OF_CONDUCT.md b/locales/nl/CODE_OF_CONDUCT.md
new file mode 100644
index 0000000000..2c9ba7d3f5
--- /dev/null
+++ b/locales/nl/CODE_OF_CONDUCT.md
@@ -0,0 +1,52 @@
+[English](../../CODE_OF_CONDUCT.md) • [Català](../ca/CODE_OF_CONDUCT.md) • [Deutsch](../de/CODE_OF_CONDUCT.md) • [Español](../es/CODE_OF_CONDUCT.md) • [Français](../fr/CODE_OF_CONDUCT.md) • [हिंदी](../hi/CODE_OF_CONDUCT.md) • [Italiano](../it/CODE_OF_CONDUCT.md) • Nederlands • [Русский](../ru/CODE_OF_CONDUCT.md)
+
+[日本語](../ja/CODE_OF_CONDUCT.md) • [한국어](../ko/CODE_OF_CONDUCT.md) • [Polski](../pl/CODE_OF_CONDUCT.md) • [Português (BR)](../pt-BR/CODE_OF_CONDUCT.md) • [Türkçe](../tr/CODE_OF_CONDUCT.md) • [Tiếng Việt](../vi/CODE_OF_CONDUCT.md) • [简体中文](../zh-CN/CODE_OF_CONDUCT.md) • [繁體中文](../zh-TW/CODE_OF_CONDUCT.md)
+
+# Contributor Covenant Gedragscode
+
+## Onze Belofte
+
+In het belang van het bevorderen van een open en gastvrije omgeving, beloven wij als bijdragers en beheerders om deelname aan ons project en onze community een ervaring zonder intimidatie te maken voor iedereen, ongeacht leeftijd, lichaamsgrootte, handicap, etniciteit, geslachtskenmerken, genderidentiteit en -expressie, ervaringsniveau, opleiding, sociaal-economische status, nationaliteit, uiterlijk, ras, religie of seksuele identiteit en oriëntatie.
+
+## Onze Standaarden
+
+Voorbeelden van gedrag dat bijdraagt aan het creëren van een positieve omgeving zijn onder andere:
+
+- Gebruik van gastvrije en inclusieve taal
+- Respect tonen voor verschillende standpunten en ervaringen
+- Constructieve kritiek gracieus accepteren
+- Focus op wat het beste is voor de community
+- Empathie tonen voor andere communityleden
+
+Voorbeelden van onacceptabel gedrag van deelnemers zijn onder andere:
+
+- Het gebruik van seksueel getinte taal of beelden en ongewenste seksuele aandacht of toenadering
+- Trollgedrag, beledigende/denigrerende opmerkingen en persoonlijke of politieke aanvallen
+- Openbare of privé-intimidatie
+- Het publiceren van andermans privé-informatie, zoals een fysiek of elektronisch adres, zonder uitdrukkelijke toestemming
+- Ander gedrag dat redelijkerwijs als ongepast kan worden beschouwd in een professionele omgeving
+
+## Onze Verantwoordelijkheden
+
+Projectbeheerders zijn verantwoordelijk voor het verduidelijken van de normen voor acceptabel gedrag en worden geacht passende en eerlijke corrigerende maatregelen te nemen in reactie op gevallen van onacceptabel gedrag.
+
+Projectbeheerders hebben het recht en de verantwoordelijkheid om opmerkingen, commits, code, wiki-bewerkingen, issues en andere bijdragen die niet in overeenstemming zijn met deze Gedragscode te verwijderen, te bewerken of te weigeren, of om elke bijdrager tijdelijk of permanent te verbannen voor ander gedrag dat zij ongepast, bedreigend, beledigend of schadelijk achten.
+
+## Toepassingsgebied
+
+Deze Gedragscode is van toepassing binnen projectruimtes en in openbare ruimtes wanneer een individu het project of de community vertegenwoordigt. Voorbeelden van vertegenwoordiging van een project of community zijn het gebruik van een officieel project-e-mailadres, posten via een officieel socialmedia-account of optreden als een aangestelde vertegenwoordiger op een online of offline evenement. De definitie van vertegenwoordiging van een project kan verder worden gespecificeerd door projectbeheerders.
+
+## Handhaving
+
+Voorvallen van beledigend, intimiderend of anderszins onacceptabel gedrag kunnen worden gemeld door contact op te nemen met het projectteam via support@roocode.com. Alle klachten zullen worden beoordeeld en onderzocht en zullen resulteren in een reactie die passend wordt geacht voor de omstandigheden. Het projectteam is verplicht vertrouwelijkheid te bewaren met betrekking tot de melder van het incident. Verdere details over specifiek handhavingsbeleid kunnen afzonderlijk worden gepubliceerd.
+
+Projectbeheerders die de Gedragscode niet te goeder trouw volgen of handhaven, kunnen tijdelijke of permanente gevolgen ondervinden zoals bepaald door andere leden van het leiderschap van het project.
+
+## Toeschrijving
+
+Deze Gedragscode is gebaseerd op [Cline's versie][cline_coc] van de [Contributor Covenant][homepage], versie 1.4, beschikbaar op https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
+
+[cline_coc]: https://github.com/cline/cline/blob/main/CODE_OF_CONDUCT.md
+[homepage]: https://www.contributor-covenant.org
+
+Voor antwoorden op veelgestelde vragen over deze gedragscode, zie https://www.contributor-covenant.org/faq
diff --git a/locales/nl/CONTRIBUTING.md b/locales/nl/CONTRIBUTING.md
new file mode 100644
index 0000000000..7665efe9f9
--- /dev/null
+++ b/locales/nl/CONTRIBUTING.md
@@ -0,0 +1,129 @@
+[English](../../CONTRIBUTING.md) • [Català](../ca/CONTRIBUTING.md) • [Deutsch](../de/CONTRIBUTING.md) • [Español](../es/CONTRIBUTING.md) • [Français](../fr/CONTRIBUTING.md) • [हिंदी](../hi/CONTRIBUTING.md) • [Italiano](../it/CONTRIBUTING.md) • Nederlands • [Русский](../ru/CONTRIBUTING.md)
+
+[日本語](../ja/CONTRIBUTING.md) • [한국어](../ko/CONTRIBUTING.md) • [Polski](../pl/CONTRIBUTING.md) • [Português (BR)](../pt-BR/CONTRIBUTING.md) • [Türkçe](../tr/CONTRIBUTING.md) • [Tiếng Việt](../vi/CONTRIBUTING.md) • [简体中文](../zh-CN/CONTRIBUTING.md) • [繁體中文](../zh-TW/CONTRIBUTING.md)
+
+# Bijdragen aan Roo Code
+
+Roo Code is een door de community gedreven project en we waarderen elke bijdrage enorm. Om de samenwerking te stroomlijnen, werken we volgens een [Issue-First](#issue-first-aanpak) principe, wat betekent dat alle [Pull Requests (PR's)](#een-pull-request-indienen) eerst gekoppeld moeten worden aan een GitHub Issue. Lees deze gids zorgvuldig door.
+
+## Inhoudsopgave
+
+- [Voordat je bijdraagt](#voordat-je-bijdraagt)
+- [Je bijdrage vinden & plannen](#je-bijdrage-vinden--plannen)
+- [Ontwikkelings- & indieningsproces](#ontwikkelings--indieningsproces)
+- [Juridisch](#juridisch)
+
+## Voordat je bijdraagt
+
+### 1. Gedragscode
+
+Alle bijdragers moeten zich houden aan onze [Gedragscode](./CODE_OF_CONDUCT.md).
+
+### 2. De project-roadmap
+
+Onze roadmap bepaalt de richting van het project. Stem je bijdragen af op deze kernpunten:
+
+### Betrouwbaarheid eerst
+
+- Zorgen dat diff-bewerking en opdrachtuitvoering consistent betrouwbaar zijn
+- Verminderen van wrijvingspunten die regelmatig gebruik ontmoedigen
+- Garanderen van soepele werking in alle talen en op alle platforms
+- Uitbreiden van robuuste ondersteuning voor een breed scala aan AI-providers en -modellen
+
+### Verbeterde gebruikerservaring
+
+- Vereenvoudigen van de gebruikersinterface voor meer duidelijkheid en intuïtiviteit
+- Continu verbeteren van de workflow om te voldoen aan de hoge verwachtingen van ontwikkelaars
+
+### Voorop lopen in agent-prestaties
+
+- Opstellen van uitgebreide evaluatiebenchmarks (evals) om productiviteit in de echte wereld te meten
+- Het voor iedereen gemakkelijk maken om deze evaluaties uit te voeren en te interpreteren
+- Verbeteringen leveren die duidelijke stijgingen in evaluatiescores aantonen
+
+Vermeld de afstemming met deze gebieden in je PR's.
+
+### 3. Word lid van de Roo Code-community
+
+- **Hoofdmethode:** Word lid van onze [Discord](https://discord.gg/roocode) en stuur een DM naar **Hannes Rudolph (`hrudolph`)**.
+- **Alternatief:** Ervaren bijdragers kunnen direct meedoen via [GitHub Projects](https://github.com/orgs/RooCodeInc/projects/1).
+
+## Je bijdrage vinden & plannen
+
+### Soorten bijdragen
+
+- **Bugfixes:** Problemen in code oplossen.
+- **Nieuwe functies:** Functionaliteit toevoegen.
+- **Documentatie:** Handleidingen verbeteren en verduidelijken.
+
+### Issue-First-aanpak
+
+Elke bijdrage moet beginnen met een GitHub Issue.
+
+- **Bestaande issues controleren:** Zoek in [GitHub Issues](https://github.com/RooCodeInc/Roo-Code/issues).
+- **Issue aanmaken:** Gebruik de juiste templates:
+ - **Bugs:** "Bug Report"-template.
+ - **Functies:** "Detailed Feature Proposal"-template. Goedkeuring vereist voor je begint.
+- **Issues claimen:** Reageer en wacht op officiële toewijzing.
+
+**PR's zonder goedgekeurde issues kunnen worden gesloten.**
+
+### Bepalen waar je aan werkt
+
+- Bekijk het [GitHub Project](https://github.com/orgs/RooCodeInc/projects/1) voor niet-toegewezen "Good First Issues".
+- Voor documentatie, bezoek [Roo Code Docs](https://github.com/RooCodeInc/Roo-Code-Docs).
+
+### Bugs of problemen melden
+
+- Controleer eerst of er al meldingen zijn.
+- Maak nieuwe bugmeldingen met de ["Bug Report"-template](https://github.com/RooCodeInc/Roo-Code/issues/new/choose).
+- **Beveiligingsproblemen:** Meld privé via [security advisories](https://github.com/RooCodeInc/Roo-Code/security/advisories/new).
+
+## Ontwikkelings- & indieningsproces
+
+### Ontwikkelomgeving instellen
+
+1. **Fork & Clone:**
+
+```
+git clone https://github.com/JOUW_GEBRUIKERSNAAM/Roo-Code.git
+```
+
+2. **Installeer afhankelijkheden:**
+
+```
+npm run install:all
+```
+
+3. **Debuggen:** Open met VS Code (`F5`).
+
+### Richtlijnen voor het schrijven van code
+
+- Eén gerichte PR per functie of fix.
+- Volg ESLint en TypeScript best practices.
+- Schrijf duidelijke, beschrijvende commits die verwijzen naar issues (bijv. `Fixes #123`).
+- Zorg voor grondige tests (`npm test`).
+- Rebase op de nieuwste `main`-branch vóór indiening.
+
+### Een Pull Request indienen
+
+- Begin als **concept-PR** als je vroege feedback zoekt.
+- Beschrijf je wijzigingen duidelijk volgens de Pull Request Template.
+- Voeg screenshots/video's toe voor UI-wijzigingen.
+- Geef aan of documentatie-updates nodig zijn.
+
+### Pull Request beleid
+
+- Moet verwijzen naar vooraf goedgekeurde en toegewezen issues.
+- PR's die niet aan het beleid voldoen, kunnen worden gesloten.
+- PR's moeten CI-tests doorstaan, aansluiten bij de roadmap en duidelijke documentatie hebben.
+
+### Reviewproces
+
+- **Dagelijkse triage:** Snelle controles door maintainers.
+- **Wekelijkse diepgaande review:** Uitgebreide beoordeling.
+- **Snel itereren** op basis van feedback.
+
+## Juridisch
+
+Door een pull request in te dienen, ga je ermee akkoord dat je bijdragen worden gelicenseerd onder de Apache 2.0-licentie, in overeenstemming met de licentie van Roo Code.
diff --git a/locales/nl/README.md b/locales/nl/README.md
new file mode 100644
index 0000000000..67e403fd64
--- /dev/null
+++ b/locales/nl/README.md
@@ -0,0 +1,220 @@
+
+### 1. Kodeks postępowania
-## Decydowanie nad czym pracować
+Wszyscy współtwórcy muszą przestrzegać naszego [Kodeksu postępowania](./CODE_OF_CONDUCT.md).
-Szukasz dobrego pierwszego wkładu? Sprawdź problemy w sekcji "Issue [Unassigned]" naszego [projektu Github Roo Code](https://github.com/orgs/RooVetGit/projects/1). Te zostały specjalnie wybrane dla nowych współtwórców i obszarów, gdzie chętnie przyjmiemy pomoc!
+### 2. Roadmapa projektu
-Cieszymy się również z wkładu do naszej [dokumentacji](https://docs.roocode.com/)! Czy to poprawianie literówek, ulepszanie istniejących przewodników, czy tworzenie nowych treści edukacyjnych - chcielibyśmy zbudować repozytorium zasobów napędzane przez społeczność, które pomaga każdemu czerpać maksimum z Roo Code. Możesz kliknąć "Edit this page" na dowolnej stronie, aby szybko przejść do odpowiedniego miejsca w Github, aby edytować plik, lub możesz przejść bezpośrednio do https://github.com/RooVetGit/Roo-Code-Docs.
+Nasza roadmapa wyznacza kierunek projektu. Dostosuj swój wkład do tych kluczowych celów:
-Jeśli planujesz pracować nad większą funkcją, proszę najpierw utwórz [prośbę o funkcję](https://github.com/RooVetGit/Roo-Code/discussions/categories/feature-requests?discussions_q=is%3Aopen+category%3A%22Feature+Requests%22+sort%3Atop), abyśmy mogli przedyskutować, czy jest ona zgodna z wizją Roo Code. Możesz również sprawdzić naszą [Mapę Drogową Projektu](#mapa-drogowa-projektu) poniżej, aby zobaczyć, czy Twój pomysł pasuje do naszego strategicznego kierunku.
+### Niezawodność przede wszystkim
-## Mapa Drogowa Projektu
+- Zapewnienie, że edycja różnic i wykonywanie poleceń są konsekwentnie niezawodne
+- Zmniejszenie punktów tarcia, które zniechęcają do regularnego użytkowania
+- Gwarancja płynnego działania we wszystkich językach i na wszystkich platformach
+- Rozszerzenie solidnego wsparcia dla szerokiej gamy dostawców i modeli AI
-Roo Code posiada jasną mapę drogową rozwoju, która kieruje naszymi priorytetami i przyszłym kierunkiem. Zrozumienie naszej mapy drogowej może pomóc Ci:
+### Ulepszone doświadczenie użytkownika
-- Dostosować swoje wkłady do celów projektu
-- Zidentyfikować obszary, w których Twoja wiedza byłaby najbardziej wartościowa
-- Zrozumieć kontekst stojący za pewnymi decyzjami projektowymi
-- Znaleźć inspirację dla nowych funkcji, które wspierają naszą wizję
+- Uproszczenie interfejsu użytkownika dla większej przejrzystości i intuicyjności
+- Ciągłe doskonalenie przepływu pracy, aby spełnić wysokie oczekiwania programistów
-Nasza obecna mapa drogowa koncentruje się na sześciu kluczowych filarach:
+### Wiodąca pozycja w wydajności agentów
-### Wsparcie dla Dostawców
+- Ustanowienie kompleksowych punktów odniesienia (evals) do mierzenia produktywności w rzeczywistym świecie
+- Ułatwienie wszystkim łatwego uruchamiania i interpretowania tych ocen
+- Dostarczanie ulepszeń, które wykazują wyraźny wzrost wyników ocen
-Dążymy do wspierania jak największej liczby dostawców:
+Wspomnij o powiązaniu z tymi obszarami w swoich PR.
-- Bardziej wszechstronne wsparcie dla "OpenAI Compatible"
-- xAI, Microsoft Azure AI, Alibaba Cloud Qwen, IBM Watsonx, Together AI, DeepInfra, Fireworks AI, Cohere, Perplexity AI, FriendliAI, Replicate
-- Ulepszone wsparcie dla Ollama i LM Studio
+### 3. Dołącz do społeczności Roo Code
-### Wsparcie dla Modeli
+- **Główna metoda:** Dołącz do naszego [Discorda](https://discord.gg/roocode) i wyślij wiadomość prywatną do **Hannes Rudolph (`hrudolph`)**.
+- **Alternatywa:** Doświadczeni współtwórcy mogą angażować się bezpośrednio przez [GitHub Projects](https://github.com/orgs/RooCodeInc/projects/1).
-Chcemy, aby Roo działał jak najlepiej na jak największej liczbie modeli, w tym modeli lokalnych:
+## Znajdowanie i planowanie swojego wkładu
-- Wsparcie dla modeli lokalnych poprzez niestandardowe promptowanie systemowe i przepływy pracy
-- Benchmarki ewaluacyjne i przypadki testowe
+### Typy wkładów
-### Wsparcie dla Systemów
+- **Poprawki błędów:** Naprawianie problemów w kodzie.
+- **Nowe funkcje:** Dodawanie nowych funkcjonalności.
+- **Dokumentacja:** Ulepszanie przewodników i zwiększanie przejrzystości.
-Chcemy, aby Roo działał dobrze na komputerze każdego:
+### Podejście Issue-First
-- Integracja terminala międzyplatformowego
-- Silne i spójne wsparcie dla Mac, Windows i Linux
+Każdy wkład musi zaczynać się od GitHub Issue.
-### Dokumentacja
+- **Sprawdź istniejące issues:** Przeszukaj [GitHub Issues](https://github.com/RooCodeInc/Roo-Code/issues).
+- **Utwórz issue:** Używaj odpowiednich szablonów:
+ - **Błędy:** Szablon "Bug Report".
+ - **Funkcje:** Szablon "Detailed Feature Proposal". Wymagane zatwierdzenie przed rozpoczęciem.
+- **Zgłoś chęć pracy:** Skomentuj i poczekaj na oficjalne przypisanie.
-Chcemy kompleksowej, dostępnej dokumentacji dla wszystkich użytkowników i współtwórców:
+**PR bez zatwierdzonego issue może zostać zamknięty.**
-- Rozszerzone przewodniki użytkownika i tutoriale
-- Jasna dokumentacja API
-- Lepsze wskazówki dla współtwórców
-- Wielojęzyczne zasoby dokumentacji
-- Interaktywne przykłady i próbki kodu
+### Decydowanie, nad czym pracować
-### Stabilność
+- Sprawdź [Projekt GitHub](https://github.com/orgs/RooCodeInc/projects/1) w poszukiwaniu nieprzypisanych "Good First Issues".
+- W kwestii dokumentacji odwiedź [Roo Code Docs](https://github.com/RooCodeInc/Roo-Code-Docs).
-Chcemy znacznie zmniejszyć liczbę błędów i zwiększyć zautomatyzowane testowanie:
+### Zgłaszanie błędów
-- Przełącznik rejestrowania debugowania
-- Przycisk kopiowania "Informacji o Maszynie/Zadaniu" do wysyłania z prośbami o pomoc/zgłoszeniami błędów
+- Najpierw sprawdź istniejące zgłoszenia.
+- Twórz nowe zgłoszenia błędów używając [szablonu "Bug Report"](https://github.com/RooCodeInc/Roo-Code/issues/new/choose).
+- **Luki bezpieczeństwa:** Zgłaszaj prywatnie przez [security advisories](https://github.com/RooCodeInc/Roo-Code/security/advisories/new).
-### Internacjonalizacja
+## Proces rozwoju i zgłaszania
-Chcemy, aby Roo mówił językiem każdego:
+### Konfiguracja środowiska
-- 我们希望 Roo Code 说每个人的语言
-- Queremos que Roo Code hable el idioma de todos
-- हम चाहते हैं कि Roo Code हर किसी की भाषा बोले
-- نريد أن يتحدث Roo Code لغة الجميع
+1. **Fork & Clone:**
-Szczególnie witamy wkłady, które przyspieszają realizację celów naszej mapy drogowej. Jeśli pracujesz nad czymś, co jest zgodne z tymi filarami, proszę wspomnij o tym w opisie swojego PR.
-
-## Konfiguracja rozwojowa
-
-1. **Sklonuj** repozytorium:
-
-```sh
-git clone https://github.com/RooVetGit/Roo-Code.git
```
-
-2. **Zainstaluj zależności**:
-
-```sh
-npm run install:all
-```
-
-3. **Uruchom webview (aplikację Vite/React z HMR)**:
-
-```sh
-npm run dev
+git clone https://github.com/TWÓJ_UŻYTKOWNIK/Roo-Code.git
```
-4. **Debugowanie**:
- Naciśnij `F5` (lub **Uruchom** → **Rozpocznij debugowanie**) w VSCode, aby otworzyć nową sesję z załadowanym Roo Code.
+2. **Instalacja zależności:**
-Zmiany w webview pojawią się natychmiast. Zmiany w podstawowym rozszerzeniu będą wymagać ponownego uruchomienia hosta rozszerzenia.
-
-Alternatywnie możesz zbudować plik .vsix i zainstalować go bezpośrednio w VSCode:
-
-```sh
-npm run build
```
-
-Plik `.vsix` pojawi się w katalogu `bin/` i można go zainstalować za pomocą:
-
-```sh
-code --install-extension bin/roo-cline-.vsix
+npm run install:all
```
-## Pisanie i przesyłanie kodu
-
-Każdy może wnieść wkład w kod Roo Code, ale prosimy o przestrzeganie tych wytycznych, aby zapewnić płynną integrację Twoich wkładów:
-
-1. **Utrzymuj Pull Requesty skupione**
-
- - Ogranicz PR do jednej funkcji lub naprawy błędu
- - Podziel większe zmiany na mniejsze, powiązane PR
- - Podziel zmiany na logiczne commity, które można przeglądać niezależnie
-
-2. **Jakość kodu**
+3. **Debugowanie:** Otwórz w VS Code (`F5`).
- - Wszystkie PR muszą przejść kontrole CI, które obejmują zarówno linting, jak i formatowanie
- - Rozwiąż wszelkie ostrzeżenia lub błędy ESLint przed przesłaniem
- - Odpowiedz na wszystkie informacje zwrotne od Ellipsis, naszego zautomatyzowanego narzędzia do przeglądu kodu
- - Przestrzegaj najlepszych praktyk TypeScript i zachowaj bezpieczeństwo typów
+### Wytyczne dotyczące pisania kodu
-3. **Testowanie**
+- Jeden skoncentrowany PR na funkcję lub poprawkę.
+- Przestrzegaj dobrych praktyk ESLint i TypeScript.
+- Pisz jasne, opisowe commity odnoszące się do issues (np. `Fixes #123`).
+- Zapewnij dokładne testy (`npm test`).
+- Zrebase'uj na najnowszą gałąź `main` przed zgłoszeniem.
- - Dodaj testy dla nowych funkcji
- - Uruchom `npm test`, aby upewnić się, że wszystkie testy przechodzą
- - Zaktualizuj istniejące testy, jeśli Twoje zmiany na nie wpływają
- - Uwzględnij zarówno testy jednostkowe, jak i integracyjne, gdy jest to właściwe
+### Zgłaszanie Pull Requesta
-4. **Wytyczne dotyczące commitów**
+- Zacznij od **wersji roboczej PR**, jeśli szukasz wczesnego feedbacku.
+- Jasno opisz swoje zmiany, zgodnie z szablonem Pull Request.
+- Dostarcz zrzuty ekranu/wideo dla zmian UI.
+- Wskaż, czy potrzebne są aktualizacje dokumentacji.
- - Pisz jasne, opisowe komunikaty commitów
- - Odwołuj się do odpowiednich problemów w commitach, używając #numer-problemu
+### Polityka Pull Request
-5. **Przed przesłaniem**
+- Musi odnosić się do wcześniej zatwierdzonych i przypisanych issues.
+- PR niezgodne z polityką mogą zostać zamknięte.
+- PR powinny przechodzić testy CI, być zgodne z roadmapą i mieć jasną dokumentację.
- - Rebase swojej gałęzi na najnowszego maina
- - Upewnij się, że Twoja gałąź buduje się pomyślnie
- - Sprawdź ponownie, czy wszystkie testy przechodzą
- - Przejrzyj swoje zmiany pod kątem wszelkiego kodu debugującego lub logów konsoli
+### Proces recenzji
-6. **Opis Pull Requesta**
- - Jasno opisz, co robią Twoje zmiany
- - Dołącz kroki do przetestowania zmian
- - Wymień wszelkie istotne zmiany
- - Dodaj zrzuty ekranu dla zmian UI
+- **Codzienna selekcja:** Szybkie sprawdzenia przez maintainerów.
+- **Cotygodniowy dokładny przegląd:** Kompleksowa ocena.
+- **Szybko iteruj** na podstawie feedbacku.
-## Umowa o współpracy
+## Prawne
-Przesyłając pull request, zgadzasz się, że Twoje wkłady będą licencjonowane na tej samej licencji co projekt ([Apache 2.0](../LICENSE)).
+Zgłaszając pull request, zgadzasz się, że twój wkład będzie licencjonowany na licencji Apache 2.0, zgodnie z licencją Roo Code.
diff --git a/locales/pl/README.md b/locales/pl/README.md
index d6958df8cc..dd6044e44c 100644
--- a/locales/pl/README.md
+++ b/locales/pl/README.md
@@ -1,7 +1,7 @@
+# Contribuindo para o Roo Code
-## Decidindo no que Trabalhar
+O Roo Code é um projeto impulsionado pela comunidade e valorizamos muito cada contribuição. Para simplificar a colaboração, operamos com uma abordagem [Issue-First](#abordagem-issue-first), o que significa que todos os [Pull Requests (PRs)](#enviando-um-pull-request) devem primeiro estar vinculados a uma Issue do GitHub. Por favor, leia este guia com atenção.
-Procurando uma boa primeira contribuição? Verifique as issues na seção "Issue [Unassigned]" do nosso [Projeto Github Roo Code](https://github.com/orgs/RooVetGit/projects/1). Estas são especialmente selecionadas para novos colaboradores e áreas onde gostaríamos de ter alguma ajuda!
+## Índice
-Também damos as boas-vindas a contribuições para nossa [documentação](https://docs.roocode.com/)! Seja corrigindo erros de digitação, melhorando guias existentes ou criando novo conteúdo educacional - adoraríamos construir um repositório de recursos impulsionado pela comunidade que ajude todos a obter o máximo do Roo Code. Você pode clicar em "Edit this page" em qualquer página para ir rapidamente ao local certo no Github para editar o arquivo, ou pode mergulhar diretamente em https://github.com/RooVetGit/Roo-Code-Docs.
+- [Antes de Contribuir](#antes-de-contribuir)
+- [Encontrando & Planejando sua Contribuição](#encontrando--planejando-sua-contribuição)
+- [Processo de Desenvolvimento & Submissão](#processo-de-desenvolvimento--submissão)
+- [Legal](#legal)
-Se você está planejando trabalhar em um recurso maior, por favor crie primeiro uma [solicitação de recurso](https://github.com/RooVetGit/Roo-Code/discussions/categories/feature-requests?discussions_q=is%3Aopen+category%3A%22Feature+Requests%22+sort%3Atop) para que possamos discutir se está alinhado com a visão do Roo Code. Você também pode verificar nosso [Roteiro do Projeto](#roteiro-do-projeto) abaixo para ver se sua ideia se encaixa em nossa direção estratégica.
+## Antes de Contribuir
-## Roteiro do Projeto
+### 1. Código de Conduta
-O Roo Code possui um roteiro de desenvolvimento claro que orienta nossas prioridades e direção futura. Entender nosso roteiro pode ajudar você a:
+Todos os colaboradores devem seguir nosso [Código de Conduta](./CODE_OF_CONDUCT.md).
-- Alinhar suas contribuições com os objetivos do projeto
-- Identificar áreas onde sua expertise seria mais valiosa
-- Entender o contexto por trás de certas decisões de design
-- Encontrar inspiração para novos recursos que apoiem nossa visão
+### 2. Roadmap do Projeto
-Nosso roteiro atual se concentra em seis pilares principais:
+Nosso roadmap orienta a direção do projeto. Alinhe suas contribuições com estes objetivos principais:
-### Suporte a Provedores
+### Confiabilidade em Primeiro Lugar
-Nosso objetivo é oferecer suporte a tantos provedores quanto possível:
+- Garantir que a edição de diferenças e a execução de comandos sejam consistentemente confiáveis
+- Reduzir pontos de atrito que desencorajam o uso regular
+- Garantir operação suave em todos os idiomas e plataformas
+- Expandir o suporte robusto para uma ampla variedade de provedores e modelos de IA
-- Suporte mais versátil para "OpenAI Compatible"
-- xAI, Microsoft Azure AI, Alibaba Cloud Qwen, IBM Watsonx, Together AI, DeepInfra, Fireworks AI, Cohere, Perplexity AI, FriendliAI, Replicate
-- Suporte aprimorado para Ollama e LM Studio
+### Experiência de Usuário Aprimorada
-### Suporte a Modelos
+- Simplificar a interface do usuário para maior clareza e intuitividade
+- Melhorar continuamente o fluxo de trabalho para atender às altas expectativas dos desenvolvedores
-Queremos que o Roo funcione bem em tantos modelos quanto possível, incluindo modelos locais:
+### Liderança em Desempenho de Agentes
-- Suporte a modelos locais através de prompts de sistema personalizados e fluxos de trabalho
-- Avaliações de benchmark e casos de teste
+- Estabelecer benchmarks de avaliação abrangentes (evals) para medir a produtividade no mundo real
+- Facilitar para que todos possam executar e interpretar essas avaliações
+- Fornecer melhorias que demonstrem aumentos claros nas pontuações de avaliação
-### Suporte a Sistemas
+Mencione o alinhamento com estas áreas em seus PRs.
-Queremos que o Roo funcione bem no computador de todos:
+### 3. Junte-se à Comunidade Roo Code
-- Integração de terminal multiplataforma
-- Suporte forte e consistente para Mac, Windows e Linux
+- **Principal:** Junte-se ao nosso [Discord](https://discord.gg/roocode) e envie um DM para **Hannes Rudolph (`hrudolph`)**.
+- **Alternativa:** Colaboradores experientes podem participar diretamente via [GitHub Projects](https://github.com/orgs/RooCodeInc/projects/1).
-### Documentação
+## Encontrando & Planejando sua Contribuição
-Queremos documentação abrangente e acessível para todos os usuários e colaboradores:
+### Tipos de Contribuição
-- Guias de usuário e tutoriais expandidos
-- Documentação clara da API
-- Melhor orientação para colaboradores
-- Recursos de documentação multilíngues
-- Exemplos interativos e amostras de código
+- **Correção de bugs:** Corrigir problemas no código.
+- **Novos recursos:** Adicionar novas funcionalidades.
+- **Documentação:** Melhorar guias e clareza.
-### Estabilidade
+### Abordagem Issue-First
-Queremos diminuir significativamente o número de bugs e aumentar os testes automatizados:
+Todas as contribuições devem começar com uma Issue do GitHub.
-- Interruptor de registro de depuração
-- Botão de cópia "Informações de Máquina/Tarefa" para enviar com solicitações de suporte/bug
+- **Verificar issues existentes:** Procure em [GitHub Issues](https://github.com/RooCodeInc/Roo-Code/issues).
+- **Criar uma issue:** Use os templates apropriados:
+ - **Bugs:** Template "Bug Report".
+ - **Recursos:** Template "Detailed Feature Proposal". Aprovação necessária antes de começar.
+- **Reivindicar issues:** Comente e aguarde atribuição oficial.
-### Internacionalização
+**PRs sem issues aprovadas podem ser fechados.**
-Queremos que o Roo fale o idioma de todos:
+### Decidindo no que Trabalhar
-- 我们希望 Roo Code 说每个人的语言
-- Queremos que Roo Code hable el idioma de todos
-- हम चाहते हैं कि Roo Code हर किसी की भाषा बोले
-- نريد أن يتحدث Roo Code لغة الجميع
+- Confira o [Projeto GitHub](https://github.com/orgs/RooCodeInc/projects/1) para "Good First Issues" não atribuídas.
+- Para documentação, visite [Roo Code Docs](https://github.com/RooCodeInc/Roo-Code-Docs).
-Damos especialmente as boas-vindas a contribuições que avançam os objetivos do nosso roteiro. Se você estiver trabalhando em algo que se alinha com esses pilares, por favor mencione isso na descrição do seu PR.
+### Relatando Bugs
-## Configuração de Desenvolvimento
+- Verifique primeiro se já existem relatórios.
+- Crie novos relatórios de bugs usando o [template "Bug Report"](https://github.com/RooCodeInc/Roo-Code/issues/new/choose).
+- **Vulnerabilidades de segurança:** Relate de forma privada via [security advisories](https://github.com/RooCodeInc/Roo-Code/security/advisories/new).
-1. **Clone** o repositório:
+## Processo de Desenvolvimento & Submissão
-```sh
-git clone https://github.com/RooVetGit/Roo-Code.git
-```
+### Configuração de Desenvolvimento
-2. **Instale as dependências**:
+1. **Fork & Clone:**
-```sh
-npm run install:all
```
-
-3. **Inicie o webview (aplicativo Vite/React com HMR)**:
-
-```sh
-npm run dev
+git clone https://github.com/SEU_USUÁRIO/Roo-Code.git
```
-4. **Depuração**:
- Pressione `F5` (ou **Executar** → **Iniciar Depuração**) no VSCode para abrir uma nova sessão com o Roo Code carregado.
-
-Alterações no webview aparecerão imediatamente. Alterações na extensão principal exigirão a reinicialização do host da extensão.
-
-Alternativamente, você pode construir um .vsix e instalá-lo diretamente no VSCode:
+2. **Instalar dependências:**
-```sh
-npm run build
```
-
-Um arquivo `.vsix` aparecerá no diretório `bin/` que pode ser instalado com:
-
-```sh
-code --install-extension bin/roo-cline-.vsix
+npm run install:all
```
-## Escrevendo e Enviando Código
-
-Qualquer pessoa pode contribuir com código para o Roo Code, mas pedimos que você siga estas diretrizes para garantir que suas contribuições possam ser integradas sem problemas:
-
-1. **Mantenha os Pull Requests Focados**
-
- - Limite os PRs a um único recurso ou correção de bug
- - Divida mudanças maiores em PRs menores e relacionados
- - Divida as mudanças em commits lógicos que possam ser revisados independentemente
-
-2. **Qualidade do Código**
+3. **Depuração:** Abra com VS Code (`F5`).
- - Todos os PRs devem passar nas verificações de CI que incluem tanto linting quanto formatação
- - Resolva quaisquer avisos ou erros do ESLint antes de enviar
- - Responda a todos os feedbacks do Ellipsis, nossa ferramenta automatizada de revisão de código
- - Siga as melhores práticas de TypeScript e mantenha a segurança de tipos
+### Diretrizes para Escrever Código
-3. **Testes**
+- Um PR focado por recurso ou correção.
+- Siga as melhores práticas de ESLint e TypeScript.
+- Escreva commits claros e descritivos referenciando issues (ex: `Fixes #123`).
+- Forneça testes completos (`npm test`).
+- Rebase na branch `main` mais recente antes do envio.
- - Adicione testes para novos recursos
- - Execute `npm test` para garantir que todos os testes passem
- - Atualize os testes existentes se suas mudanças os afetarem
- - Inclua tanto testes unitários quanto de integração quando apropriado
+### Enviando um Pull Request
-4. **Diretrizes de Commit**
+- Comece como **PR em rascunho** se buscar feedback antecipado.
+- Descreva claramente suas alterações seguindo o Template de Pull Request.
+- Forneça capturas de tela/vídeos para alterações de UI.
+- Indique se atualizações de documentação são necessárias.
- - Escreva mensagens de commit claras e descritivas
- - Referencie issues relevantes nos commits usando #número-da-issue
+### Política de Pull Request
-5. **Antes de Enviar**
+- Deve referenciar issues pré-aprovadas e atribuídas.
+- PRs que não seguem a política podem ser fechados.
+- PRs devem passar nos testes de CI, alinhar-se ao roadmap e ter documentação clara.
- - Faça rebase da sua branch na última main
- - Certifique-se de que sua branch é construída com sucesso
- - Verifique novamente se todos os testes estão passando
- - Revise suas mudanças para qualquer código de depuração ou logs de console
+### Processo de Revisão
-6. **Descrição do Pull Request**
- - Descreva claramente o que suas mudanças fazem
- - Inclua passos para testar as mudanças
- - Liste quaisquer mudanças significativas
- - Adicione capturas de tela para mudanças na UI
+- **Triagem diária:** Verificações rápidas pelos mantenedores.
+- **Revisão semanal detalhada:** Avaliação abrangente.
+- **Itere rapidamente** com base no feedback.
-## Acordo de Contribuição
+## Legal
-Ao enviar um pull request, você concorda que suas contribuições serão licenciadas sob a mesma licença do projeto ([Apache 2.0](../LICENSE)).
+Ao enviar um pull request, você concorda que suas contribuições serão licenciadas sob a Licença Apache 2.0, consistente com o licenciamento do Roo Code.
diff --git a/locales/pt-BR/README.md b/locales/pt-BR/README.md
index fd3cb50916..a2a57219c0 100644
--- a/locales/pt-BR/README.md
+++ b/locales/pt-BR/README.md
@@ -1,7 +1,7 @@
-
+
@@ -43,17 +43,17 @@
Seja você esteja buscando um parceiro de codificação flexível, um arquiteto de sistema ou funções especializadas como engenheiro de QA ou gerente de produto, o Roo Code pode ajudá-lo a construir software com mais eficiência.
-Confira o [CHANGELOG](../CHANGELOG.md) para atualizações e correções detalhadas.
+Confira o [CHANGELOG](../../CHANGELOG.md) para atualizações e correções detalhadas.
---
-## 🎉 Roo Code 3.14 Lançado
+## 🎉 Roo Code 3.17 Lançado
-O Roo Code 3.14 traz novas funcionalidades e melhorias baseadas no seu feedback!
+O Roo Code 3.17 traz poderosas novas funcionalidades e melhorias baseadas no seu feedback!
-- **Cache para prompts** - `gemini-2.5-pro-preview-03-25` agora suporta cache de prompts no provedor Gemini (Vertex e OpenRouter em breve).
-- **Ferramentas de edição aprimoradas** - As ferramentas `search_and_replace` e `insert_content` foram aprimoradas e não são mais experimentais.
-- **Toneladas de outras melhorias** - Inúmeras correções e aprimoramentos em toda a extensão.
+- **Cache Implícito para Gemini** - Chamadas de API Gemini agora são automaticamente armazenadas em cache, reduzindo custos de API.
+- **Seleção de Modo mais Inteligente** - Definições de modo agora podem incluir orientações sobre quando cada modo deve ser usado, permitindo melhor orquestração.
+- **Condensação Inteligente de Contexto** - Resume de forma inteligente o histórico de conversas quando o contexto se enche, em vez de truncar (ative em Configurações -> Experimental).
---
@@ -116,7 +116,7 @@ Faça o Roo Code funcionar do seu jeito com:
- **Discord:** [Participe do nosso servidor Discord](https://discord.gg/roocode) para ajuda em tempo real e discussões
- **Reddit:** [Visite nosso subreddit](https://www.reddit.com/r/RooCode) para compartilhar experiências e dicas
-- **GitHub:** [Reportar problemas](https://github.com/RooVetGit/Roo-Code/issues) ou [solicitar recursos](https://github.com/RooVetGit/Roo-Code/discussions/categories/feature-requests?discussions_q=is%3Aopen+category%3A%22Feature+Requests%22+sort%3Atop)
+- **GitHub:** [Reportar problemas](https://github.com/RooCodeInc/Roo-Code/issues) ou [solicitar recursos](https://github.com/RooCodeInc/Roo-Code/discussions/categories/feature-requests?discussions_q=is%3Aopen+category%3A%22Feature+Requests%22+sort%3Atop)
---
@@ -125,7 +125,7 @@ Faça o Roo Code funcionar do seu jeito com:
1. **Clone** o repositório:
```sh
-git clone https://github.com/RooVetGit/Roo-Code.git
+git clone https://github.com/RooCodeInc/Roo-Code.git
```
2. **Instale as dependências**:
@@ -178,31 +178,36 @@ Adoramos contribuições da comunidade! Comece lendo nosso [CONTRIBUTING.md](CON
Obrigado a todos os nossos contribuidores que ajudaram a tornar o Roo Code melhor!
-| mrubens| saoudrizwan| cte| samhvw8| daniel-lxs| a8trejo|
-|:---:|:---:|:---:|:---:|:---:|:---:|
-| ColemanRoo| stea9499| joemanley201| System233| hannesrudolph| jquanton|
-| nissa-seru| KJ7LNW| NyxJae| MuriloFP| d-oit| punkpeye|
-| Smartsheet-JB-Brown| monotykamary| wkordalski| feifei325| cannuri| lloydchang|
-| vigneshsubbiah16| qdaxb| Szpadel| Premshay| psv2522| diarmidmackenzie|
-| lupuletic| elianiva| olweraltuve| sachasayan| afshawnlotfi| pugazhendhi-m|
-| aheizi| RaySinner| PeterDaveHello| nbihan-mediware| dtrugman| emshvac|
-| kyle-apex| pdecat| zhangtony239| Lunchb0ne| arthurauffray| upamune|
-| StevenTCramer| sammcj| p12tic| gtaylor| aitoroses| yt3trees|
-| franekp| yongjer| vincentsong| vagadiya| teddyOOXX| eonghk|
-| taisukeoe| heyseth| ross| philfung| napter| mdp|
-| SplittyDev| Chenjiayuan195| jcbdev| GitlyHallows| bramburn| benzntech|
-| axkirillov| anton-otee| shoopapa| jwcraig| kinandan| kohii|
-| lightrabbit| olup| mecab| nevermorec| im47cn| hongzio|
-| dqroid| dairui1| bannzai| axmo| asychin| ashktn|
-| eltociear| PretzelVector| cdlliuy| student20880| shohei-ihaya| shaybc|
-| shariqriazz| seedlord| samir-nimbly| ronyblum| refactorthis| pokutuna|
-| philipnext| oprstchn| nobu007| mosleyit| moqimoqidea| mlopezr|
-| hesara| DeXtroTip| celestial-vault| linegel| snoyiatk| dbasclpy|
-| dleen| chadgauth| bogdan0083| Atlogit| atlasgong| andreastempsch|
-| QuinsZouls| alarno| adamwlarson| AMHesch| amittell| Yoshino-Yukitaro|
-| Yikai-Liao| vladstudio| NamesMT| tmsjngx0| tgfjt| maekawataiki|
-| samsilveira| 01Rian| Sarke| kvokka| marvijo-code| mamertofabian|
-| libertyteeth| shtse8| Jdo300| | | |
+
+| mrubens | saoudrizwan | cte | samhvw8 | daniel-lxs | a8trejo |
+| :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |
+| ColemanRoo | hannesrudolph | KJ7LNW | stea9499 | joemanley201 | System233 |
+| nissa-seru | jquanton | NyxJae | MuriloFP | d-oit | punkpeye |
+| wkordalski | Smartsheet-JB-Brown | monotykamary | elianiva | cannuri | feifei325 |
+| zhangtony239 | sachasayan | lloydchang | vigneshsubbiah16 | Szpadel | qdaxb |
+| lupuletic | Premshay | psv2522 | diarmidmackenzie | aheizi | olweraltuve |
+| jr | dtrugman | nbihan-mediware | PeterDaveHello | RaySinner | pugazhendhi-m |
+| afshawnlotfi | shariqriazz | pdecat | kyle-apex | emshvac | Lunchb0ne |
+| xyOz-dev | arthurauffray | upamune | StevenTCramer | sammcj | p12tic |
+| gtaylor | aitoroses | benzntech | ross | heyseth | taisukeoe |
+| dlab-anton | eonghk | teddyOOXX | vagadiya | vincentsong | yongjer |
+| ashktn | franekp | yt3trees | anton-otee | axkirillov | bramburn |
+| snoyiatk | GitlyHallows | jcbdev | Chenjiayuan195 | julionav | SplittyDev |
+| mdp | napter | philfung | im47cn | shoopapa | jwcraig |
+| kinandan | kohii | lightrabbit | olup | mecab | nevermorec |
+| hongzio | GOODBOY008 | dqroid | dairui1 | bannzai | axmo |
+| asychin | amittell | Yoshino-Yukitaro | Yikai-Liao | SmartManoj | PretzelVector |
+| zetaloop | cdlliuy | student20880 | shohei-ihaya | shaybc | seedlord |
+| samir-nimbly | ronyblum | robertheadley | refactorthis | pokutuna | philipnext |
+| oprstchn | nobu007 | mosleyit | moqimoqidea | mlopezr | zxdvd |
+| DeXtroTip | pfitz | celestial-vault | linegel | dbasclpy | Deon588 |
+| dleen | chadgauth | olearycrew | bogdan0083 | Atlogit | atlasgong |
+| andreastempsch | alasano | QuinsZouls | HadesArchitect | alarno | adamwlarson |
+| AMHesch | vladstudio | NamesMT | tmsjngx0 | tgfjt | maekawataiki |
+| samsilveira | mr-ryan-james | 01Rian | Sarke | kvokka | ecmasx |
+| marvijo-code | mamertofabian | monkeyDluffy6017 | libertyteeth | shtse8 | ksze |
+| Jdo300 | hesara | | | | |
+
## Licença
diff --git a/locales/ru/CODE_OF_CONDUCT.md b/locales/ru/CODE_OF_CONDUCT.md
index cfc93a3b73..203a836d24 100644
--- a/locales/ru/CODE_OF_CONDUCT.md
+++ b/locales/ru/CODE_OF_CONDUCT.md
@@ -1,3 +1,7 @@
+[English](../../CODE_OF_CONDUCT.md) • [Català](../ca/CODE_OF_CONDUCT.md) • [Deutsch](../de/CODE_OF_CONDUCT.md) • [Español](../es/CODE_OF_CONDUCT.md) • [Français](../fr/CODE_OF_CONDUCT.md) • [हिंदी](../hi/CODE_OF_CONDUCT.md) • [Italiano](../it/CODE_OF_CONDUCT.md) • [Nederlands](../nl/CODE_OF_CONDUCT.md) • Русский
+
+[日本語](../ja/CODE_OF_CONDUCT.md) • [한국어](../ko/CODE_OF_CONDUCT.md) • [Polski](../pl/CODE_OF_CONDUCT.md) • [Português (BR)](../pt-BR/CODE_OF_CONDUCT.md) • [Türkçe](../tr/CODE_OF_CONDUCT.md) • [Tiếng Việt](../vi/CODE_OF_CONDUCT.md) • [简体中文](../zh-CN/CODE_OF_CONDUCT.md) • [繁體中文](../zh-TW/CODE_OF_CONDUCT.md)
+
# Кодекс поведения участников
## Наше обязательство
diff --git a/locales/ru/CONTRIBUTING.md b/locales/ru/CONTRIBUTING.md
index 0c12ef5cd2..155353c0ce 100644
--- a/locales/ru/CONTRIBUTING.md
+++ b/locales/ru/CONTRIBUTING.md
@@ -1,72 +1,129 @@
-# Руководство по участию в проекте
+[English](../../CONTRIBUTING.md) • [Català](../ca/CONTRIBUTING.md) • [Deutsch](../de/CONTRIBUTING.md) • [Español](../es/CONTRIBUTING.md) • [Français](../fr/CONTRIBUTING.md) • [हिंदी](../hi/CONTRIBUTING.md) • [Italiano](../it/CONTRIBUTING.md) • [Nederlands](../nl/CONTRIBUTING.md) • Русский
-Спасибо за интерес к участию в развитии Roo Code! Мы рады приветствовать новых участников в нашем сообществе.
+[日本語](../ja/CONTRIBUTING.md) • [한국어](../ko/CONTRIBUTING.md) • [Polski](../pl/CONTRIBUTING.md) • [Português (BR)](../pt-BR/CONTRIBUTING.md) • [Türkçe](../tr/CONTRIBUTING.md) • [Tiếng Việt](../vi/CONTRIBUTING.md) • [简体中文](../zh-CN/CONTRIBUTING.md) • [繁體中文](../zh-TW/CONTRIBUTING.md)
-## Присоединяйтесь к сообществу
+# Вклад в Roo Code
-- [Discord](https://discord.gg/roocode)
-- [Reddit](https://www.reddit.com/r/roocode)
+Roo Code — проект, управляемый сообществом, и мы высоко ценим каждый вклад. Для упрощения сотрудничества мы работаем по принципу [Issue-First](#подход-issue-first), что означает, что все [Pull Request (PR)](#отправка-pull-request) должны сначала быть связаны с GitHub Issue. Пожалуйста, внимательно ознакомься с этим руководством.
-## Сообщение об ошибках
+## Содержание
-Если вы обнаружили ошибку, пожалуйста, создайте issue в нашем репозитории. Убедитесь, что:
+- [Перед тем как внести вклад](#перед-тем-как-внести-вклад)
+- [Поиск и планирование вклада](#поиск-и-планирование-вклада)
+- [Процесс разработки и отправки](#процесс-разработки-и-отправки)
+- [Юридическая информация](#юридическая-информация)
-1. Ошибка воспроизводима
-2. Вы предоставили всю необходимую информацию для воспроизведения ошибки
-3. Вы проверили, что подобная проблема еще не была зарегистрирована
+## Перед тем как внести вклад
-## Над чем работать
+### 1. Кодекс поведения
-Есть несколько способов начать участие в проекте:
+Все участники должны соблюдать наш [Кодекс поведения](./CODE_OF_CONDUCT.md).
-1. Просмотрите открытые issues с меткой "good first issue"
-2. Исправьте опечатки в документации
-3. Добавьте тесты для существующего кода
-4. Предложите новые функции через issues
+### 2. Дорожная карта проекта
-## Дорожная карта проекта
+Наша дорожная карта определяет направление проекта. Согласуй свой вклад с этими ключевыми целями:
-Наши текущие приоритеты:
+### Надежность в первую очередь
-- Улучшение производительности и стабильности
-- Расширение поддержки языков программирования
-- Улучшение пользовательского интерфейса
-- Интеграция с популярными инструментами разработки
+- Обеспечение стабильной работы редактирования различий и выполнения команд
+- Сокращение точек трения, препятствующих регулярному использованию
+- Гарантия бесперебойной работы на всех языках и платформах
+- Расширение надежной поддержки для широкого спектра ИИ-провайдеров и моделей
-## Настройка среды разработки
+### Улучшенный пользовательский опыт
-1. Форкните репозиторий
-2. Клонируйте ваш форк:
- ```bash
- git clone https://github.com/YOUR_USERNAME/roo-code.git
- ```
-3. Установите зависимости:
- ```bash
- npm install
- ```
-4. Создайте новую ветку для ваших изменений:
- ```bash
- git checkout -b feature/your-feature-name
- ```
+- Упрощение пользовательского интерфейса для большей ясности и интуитивности
+- Постоянное совершенствование рабочего процесса для соответствия высоким ожиданиям разработчиков
-## Написание и отправка кода
+### Лидерство в производительности агентов
-1. Следуйте существующему стилю кода
-2. Добавляйте тесты для нового кода
-3. Обновляйте документацию при необходимости
-4. Убедитесь, что все тесты проходят
-5. Создайте pull request с описанием ваших изменений
+- Создание комплексных показателей оценки (evals) для измерения реальной продуктивности
+- Упрощение запуска и интерпретации этих оценок для всех пользователей
+- Внедрение улучшений, демонстрирующих явное повышение оценочных показателей
-## Соглашение о сотрудничестве
+Упоминай связь с этими направлениями в своих PR.
-Отправляя pull request, вы соглашаетесь с тем, что ваш код будет распространяться под лицензией проекта. Все участники должны следовать нашему [Кодексу поведения](CODE_OF_CONDUCT.md).
+### 3. Присоединяйся к сообществу Roo Code
-## Получение помощи
+- **Основной способ:** Присоединись к нашему [Discord](https://discord.gg/roocode) и отправь личное сообщение **Hannes Rudolph (`hrudolph`)**.
+- **Альтернатива:** Опытные участники могут взаимодействовать напрямую через [GitHub Projects](https://github.com/orgs/RooCodeInc/projects/1).
-Если у вас возникли вопросы или нужна помощь:
+## Поиск и планирование вклада
-1. Проверьте существующую документацию
-2. Спросите в Discord сообществе
-3. Создайте issue с меткой "question"
+### Виды вклада
-Еще раз спасибо за ваш интерес к улучшению Roo Code!
+- **Исправление ошибок:** Решение проблем в коде.
+- **Новые функции:** Добавление функциональности.
+- **Документация:** Улучшение руководств и ясности.
+
+### Подход Issue-First
+
+Весь вклад должен начинаться с GitHub Issue.
+
+- **Проверь существующие issues:** Поищи в [GitHub Issues](https://github.com/RooCodeInc/Roo-Code/issues).
+- **Создай issue:** Используй подходящие шаблоны:
+ - **Баги:** Шаблон "Bug Report".
+ - **Функции:** Шаблон "Detailed Feature Proposal". Требуется одобрение перед началом.
+- **Заяви issue:** Оставь комментарий и дождись официального назначения.
+
+**PR без одобренных issue могут быть закрыты.**
+
+### Решение, над чем работать
+
+- Проверь [GitHub проект](https://github.com/orgs/RooCodeInc/projects/1) на наличие незанятых "Good First Issues".
+- Для документации посети [Roo Code Docs](https://github.com/RooCodeInc/Roo-Code-Docs).
+
+### Сообщение об ошибках
+
+- Сначала проверь существующие сообщения.
+- Создай новые сообщения об ошибках, используя [шаблон "Bug Report"](https://github.com/RooCodeInc/Roo-Code/issues/new/choose).
+- **Уязвимости безопасности:** Сообщай приватно через [security advisories](https://github.com/RooCodeInc/Roo-Code/security/advisories/new).
+
+## Процесс разработки и отправки
+
+### Настройка среды разработки
+
+1. **Fork & Clone:**
+
+```
+git clone https://github.com/ТВОЙ_ПОЛЬЗОВАТЕЛЬ/Roo-Code.git
+```
+
+2. **Установка зависимостей:**
+
+```
+npm run install:all
+```
+
+3. **Отладка:** Открой в VS Code (`F5`).
+
+### Руководство по написанию кода
+
+- Один сфокусированный PR на функцию или исправление.
+- Следуй лучшим практикам ESLint и TypeScript.
+- Пиши ясные, описательные сообщения коммитов с ссылками на issues (например, `Fixes #123`).
+- Обеспечь тщательное тестирование (`npm test`).
+- Перебазируй на последнюю ветку `main` перед отправкой.
+
+### Отправка Pull Request
+
+- Начни с **черновика PR**, если ищешь ранний фидбек.
+- Четко опиши свои изменения, следуя шаблону Pull Request.
+- Предоставь скриншоты/видео для изменений UI.
+- Укажи, нужны ли обновления документации.
+
+### Политика Pull Request
+
+- Должен ссылаться на предварительно одобренные и назначенные issue.
+- PR, не соответствующие политике, могут быть закрыты.
+- PR должны проходить CI-тесты, соответствовать дорожной карте и иметь четкую документацию.
+
+### Процесс проверки
+
+- **Ежедневный отбор:** Быстрые проверки мейнтейнерами.
+- **Еженедельный глубокий обзор:** Комплексная оценка.
+- **Быстро итерируй** на основе полученного фидбека.
+
+## Юридическая информация
+
+Отправляя pull request, ты соглашаешься, что твой вклад будет лицензирован под лицензией Apache 2.0, в соответствии с лицензией Roo Code.
diff --git a/locales/ru/README.md b/locales/ru/README.md
index 84c4255b6f..e7e864fc66 100644
--- a/locales/ru/README.md
+++ b/locales/ru/README.md
@@ -1,7 +1,7 @@
+# Roo Code'a Katkıda Bulunma
-## Ne Üzerinde Çalışacağınıza Karar Verme
+Roo Code, topluluk odaklı bir projedir ve her katkıyı çok önemsiyoruz. İşbirliğini kolaylaştırmak için [Issue-First](#issue-first-yaklaşımı) yaklaşımıyla çalışıyoruz; bu, tüm [Pull Request'lerin (PR'lar)](#pull-request-gönderme) önce bir GitHub Issue'ya bağlanması gerektiği anlamına gelir. Lütfen bu rehberi dikkatlice incele.
-İyi bir ilk katkı mı arıyorsunuz? [Roo Code Sorunları](https://github.com/orgs/RooVetGit/projects/1) Github Projemizin "Issue [Unassigned]" bölümündeki sorunları kontrol edin. Bunlar özellikle yeni katkıda bulunanlar ve biraz yardıma ihtiyaç duyduğumuz alanlar için seçilmiştir!
+## İçindekiler
-[Belgelerimize](https://docs.roocode.com/) katkıları da memnuniyetle karşılıyoruz! İster yazım hatalarını düzeltmek, mevcut kılavuzları geliştirmek veya yeni eğitim içeriği oluşturmak olsun - herkesin Roo Code'dan en iyi şekilde yararlanmasına yardımcı olan topluluk odaklı bir kaynak deposu oluşturmak istiyoruz. Dosyayı düzenlemek için Github'daki doğru yere hızlıca gitmek için herhangi bir sayfada "Edit this page" düğmesine tıklayabilir veya doğrudan https://github.com/RooVetGit/Roo-Code-Docs adresine dalabilirsiniz.
+- [Katkıdan Önce](#katkıdan-önce)
+- [Katkı Bulma & Planlama](#katkı-bulma--planlama)
+- [Geliştirme & Gönderim Süreci](#geliştirme--gönderim-süreci)
+- [Yasal](#yasal)
-Daha büyük bir özellik üzerinde çalışmayı planlıyorsanız, lütfen önce bir [özellik isteği](https://github.com/RooVetGit/Roo-Code/discussions/categories/feature-requests?discussions_q=is%3Aopen+category%3A%22Feature+Requests%22+sort%3Atop) oluşturun, böylece Roo Code'un vizyonuyla uyumlu olup olmadığını tartışabiliriz. Ayrıca, fikrinizin stratejik yönümüze uyup uymadığını görmek için aşağıdaki [Proje Yol Haritası](#proje-yol-haritası)'nı kontrol edebilirsiniz.
+## Katkıdan Önce
-## Proje Yol Haritası
+### 1. Davranış Kuralları
-Roo Code, önceliklerimizi ve gelecekteki yönümüzü yönlendiren net bir geliştirme yol haritasına sahiptir. Yol haritamızı anlamak size şu konularda yardımcı olabilir:
+Tüm katkı sağlayanlar [Davranış Kuralları](./CODE_OF_CONDUCT.md)'na uymalıdır.
-- Katkılarınızı proje hedefleriyle uyumlu hale getirmek
-- Uzmanlığınızın en değerli olacağı alanları belirlemek
-- Belirli tasarım kararlarının arkasındaki bağlamı anlamak
-- Vizyonumuzu destekleyen yeni özellikler için ilham bulmak
+### 2. Proje Yol Haritası
-Mevcut yol haritamız altı temel sütun üzerine odaklanmaktadır:
+Yol haritamız projenin yönünü belirler. Katkılarını bu temel hedeflerle uyumlu hale getir:
-### Sağlayıcı Desteği
+### Güvenilirlik Öncelikli
-Mümkün olduğunca çok sağlayıcıyı desteklemeyi hedefliyoruz:
+- Diff düzenleme ve komut yürütme işlemlerinin sürekli olarak güvenilir olmasını sağlamak
+- Düzenli kullanımı engelleyen sürtünme noktalarını azaltmak
+- Tüm dillerde ve platformlarda sorunsuz çalışmayı garanti etmek
+- Çok çeşitli yapay zeka sağlayıcıları ve modelleri için güçlü desteği genişletmek
-- Daha çok yönlü "OpenAI Uyumlu" destek
-- xAI, Microsoft Azure AI, Alibaba Cloud Qwen, IBM Watsonx, Together AI, DeepInfra, Fireworks AI, Cohere, Perplexity AI, FriendliAI, Replicate
-- Ollama ve LM Studio için geliştirilmiş destek
+### Geliştirilmiş Kullanıcı Deneyimi
-### Model Desteği
+- Daha fazla netlik ve sezgisellik için kullanıcı arayüzünü basitleştirmek
+- Geliştiricilerin yüksek beklentilerini karşılamak üzere iş akışını sürekli iyileştirmek
-Roo'nun yerel modeller de dahil olmak üzere mümkün olduğunca çok modelde iyi çalışmasını istiyoruz:
+### Ajan Performansında Liderlik
-- Özel sistem yönlendirmesi ve iş akışları aracılığıyla yerel model desteği
-- Kıyaslama değerlendirmeleri ve test vakaları
+- Gerçek dünyadaki üretkenliği ölçmek için kapsamlı değerlendirme kriterleri (evals) oluşturmak
+- Herkesin bu değerlendirmeleri kolayca çalıştırıp yorumlamasını sağlamak
+- Değerlendirme puanlarında net artışlar gösteren iyileştirmeler sunmak
-### Sistem Desteği
+PR'larında bu alanlarla olan bağlantıyı belirt.
-Roo'nun herkesin bilgisayarında iyi çalışmasını istiyoruz:
+### 3. Roo Code Topluluğuna Katıl
-- Çapraz platform terminal entegrasyonu
-- Mac, Windows ve Linux için güçlü ve tutarlı destek
+- **Ana yöntem:** [Discord](https://discord.gg/roocode)'umuza katıl ve **Hannes Rudolph (`hrudolph`)**'a DM gönder.
+- **Alternatif:** Deneyimli katkı sağlayanlar [GitHub Projects](https://github.com/orgs/RooCodeInc/projects/1) üzerinden doğrudan katılabilir.
-### Dokümantasyon
+## Katkı Bulma & Planlama
-Tüm kullanıcılar ve katkıda bulunanlar için kapsamlı, erişilebilir dokümantasyon istiyoruz:
+### Katkı Türleri
-- Genişletilmiş kullanıcı kılavuzları ve öğreticiler
-- Net API dokümantasyonu
-- Katkıda bulunanlar için daha iyi rehberlik
-- Çok dilli dokümantasyon kaynakları
-- Etkileşimli örnekler ve kod örnekleri
+- **Hata düzeltmeleri:** Koddaki sorunları çözmek.
+- **Yeni özellikler:** Yeni işlevsellik eklemek.
+- **Dokümantasyon:** Rehberleri geliştirmek ve netleştirmek.
-### Kararlılık
+### Issue-First Yaklaşımı
-Hata sayısını önemli ölçüde azaltmak ve otomatik testleri artırmak istiyoruz:
+Tüm katkılar bir GitHub Issue ile başlamalıdır.
-- Hata ayıklama günlüğü anahtarı
-- Hata/destek istekleriyle birlikte göndermek için "Makine/Görev Bilgisi" kopyalama düğmesi
+- **Mevcut issue'ları kontrol et:** [GitHub Issues](https://github.com/RooCodeInc/Roo-Code/issues)'da ara.
+- **Issue oluştur:** Uygun şablonları kullan:
+ - **Hatalar:** "Bug Report" şablonu.
+ - **Özellikler:** "Detailed Feature Proposal" şablonu. Başlamadan önce onay gerekir.
+- **Issue'ları sahiplen:** Yorum yap ve resmi atamayı bekle.
-### Uluslararasılaştırma
+**Onaylanmış issue'lara bağlı olmayan PR'lar kapatılabilir.**
-Roo'nun herkesin dilini konuşmasını istiyoruz:
+### Ne Üzerinde Çalışacağına Karar Verme
-- 我们希望 Roo Code 说每个人的语言
-- Queremos que Roo Code hable el idioma de todos
-- हम चाहते हैं कि Roo Code हर किसी की भाषा बोले
-- نريد أن يتحدث Roo Code لغة الجميع
+- [GitHub Projesi](https://github.com/orgs/RooCodeInc/projects/1)'nde atanmamış "Good First Issues" bak.
+- Dokümantasyon için [Roo Code Docs](https://github.com/RooCodeInc/Roo-Code-Docs)'u ziyaret et.
-Özellikle yol haritamızın hedeflerini ileriye taşıyan katkıları memnuniyetle karşılıyoruz. Bu sütunlarla uyumlu bir şey üzerinde çalışıyorsanız, lütfen PR açıklamanızda bundan bahsedin.
+### Hata veya Sorun Bildirme
-## Geliştirme Kurulumu
+- Önce mevcut raporları kontrol et.
+- ["Bug Report" şablonu](https://github.com/RooCodeInc/Roo-Code/issues/new/choose) kullanarak yeni hata raporları oluştur.
+- **Güvenlik açıkları:** [security advisories](https://github.com/RooCodeInc/Roo-Code/security/advisories/new) aracılığıyla özel olarak bildir.
-1. Depoyu **klonlayın**:
+## Geliştirme & Gönderim Süreci
-```sh
-git clone https://github.com/RooVetGit/Roo-Code.git
-```
+### Geliştirme Ortamı Kurulumu
-2. **Bağımlılıkları yükleyin**:
+1. **Fork & Clone:**
-```sh
-npm run install:all
```
-
-3. **Webview'ı başlatın (HMR ile Vite/React uygulaması)**:
-
-```sh
-npm run dev
+git clone https://github.com/KULLANICI_ADIN/Roo-Code.git
```
-4. **Hata ayıklama**:
- VSCode'da `F5` tuşuna basın (veya **Run** → **Start Debugging**) Roo Code yüklenmiş yeni bir oturum açmak için.
-
-Webview'daki değişiklikler anında görünecektir. Ana uzantıdaki değişiklikler uzantı ana bilgisayarının yeniden başlatılmasını gerektirecektir.
-
-Alternatif olarak, bir .vsix dosyası oluşturabilir ve doğrudan VSCode'a kurabilirsiniz:
+2. **Bağımlılıkları yükle:**
-```sh
-npm run build
```
-
-`bin/` dizininde bir `.vsix` dosyası görünecek ve şu komutla kurulabilir:
-
-```sh
-code --install-extension bin/roo-cline-.vsix
+npm run install:all
```
-## Kod Yazma ve Gönderme
-
-Herkes Roo Code'a kod katkısında bulunabilir, ancak katkılarınızın sorunsuz bir şekilde entegre edilebilmesi için bu kurallara uymanızı rica ediyoruz:
-
-1. **Pull Request'leri Odaklı Tutun**
-
- - PR'leri tek bir özellik veya hata düzeltmesiyle sınırlayın
- - Daha büyük değişiklikleri daha küçük, ilgili PR'lere bölün
- - Değişiklikleri bağımsız olarak incelenebilen mantıklı commitlere bölün
-
-2. **Kod Kalitesi**
+3. **Hata ayıklama:** VS Code'da `F5` ile aç.
- - Tüm PR'ler hem linting hem de formatlama içeren CI kontrollerini geçmelidir
- - Göndermeden önce tüm ESLint uyarılarını veya hatalarını çözün
- - Otomatik kod inceleme aracımız Ellipsis'ten gelen tüm geri bildirimlere yanıt verin
- - TypeScript en iyi uygulamalarını takip edin ve tip güvenliğini koruyun
+### Kod Yazma Rehberi
-3. **Test Etme**
+- Her özellik veya düzeltme için odaklı bir PR.
+- ESLint ve TypeScript en iyi uygulamalarını takip et.
+- Issue'lara referans veren açık, açıklayıcı commit mesajları yaz (örn. `Fixes #123`).
+- Kapsamlı testler sağla (`npm test`).
+- Göndermeden önce en son `main` branch'i üzerine rebase yap.
- - Yeni özellikler için testler ekleyin
- - Tüm testlerin geçtiğinden emin olmak için `npm test` çalıştırın
- - Değişiklikleriniz etkiliyorsa mevcut testleri güncelleyin
- - Uygun olduğunda hem birim testlerini hem de entegrasyon testlerini dahil edin
+### Pull Request Gönderme
-4. **Commit Yönergeleri**
+- Erken geri bildirim istiyorsan **taslak PR** olarak başla.
+- Pull Request Şablonunu takip ederek değişikliklerini açıkça tanımla.
+- UI değişiklikleri için ekran görüntüleri/videolar sağla.
+- Dokümantasyon güncellemeleri gerekip gerekmediğini belirt.
- - Net, açıklayıcı commit mesajları yazın
- - #issue-number kullanarak commitlerdeki ilgili sorunlara atıfta bulunun
+### Pull Request Politikası
-5. **Göndermeden Önce**
+- Önceden onaylanmış ve atanmış issue'lara referans vermelidir.
+- Politikaya uymayan PR'lar kapatılabilir.
+- PR'lar CI testlerini geçmeli, yol haritasıyla uyumlu olmalı ve net dokümantasyona sahip olmalıdır.
- - Dalınızı en son main üzerine rebase edin
- - Dalınızın başarıyla oluşturulduğundan emin olun
- - Tüm testlerin geçtiğini tekrar kontrol edin
- - Değişikliklerinizi hata ayıklama kodu veya konsol günlükleri için gözden geçirin
+### İnceleme Süreci
-6. **Pull Request Açıklaması**
- - Değişikliklerinizin ne yaptığını açıkça açıklayın
- - Değişiklikleri test etmek için adımlar ekleyin
- - Herhangi bir önemli değişikliği listeleyin
- - UI değişiklikleri için ekran görüntüleri ekleyin
+- **Günlük triyaj:** Maintainer'lar tarafından hızlı kontroller.
+- **Haftalık detaylı inceleme:** Kapsamlı değerlendirme.
+- **Geri bildirim temelinde hızla yinele.**
-## Katkı Anlaşması
+## Yasal
-Bir pull request göndererek, katkılarınızın projeyle aynı lisans altında ([Apache 2.0](../LICENSE)) lisanslanacağını kabul edersiniz.
+Pull request göndererek, katkılarının Roo Code'un lisanslamasıyla tutarlı olarak Apache 2.0 Lisansı altında lisanslanacağını kabul etmiş olursun.
diff --git a/locales/tr/README.md b/locales/tr/README.md
index f26e54dca7..572c26fc7f 100644
--- a/locales/tr/README.md
+++ b/locales/tr/README.md
@@ -1,7 +1,7 @@
+### 1. Quy tắc ứng xử
-## Quyết Định Làm Việc trên Cái Gì
+Tất cả thành viên đóng góp phải tuân thủ [Quy tắc ứng xử](./CODE_OF_CONDUCT.md) của chúng mình.
-Tìm kiếm đóng góp đầu tiên tốt? Kiểm tra các vấn đề trong phần "Issue [Unassigned]" của [Dự án Github Roo Code](https://github.com/orgs/RooVetGit/projects/1) của chúng tôi. Những vấn đề này được chọn lọc đặc biệt cho người đóng góp mới và các lĩnh vực mà chúng tôi muốn nhận được sự giúp đỡ!
+### 2. Lộ trình phát triển dự án
-Chúng tôi cũng hoan nghênh đóng góp cho [tài liệu](https://docs.roocode.com/) của chúng tôi! Dù là sửa lỗi chính tả, cải thiện hướng dẫn hiện có, hay tạo nội dung giáo dục mới - chúng tôi muốn xây dựng một kho tài nguyên do cộng đồng thúc đẩy giúp mọi người tận dụng tối đa Roo Code. Bạn có thể nhấp vào "Edit this page" trên bất kỳ trang nào để nhanh chóng đến đúng vị trí trong Github để chỉnh sửa tệp, hoặc bạn có thể đi trực tiếp vào https://github.com/RooVetGit/Roo-Code-Docs.
+Lộ trình của chúng mình định hướng dự án. Hãy điều chỉnh đóng góp của bạn theo các mục tiêu chính:
-Nếu bạn đang lên kế hoạch làm việc trên một tính năng lớn hơn, vui lòng tạo [yêu cầu tính năng](https://github.com/RooVetGit/Roo-Code/discussions/categories/feature-requests?discussions_q=is%3Aopen+category%3A%22Feature+Requests%22+sort%3Atop) trước để chúng tôi có thể thảo luận xem nó có phù hợp với tầm nhìn của Roo Code không. Bạn cũng có thể kiểm tra [Lộ Trình Dự Án](#lộ-trình-dự-án) bên dưới để xem liệu ý tưởng của bạn có phù hợp với định hướng chiến lược của chúng tôi không.
+### Độ tin cậy là ưu tiên hàng đầu
-## Lộ Trình Dự Án
+- Đảm bảo việc chỉnh sửa diff và thực thi lệnh luôn đáng tin cậy
+- Giảm thiểu các điểm cản trở khiến người dùng ngại sử dụng thường xuyên
+- Đảm bảo hoạt động mượt mà trên mọi ngôn ngữ và nền tảng
+- Mở rộng hỗ trợ mạnh mẽ cho nhiều nhà cung cấp và mô hình AI đa dạng
-Roo Code có một lộ trình phát triển rõ ràng hướng dẫn các ưu tiên và định hướng tương lai của chúng tôi. Hiểu lộ trình của chúng tôi có thể giúp bạn:
+### Nâng cao trải nghiệm người dùng
-- Điều chỉnh đóng góp của bạn với mục tiêu của dự án
-- Xác định các lĩnh vực mà chuyên môn của bạn sẽ có giá trị nhất
-- Hiểu bối cảnh đằng sau một số quyết định thiết kế
-- Tìm cảm hứng cho các tính năng mới hỗ trợ tầm nhìn của chúng tôi
+- Đơn giản hóa giao diện người dùng để tăng tính rõ ràng và trực quan
+- Liên tục cải thiện quy trình làm việc để đáp ứng kỳ vọng cao của các nhà phát triển
-Lộ trình hiện tại của chúng tôi tập trung vào sáu trụ cột chính:
+### Dẫn đầu về hiệu suất agent
-### Hỗ Trợ Nhà Cung Cấp
+- Thiết lập các tiêu chuẩn đánh giá toàn diện (evals) để đo lường năng suất trong thực tế
+- Giúp mọi người dễ dàng chạy và hiểu các đánh giá này
+- Cung cấp các cải tiến thể hiện rõ sự tăng trưởng trong điểm đánh giá
-Chúng tôi hướng đến việc hỗ trợ càng nhiều nhà cung cấp càng tốt:
+Đề cập đến sự liên quan với các lĩnh vực này trong PR của bạn.
-- Hỗ trợ "OpenAI Compatible" linh hoạt hơn
-- xAI, Microsoft Azure AI, Alibaba Cloud Qwen, IBM Watsonx, Together AI, DeepInfra, Fireworks AI, Cohere, Perplexity AI, FriendliAI, Replicate
-- Hỗ trợ nâng cao cho Ollama và LM Studio
+### 3. Tham gia cộng đồng Roo Code
-### Hỗ Trợ Mô Hình
+- **Cách chính:** Tham gia [Discord](https://discord.gg/roocode) của chúng mình và nhắn tin trực tiếp cho **Hannes Rudolph (`hrudolph`)**.
+- **Cách thay thế:** Cộng tác viên có kinh nghiệm có thể tham gia trực tiếp qua [GitHub Projects](https://github.com/orgs/RooCodeInc/projects/1).
-Chúng tôi muốn Roo hoạt động tốt trên càng nhiều mô hình càng tốt, bao gồm cả mô hình cục bộ:
+## Tìm kiếm & lên kế hoạch đóng góp
-- Hỗ trợ mô hình cục bộ thông qua prompting hệ thống tùy chỉnh và quy trình làm việc
-- Đánh giá hiệu suất và các trường hợp thử nghiệm
+### Các loại đóng góp
-### Hỗ Trợ Hệ Thống
+- **Sửa lỗi:** Khắc phục vấn đề trong mã nguồn.
+- **Tính năng mới:** Thêm chức năng mới.
+- **Tài liệu:** Cải thiện hướng dẫn và độ rõ ràng.
-Chúng tôi muốn Roo chạy tốt trên máy tính của mọi người:
+### Cách tiếp cận Issue-First
-- Tích hợp terminal đa nền tảng
-- Hỗ trợ mạnh mẽ và nhất quán cho Mac, Windows và Linux
+Mọi đóng góp đều phải bắt đầu bằng một GitHub Issue.
-### Tài Liệu
+- **Kiểm tra issue hiện có:** Tìm kiếm trong [GitHub Issues](https://github.com/RooCodeInc/Roo-Code/issues).
+- **Tạo issue mới:** Sử dụng mẫu phù hợp:
+ - **Lỗi:** Mẫu "Bug Report".
+ - **Tính năng:** Mẫu "Detailed Feature Proposal". Cần được phê duyệt trước khi bắt đầu.
+- **Nhận issue:** Bình luận và chờ được gán chính thức.
-Chúng tôi muốn tài liệu toàn diện, dễ tiếp cận cho tất cả người dùng và người đóng góp:
+**PR không có issue đã duyệt có thể bị đóng.**
-- Hướng dẫn người dùng và hướng dẫn mở rộng
-- Tài liệu API rõ ràng
-- Hướng dẫn tốt hơn cho người đóng góp
-- Tài nguyên tài liệu đa ngôn ngữ
-- Ví dụ tương tác và mẫu mã
+### Quyết định việc cần làm
-### Ổn Định
+- Xem [Dự án GitHub](https://github.com/orgs/RooCodeInc/projects/1) để tìm "Good First Issues" chưa được gán.
+- Về tài liệu, hãy xem [Roo Code Docs](https://github.com/RooCodeInc/Roo-Code-Docs).
-Chúng tôi muốn giảm đáng kể số lượng lỗi và tăng kiểm tra tự động:
+### Báo cáo lỗi
-- Công tắc ghi nhật ký gỡ lỗi
-- Nút sao chép "Thông Tin Máy/Nhiệm Vụ" để gửi kèm với yêu cầu hỗ trợ/lỗi
+- Kiểm tra báo cáo hiện có trước.
+- Tạo báo cáo lỗi mới bằng [mẫu "Bug Report"](https://github.com/RooCodeInc/Roo-Code/issues/new/choose).
+- **Lỗ hổng bảo mật:** Báo cáo riêng qua [security advisories](https://github.com/RooCodeInc/Roo-Code/security/advisories/new).
-### Quốc Tế Hóa
+## Quy trình phát triển & gửi bài
-Chúng tôi muốn Roo nói ngôn ngữ của mọi người:
+### Thiết lập môi trường phát triển
-- 我们希望 Roo Code 说每个人的语言
-- Queremos que Roo Code hable el idioma de todos
-- हम चाहते हैं कि Roo Code हर किसी की भाषा बोले
-- نريد أن يتحدث Roo Code لغة الجميع
+1. **Fork & Clone:**
-Chúng tôi đặc biệt hoan nghênh những đóng góp thúc đẩy mục tiêu lộ trình của chúng tôi. Nếu bạn đang làm việc trên điều gì đó phù hợp với những trụ cột này, vui lòng đề cập đến điều đó trong mô tả PR của bạn.
-
-## Thiết Lập Phát Triển
-
-1. **Clone** kho lưu trữ:
-
-```sh
-git clone https://github.com/RooVetGit/Roo-Code.git
```
-
-2. **Cài đặt các phụ thuộc**:
-
-```sh
-npm run install:all
-```
-
-3. **Khởi động webview (ứng dụng Vite/React với HMR)**:
-
-```sh
-npm run dev
+git clone https://github.com/TEN_TAI_KHOAN/Roo-Code.git
```
-4. **Gỡ lỗi**:
- Nhấn `F5` (hoặc **Run** → **Start Debugging**) trong VSCode để mở phiên mới với Roo Code được tải.
+2. **Cài đặt phụ thuộc:**
-Các thay đổi đối với webview sẽ xuất hiện ngay lập tức. Các thay đổi đối với phần mở rộng cốt lõi sẽ yêu cầu khởi động lại máy chủ phần mở rộng.
-
-Hoặc bạn có thể xây dựng một tệp .vsix và cài đặt nó trực tiếp trong VSCode:
-
-```sh
-npm run build
```
-
-Một tệp `.vsix` sẽ xuất hiện trong thư mục `bin/` có thể được cài đặt bằng:
-
-```sh
-code --install-extension bin/roo-cline-.vsix
+npm run install:all
```
-## Viết và Gửi Mã
-
-Bất kỳ ai cũng có thể đóng góp mã cho Roo Code, nhưng chúng tôi yêu cầu bạn tuân theo những hướng dẫn này để đảm bảo đóng góp của bạn có thể được tích hợp suôn sẻ:
-
-1. **Giữ Pull Request Tập Trung**
-
- - Giới hạn PR vào một tính năng hoặc sửa lỗi duy nhất
- - Chia các thay đổi lớn hơn thành các PR nhỏ hơn, có liên quan
- - Chia các thay đổi thành các commit hợp lý có thể được xem xét độc lập
-
-2. **Chất Lượng Mã**
+3. **Debug:** Mở bằng VS Code (`F5`).
- - Tất cả PR phải vượt qua kiểm tra CI bao gồm cả linting và định dạng
- - Giải quyết mọi cảnh báo hoặc lỗi ESLint trước khi gửi
- - Phản hồi tất cả phản hồi từ Ellipsis, công cụ đánh giá mã tự động của chúng tôi
- - Tuân theo các thực hành tốt nhất của TypeScript và duy trì an toàn kiểu
+### Hướng dẫn viết mã
-3. **Kiểm Tra**
+- Mỗi PR chỉ tập trung vào một tính năng hoặc sửa lỗi.
+- Tuân thủ các thực hành tốt nhất của ESLint và TypeScript.
+- Viết thông điệp commit rõ ràng, tham chiếu đến issue (ví dụ: `Fixes #123`).
+- Cung cấp bài kiểm tra đầy đủ (`npm test`).
+- Rebase trên nhánh `main` mới nhất trước khi gửi.
- - Thêm kiểm tra cho các tính năng mới
- - Chạy `npm test` để đảm bảo tất cả các kiểm tra đều vượt qua
- - Cập nhật các bài kiểm tra hiện có nếu thay đổi của bạn ảnh hưởng đến chúng
- - Bao gồm cả kiểm tra đơn vị và kiểm tra tích hợp khi thích hợp
+### Gửi Pull Request
-4. **Hướng Dẫn Commit**
+- Bắt đầu với **PR nháp** nếu muốn nhận phản hồi sớm.
+- Mô tả rõ ràng các thay đổi, tuân theo Mẫu Pull Request.
+- Cung cấp ảnh chụp/video cho thay đổi UI.
+- Chỉ rõ nếu cần cập nhật tài liệu.
- - Viết thông điệp commit rõ ràng, mô tả
- - Tham chiếu các vấn đề có liên quan trong commit bằng cách sử dụng #số-vấn-đề
+### Chính sách Pull Request
-5. **Trước Khi Gửi**
+- Phải tham chiếu đến issue đã được phê duyệt và gán.
+- PR không tuân thủ chính sách có thể bị đóng.
+- PR cần vượt qua kiểm tra CI, phù hợp với lộ trình và có tài liệu rõ ràng.
- - Rebase nhánh của bạn trên main mới nhất
- - Đảm bảo nhánh của bạn xây dựng thành công
- - Kiểm tra lại rằng tất cả các bài kiểm tra đều vượt qua
- - Xem xét các thay đổi của bạn cho bất kỳ mã gỡ lỗi hoặc bản ghi console nào
+### Quy trình đánh giá
-6. **Mô Tả Pull Request**
- - Mô tả rõ ràng những gì thay đổi của bạn làm
- - Bao gồm các bước để kiểm tra các thay đổi
- - Liệt kê bất kỳ thay đổi đáng kể nào
- - Thêm ảnh chụp màn hình cho các thay đổi UI
+- **Phân loại hàng ngày:** Kiểm tra nhanh bởi maintainer.
+- **Đánh giá chi tiết hàng tuần:** Đánh giá toàn diện.
+- **Lặp lại nhanh chóng** dựa trên phản hồi.
-## Thỏa Thuận Đóng Góp
+## Pháp lý
-Bằng cách gửi một pull request, bạn đồng ý rằng đóng góp của bạn sẽ được cấp phép theo cùng giấy phép với dự án ([Apache 2.0](../LICENSE)).
+Khi gửi pull request, bạn đồng ý rằng đóng góp của mình sẽ được cấp phép theo Giấy phép Apache 2.0, phù hợp với giấy phép của Roo Code.
diff --git a/locales/vi/README.md b/locales/vi/README.md
index 70bfa54527..45a3e44570 100644
--- a/locales/vi/README.md
+++ b/locales/vi/README.md
@@ -1,7 +1,7 @@
")
+ expect(toolUse.params.path).toBe("src")
+ expect(toolUse.partial).toBe(false)
+ })
+
+ it("should handle consecutive tool uses without text in between", () => {
+ const message =
+ "file1.tsfile2.ts"
+ const result = parser(message).filter((block) => !isEmptyTextContent(block))
+
+ expect(result).toHaveLength(2)
+
+ const toolUse1 = result[0] as ToolUse
+ expect(toolUse1.type).toBe("tool_use")
+ expect(toolUse1.name).toBe("read_file")
+ expect(toolUse1.params.path).toBe("file1.ts")
+ expect(toolUse1.partial).toBe(false)
+
+ const toolUse2 = result[1] as ToolUse
+ expect(toolUse2.type).toBe("tool_use")
+ expect(toolUse2.name).toBe("read_file")
+ expect(toolUse2.params.path).toBe("file2.ts")
+ expect(toolUse2.partial).toBe(false)
+ })
+
+ it("should handle whitespace in parameters", () => {
+ const message = " src/file.ts "
+ const result = parser(message).filter((block) => !isEmptyTextContent(block))
+
+ expect(result).toHaveLength(1)
+ const toolUse = result[0] as ToolUse
+ expect(toolUse.type).toBe("tool_use")
+ expect(toolUse.name).toBe("read_file")
+ expect(toolUse.params.path).toBe("src/file.ts")
+ expect(toolUse.partial).toBe(false)
+ })
+
+ it("should handle multi-line parameters", () => {
+ const message = `file.ts
+ line 1
+ line 2
+ line 3
+ 3`
+ const result = parser(message).filter((block) => !isEmptyTextContent(block))
+
+ expect(result).toHaveLength(1)
+ const toolUse = result[0] as ToolUse
+ expect(toolUse.type).toBe("tool_use")
+ expect(toolUse.name).toBe("write_to_file")
+ expect(toolUse.params.path).toBe("file.ts")
+ expect(toolUse.params.content).toContain("line 1")
+ expect(toolUse.params.content).toContain("line 2")
+ expect(toolUse.params.content).toContain("line 3")
+ expect(toolUse.params.line_count).toBe("3")
+ expect(toolUse.partial).toBe(false)
+ })
+
+ it("should handle a complex message with multiple content types", () => {
+ const message = `I'll help you with that task.
+
+ src/index.ts
+
+ Now let's modify the file:
+
+ src/index.ts
+ // Updated content
+ console.log("Hello world");
+ 2
+
+ Let's run the code:
+
+ node src/index.ts`
+
+ const result = parser(message)
+
+ expect(result).toHaveLength(6)
+
+ // First text block
+ expect(result[0].type).toBe("text")
+ expect((result[0] as TextContent).content).toBe("I'll help you with that task.")
+
+ // First tool use (read_file)
+ expect(result[1].type).toBe("tool_use")
+ expect((result[1] as ToolUse).name).toBe("read_file")
+
+ // Second text block
+ expect(result[2].type).toBe("text")
+ expect((result[2] as TextContent).content).toContain("Now let's modify the file:")
+
+ // Second tool use (write_to_file)
+ expect(result[3].type).toBe("tool_use")
+ expect((result[3] as ToolUse).name).toBe("write_to_file")
+
+ // Third text block
+ expect(result[4].type).toBe("text")
+ expect((result[4] as TextContent).content).toContain("Let's run the code:")
+
+ // Third tool use (execute_command)
+ expect(result[5].type).toBe("tool_use")
+ expect((result[5] as ToolUse).name).toBe("execute_command")
+ })
+ })
+ })
+})
diff --git a/src/core/assistant-message/__tests__/parseAssistantMessageBenchmark.ts b/src/core/assistant-message/__tests__/parseAssistantMessageBenchmark.ts
new file mode 100644
index 0000000000..bea161330b
--- /dev/null
+++ b/src/core/assistant-message/__tests__/parseAssistantMessageBenchmark.ts
@@ -0,0 +1,109 @@
+// node --expose-gc --import tsx src/core/assistant-message/__tests__/parseAssistantMessageBenchmark.ts
+
+import { performance } from "perf_hooks"
+import { parseAssistantMessage as parseAssistantMessageV1 } from "../parseAssistantMessage"
+import { parseAssistantMessageV2 } from "../parseAssistantMessageV2"
+
+const formatNumber = (num: number): string => {
+ return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")
+}
+
+const measureExecutionTime = (fn: Function, input: string, iterations: number = 1000): number => {
+ for (let i = 0; i < 10; i++) {
+ fn(input)
+ }
+
+ const start = performance.now()
+
+ for (let i = 0; i < iterations; i++) {
+ fn(input)
+ }
+
+ const end = performance.now()
+ return (end - start) / iterations // Average time per iteration in ms.
+}
+
+const measureMemoryUsage = (
+ fn: Function,
+ input: string,
+ iterations: number = 100,
+): { heapUsed: number; heapTotal: number } => {
+ if (global.gc) {
+ // Force garbage collection if available.
+ global.gc()
+ } else {
+ console.warn("No garbage collection hook! Run with --expose-gc for more accurate memory measurements.")
+ }
+
+ const initialMemory = process.memoryUsage()
+
+ for (let i = 0; i < iterations; i++) {
+ fn(input)
+ }
+
+ const finalMemory = process.memoryUsage()
+
+ return {
+ heapUsed: (finalMemory.heapUsed - initialMemory.heapUsed) / iterations,
+ heapTotal: (finalMemory.heapTotal - initialMemory.heapTotal) / iterations,
+ }
+}
+
+const testCases = [
+ {
+ name: "Simple text message",
+ input: "This is a simple text message without any tool uses.",
+ },
+ {
+ name: "Message with a simple tool use",
+ input: "Let's read a file: src/file.ts",
+ },
+ {
+ name: "Message with a complex tool use (write_to_file)",
+ input: "src/file.ts\nfunction example() {\n // This has XML-like content: \n return true;\n}\n5",
+ },
+ {
+ name: "Message with multiple tool uses",
+ input: "First file: src/file1.ts\nSecond file: src/file2.ts\nLet's write a new file: src/file3.ts\nexport function newFunction() {\n return 'Hello world';\n}\n3",
+ },
+ {
+ name: "Large message with repeated tool uses",
+ input: Array(50)
+ .fill(
+ 'src/file.ts\noutput.tsconsole.log("hello");1',
+ )
+ .join("\n"),
+ },
+]
+
+const runBenchmark = () => {
+ const maxNameLength = testCases.reduce((max, testCase) => Math.max(max, testCase.name.length), 0)
+ const namePadding = maxNameLength + 2
+
+ console.log(
+ `| ${"Test Case".padEnd(namePadding)} | V1 Time (ms) | V2 Time (ms) | V1/V2 Ratio | V1 Heap (bytes) | V2 Heap (bytes) |`,
+ )
+ console.log(
+ `| ${"-".repeat(namePadding)} | ------------ | ------------ | ----------- | ---------------- | ---------------- |`,
+ )
+
+ for (const testCase of testCases) {
+ const v1Time = measureExecutionTime(parseAssistantMessageV1, testCase.input)
+ const v2Time = measureExecutionTime(parseAssistantMessageV2, testCase.input)
+ const timeRatio = v1Time / v2Time
+
+ const v1Memory = measureMemoryUsage(parseAssistantMessageV1, testCase.input)
+ const v2Memory = measureMemoryUsage(parseAssistantMessageV2, testCase.input)
+
+ console.log(
+ `| ${testCase.name.padEnd(namePadding)} | ` +
+ `${v1Time.toFixed(4).padStart(12)} | ` +
+ `${v2Time.toFixed(4).padStart(12)} | ` +
+ `${timeRatio.toFixed(2).padStart(11)} | ` +
+ `${formatNumber(Math.round(v1Memory.heapUsed)).padStart(16)} | ` +
+ `${formatNumber(Math.round(v2Memory.heapUsed)).padStart(16)} |`,
+ )
+ }
+}
+
+runBenchmark()
diff --git a/src/core/assistant-message/index.ts b/src/core/assistant-message/index.ts
index c53e88ed96..72201b7722 100644
--- a/src/core/assistant-message/index.ts
+++ b/src/core/assistant-message/index.ts
@@ -1 +1,2 @@
-export { type AssistantMessageContent, parseAssistantMessage } from "./parse-assistant-message"
+export { type AssistantMessageContent, parseAssistantMessage } from "./parseAssistantMessage"
+export { presentAssistantMessage } from "./presentAssistantMessage"
diff --git a/src/core/assistant-message/parse-assistant-message.ts b/src/core/assistant-message/parseAssistantMessage.ts
similarity index 73%
rename from src/core/assistant-message/parse-assistant-message.ts
rename to src/core/assistant-message/parseAssistantMessage.ts
index 0cac4dfb98..f641cabdaa 100644
--- a/src/core/assistant-message/parse-assistant-message.ts
+++ b/src/core/assistant-message/parseAssistantMessage.ts
@@ -3,7 +3,7 @@ import { toolNames, ToolName } from "../../schemas"
export type AssistantMessageContent = TextContent | ToolUse
-export function parseAssistantMessage(assistantMessage: string) {
+export function parseAssistantMessage(assistantMessage: string): AssistantMessageContent[] {
let contentBlocks: AssistantMessageContent[] = []
let currentTextContent: TextContent | undefined = undefined
let currentTextContentStartIndex = 0
@@ -17,28 +17,28 @@ export function parseAssistantMessage(assistantMessage: string) {
const char = assistantMessage[i]
accumulator += char
- // there should not be a param without a tool use
+ // There should not be a param without a tool use.
if (currentToolUse && currentParamName) {
const currentParamValue = accumulator.slice(currentParamValueStartIndex)
const paramClosingTag = `${currentParamName}>`
if (currentParamValue.endsWith(paramClosingTag)) {
- // end of param value
+ // End of param value.
currentToolUse.params[currentParamName] = currentParamValue.slice(0, -paramClosingTag.length).trim()
currentParamName = undefined
continue
} else {
- // partial param value is accumulating
+ // Partial param value is accumulating.
continue
}
}
- // no currentParamName
+ // No currentParamName.
if (currentToolUse) {
const currentToolValue = accumulator.slice(currentToolUseStartIndex)
const toolUseClosingTag = `${currentToolUse.name}>`
if (currentToolValue.endsWith(toolUseClosingTag)) {
- // end of a tool use
+ // End of a tool use.
currentToolUse.partial = false
contentBlocks.push(currentToolUse)
currentToolUse = undefined
@@ -47,23 +47,29 @@ export function parseAssistantMessage(assistantMessage: string) {
const possibleParamOpeningTags = toolParamNames.map((name) => `<${name}>`)
for (const paramOpeningTag of possibleParamOpeningTags) {
if (accumulator.endsWith(paramOpeningTag)) {
- // start of a new parameter
+ // Start of a new parameter.
currentParamName = paramOpeningTag.slice(1, -1) as ToolParamName
currentParamValueStartIndex = accumulator.length
break
}
}
- // there's no current param, and not starting a new param
+ // There's no current param, and not starting a new param.
- // special case for write_to_file where file contents could contain the closing tag, in which case the param would have closed and we end up with the rest of the file contents here. To work around this, we get the string between the starting content tag and the LAST content tag.
+ // Special case for write_to_file where file contents could
+ // contain the closing tag, in which case the param would have
+ // closed and we end up with the rest of the file contents here.
+ // To work around this, we get the string between the starting
+ // ontent tag and the LAST content tag.
const contentParamName: ToolParamName = "content"
+
if (currentToolUse.name === "write_to_file" && accumulator.endsWith(`${contentParamName}>`)) {
const toolContent = accumulator.slice(currentToolUseStartIndex)
const contentStartTag = `<${contentParamName}>`
const contentEndTag = `${contentParamName}>`
const contentStartIndex = toolContent.indexOf(contentStartTag) + contentStartTag.length
const contentEndIndex = toolContent.lastIndexOf(contentEndTag)
+
if (contentStartIndex !== -1 && contentEndIndex !== -1 && contentEndIndex > contentStartIndex) {
currentToolUse.params[contentParamName] = toolContent
.slice(contentStartIndex, contentEndIndex)
@@ -71,32 +77,38 @@ export function parseAssistantMessage(assistantMessage: string) {
}
}
- // partial tool value is accumulating
+ // Partial tool value is accumulating.
continue
}
}
- // no currentToolUse
+ // No currentToolUse.
let didStartToolUse = false
const possibleToolUseOpeningTags = toolNames.map((name) => `<${name}>`)
+
for (const toolUseOpeningTag of possibleToolUseOpeningTags) {
if (accumulator.endsWith(toolUseOpeningTag)) {
- // start of a new tool use
+ // Start of a new tool use.
currentToolUse = {
type: "tool_use",
name: toolUseOpeningTag.slice(1, -1) as ToolName,
params: {},
partial: true,
}
+
currentToolUseStartIndex = accumulator.length
- // this also indicates the end of the current text content
+
+ // This also indicates the end of the current text content.
if (currentTextContent) {
currentTextContent.partial = false
- // remove the partially accumulated tool use tag from the end of text (()
+ const toolParamOpenTags = new Map()
+
+ for (const name of toolNames) {
+ toolUseOpenTags.set(`<${name}>`, name)
+ }
+
+ for (const name of toolParamNames) {
+ toolParamOpenTags.set(`<${name}>`, name)
+ }
+
+ const len = assistantMessage.length
+
+ for (let i = 0; i < len; i++) {
+ const currentCharIndex = i
+
+ // Parsing a tool parameter
+ if (currentToolUse && currentParamName) {
+ const closeTag = `${currentParamName}>`
+ // Check if the string *ending* at index `i` matches the closing tag
+ if (
+ currentCharIndex >= closeTag.length - 1 &&
+ assistantMessage.startsWith(
+ closeTag,
+ currentCharIndex - closeTag.length + 1, // Start checking from potential start of tag.
+ )
+ ) {
+ // Found the closing tag for the parameter.
+ const value = assistantMessage
+ .slice(
+ currentParamValueStart, // Start after the opening tag.
+ currentCharIndex - closeTag.length + 1, // End before the closing tag.
+ )
+ .trim()
+ currentToolUse.params[currentParamName] = value
+ currentParamName = undefined // Go back to parsing tool content.
+ // We don't continue loop here, need to check for tool close or other params at index i.
+ } else {
+ continue // Still inside param value, move to next char.
+ }
+ }
+
+ // Parsing a tool use (but not a specific parameter).
+ if (currentToolUse && !currentParamName) {
+ // Ensure we are not inside a parameter already.
+ // Check if starting a new parameter.
+ let startedNewParam = false
+
+ for (const [tag, paramName] of toolParamOpenTags.entries()) {
+ if (
+ currentCharIndex >= tag.length - 1 &&
+ assistantMessage.startsWith(tag, currentCharIndex - tag.length + 1)
+ ) {
+ currentParamName = paramName
+ currentParamValueStart = currentCharIndex + 1 // Value starts after the tag.
+ startedNewParam = true
+ break
+ }
+ }
+
+ if (startedNewParam) {
+ continue // Handled start of param, move to next char.
+ }
+
+ // Check if closing the current tool use.
+ const toolCloseTag = `${currentToolUse.name}>`
+
+ if (
+ currentCharIndex >= toolCloseTag.length - 1 &&
+ assistantMessage.startsWith(toolCloseTag, currentCharIndex - toolCloseTag.length + 1)
+ ) {
+ // End of the tool use found.
+ // Special handling for content params *before* finalizing the
+ // tool.
+ const toolContentSlice = assistantMessage.slice(
+ currentToolUseStart, // From after the tool opening tag.
+ currentCharIndex - toolCloseTag.length + 1, // To before the tool closing tag.
+ )
+
+ // Check if content parameter needs special handling
+ // (write_to_file/new_rule).
+ // This check is important if the closing tag was
+ // missed by the parameter parsing logic (e.g., if content is
+ // empty or parsing logic prioritizes tool close).
+ const contentParamName: ToolParamName = "content"
+ if (
+ currentToolUse.name === "write_to_file" /* || currentToolUse.name === "new_rule" */ &&
+ // !(contentParamName in currentToolUse.params) && // Only if not already parsed.
+ toolContentSlice.includes(`<${contentParamName}>`) // Check if tag exists.
+ ) {
+ const contentStartTag = `<${contentParamName}>`
+ const contentEndTag = `${contentParamName}>`
+ const contentStart = toolContentSlice.indexOf(contentStartTag)
+
+ // Use `lastIndexOf` for robustness against nested tags.
+ const contentEnd = toolContentSlice.lastIndexOf(contentEndTag)
+
+ if (contentStart !== -1 && contentEnd !== -1 && contentEnd > contentStart) {
+ const contentValue = toolContentSlice
+ .slice(contentStart + contentStartTag.length, contentEnd)
+ .trim()
+
+ currentToolUse.params[contentParamName] = contentValue
+ }
+ }
+
+ currentToolUse.partial = false // Mark as complete.
+ contentBlocks.push(currentToolUse)
+ currentToolUse = undefined // Reset state.
+ currentTextContentStart = currentCharIndex + 1 // Potential text starts after this tag.
+ continue // Move to next char.
+ }
+
+ // If not starting a param and not closing the tool, continue
+ // accumulating tool content implicitly.
+ continue
+ }
+
+ // Parsing text / looking for tool start.
+ if (!currentToolUse) {
+ // Check if starting a new tool use.
+ let startedNewTool = false
+
+ for (const [tag, toolName] of toolUseOpenTags.entries()) {
+ if (
+ currentCharIndex >= tag.length - 1 &&
+ assistantMessage.startsWith(tag, currentCharIndex - tag.length + 1)
+ ) {
+ // End current text block if one was active.
+ if (currentTextContent) {
+ currentTextContent.content = assistantMessage
+ .slice(
+ currentTextContentStart, // From where text started.
+ currentCharIndex - tag.length + 1, // To before the tool tag starts.
+ )
+ .trim()
+
+ currentTextContent.partial = false // Ended because tool started.
+
+ if (currentTextContent.content.length > 0) {
+ contentBlocks.push(currentTextContent)
+ }
+
+ currentTextContent = undefined
+ } else {
+ // Check for any text between the last block and this tag.
+ const potentialText = assistantMessage
+ .slice(
+ currentTextContentStart, // From where text *might* have started.
+ currentCharIndex - tag.length + 1, // To before the tool tag starts.
+ )
+ .trim()
+
+ if (potentialText.length > 0) {
+ contentBlocks.push({
+ type: "text",
+ content: potentialText,
+ partial: false,
+ })
+ }
+ }
+
+ // Start the new tool use.
+ currentToolUse = {
+ type: "tool_use",
+ name: toolName,
+ params: {},
+ partial: true, // Assume partial until closing tag is found.
+ }
+
+ currentToolUseStart = currentCharIndex + 1 // Tool content starts after the opening tag.
+ startedNewTool = true
+
+ break
+ }
+ }
+
+ if (startedNewTool) {
+ continue // Handled start of tool, move to next char.
+ }
+
+ // If not starting a tool, it must be text content.
+ if (!currentTextContent) {
+ // Start a new text block if we aren't already in one.
+ currentTextContentStart = currentCharIndex // Text starts at the current character.
+
+ // Check if the current char is the start of potential text *immediately* after a tag.
+ // This needs the previous state - simpler to let slicing handle it later.
+ // Resetting start index accurately is key.
+ // It should be the index *after* the last processed tag.
+ // The logic managing currentTextContentStart after closing tags handles this.
+ currentTextContent = {
+ type: "text",
+ content: "", // Will be determined by slicing at the end or when a tool starts
+ partial: true,
+ }
+ }
+ // Continue accumulating text implicitly; content is extracted later.
+ }
+ }
+
+ // Finalize any open parameter within an open tool use.
+ if (currentToolUse && currentParamName) {
+ currentToolUse.params[currentParamName] = assistantMessage
+ .slice(currentParamValueStart) // From param start to end of string.
+ .trim()
+ // Tool use remains partial.
+ }
+
+ // Finalize any open tool use (which might contain the finalized partial param).
+ if (currentToolUse) {
+ // Tool use is partial because the loop finished before its closing tag.
+ contentBlocks.push(currentToolUse)
+ }
+ // Finalize any trailing text content.
+ // Only possible if a tool use wasn't open at the very end.
+ else if (currentTextContent) {
+ currentTextContent.content = assistantMessage
+ .slice(currentTextContentStart) // From text start to end of string.
+ .trim()
+
+ // Text is partial because the loop finished.
+ if (currentTextContent.content.length > 0) {
+ contentBlocks.push(currentTextContent)
+ }
+ }
+
+ return contentBlocks
+}
diff --git a/src/core/assistant-message/presentAssistantMessage.ts b/src/core/assistant-message/presentAssistantMessage.ts
new file mode 100644
index 0000000000..ef2bb04963
--- /dev/null
+++ b/src/core/assistant-message/presentAssistantMessage.ts
@@ -0,0 +1,525 @@
+import cloneDeep from "clone-deep"
+import { serializeError } from "serialize-error"
+
+import type { ToolName } from "../../schemas"
+
+import { defaultModeSlug, getModeBySlug } from "../../shared/modes"
+import type { ToolParamName, ToolResponse } from "../../shared/tools"
+import type { ClineAsk, ToolProgressStatus } from "../../shared/ExtensionMessage"
+
+import { telemetryService } from "../../services/telemetry/TelemetryService"
+
+import { fetchInstructionsTool } from "../tools/fetchInstructionsTool"
+import { listFilesTool } from "../tools/listFilesTool"
+import { readFileTool } from "../tools/readFileTool"
+import { writeToFileTool } from "../tools/writeToFileTool"
+import { applyDiffTool } from "../tools/applyDiffTool"
+import { insertContentTool } from "../tools/insertContentTool"
+import { searchAndReplaceTool } from "../tools/searchAndReplaceTool"
+import { listCodeDefinitionNamesTool } from "../tools/listCodeDefinitionNamesTool"
+import { searchFilesTool } from "../tools/searchFilesTool"
+import { browserActionTool } from "../tools/browserActionTool"
+import { executeCommandTool } from "../tools/executeCommandTool"
+import { useMcpToolTool } from "../tools/useMcpToolTool"
+import { accessMcpResourceTool } from "../tools/accessMcpResourceTool"
+import { askFollowupQuestionTool } from "../tools/askFollowupQuestionTool"
+import { switchModeTool } from "../tools/switchModeTool"
+import { attemptCompletionTool } from "../tools/attemptCompletionTool"
+import { newTaskTool } from "../tools/newTaskTool"
+
+import { checkpointSave } from "../checkpoints"
+
+import { formatResponse } from "../prompts/responses"
+import { validateToolUse } from "../tools/validateToolUse"
+import { Task } from "../task/Task"
+
+/**
+ * Processes and presents assistant message content to the user interface.
+ *
+ * This function is the core message handling system that:
+ * - Sequentially processes content blocks from the assistant's response.
+ * - Displays text content to the user.
+ * - Executes tool use requests with appropriate user approval.
+ * - Manages the flow of conversation by determining when to proceed to the next content block.
+ * - Coordinates file system checkpointing for modified files.
+ * - Controls the conversation state to determine when to continue to the next request.
+ *
+ * The function uses a locking mechanism to prevent concurrent execution and handles
+ * partial content blocks during streaming. It's designed to work with the streaming
+ * API response pattern, where content arrives incrementally and needs to be processed
+ * as it becomes available.
+ */
+
+export async function presentAssistantMessage(cline: Task) {
+ if (cline.abort) {
+ throw new Error(`[Cline#presentAssistantMessage] task ${cline.taskId}.${cline.instanceId} aborted`)
+ }
+
+ if (cline.presentAssistantMessageLocked) {
+ cline.presentAssistantMessageHasPendingUpdates = true
+ return
+ }
+
+ cline.presentAssistantMessageLocked = true
+ cline.presentAssistantMessageHasPendingUpdates = false
+
+ if (cline.currentStreamingContentIndex >= cline.assistantMessageContent.length) {
+ // This may happen if the last content block was completed before
+ // streaming could finish. If streaming is finished, and we're out of
+ // bounds then this means we already presented/executed the last
+ // content block and are ready to continue to next request.
+ if (cline.didCompleteReadingStream) {
+ cline.userMessageContentReady = true
+ }
+
+ cline.presentAssistantMessageLocked = false
+ return
+ }
+
+ const block = cloneDeep(cline.assistantMessageContent[cline.currentStreamingContentIndex]) // need to create copy bc while stream is updating the array, it could be updating the reference block properties too
+
+ switch (block.type) {
+ case "text": {
+ if (cline.didRejectTool || cline.didAlreadyUseTool) {
+ break
+ }
+
+ let content = block.content
+
+ if (content) {
+ // Have to do this for partial and complete since sending
+ // content in thinking tags to markdown renderer will
+ // automatically be removed.
+ // Remove end substrings of (with optional line break
+ // after) and (with optional line break before).
+ // - Needs to be separate since we dont want to remove the line
+ // break before the first tag.
+ // - Needs to happen before the xml parsing below.
+ content = content.replace(/\s?/g, "")
+ content = content.replace(/\s?<\/thinking>/g, "")
+
+ // Remove partial XML tag at the very end of the content (for
+ // tool use and thinking tags), Prevents scrollview from
+ // jumping when tags are automatically removed.
+ const lastOpenBracketIndex = content.lastIndexOf("<")
+
+ if (lastOpenBracketIndex !== -1) {
+ const possibleTag = content.slice(lastOpenBracketIndex)
+
+ // Check if there's a '>' after the last '<' (i.e., if the
+ // tag is complete) (complete thinking and tool tags will
+ // have been removed by now.)
+ const hasCloseBracket = possibleTag.includes(">")
+
+ if (!hasCloseBracket) {
+ // Extract the potential tag name.
+ let tagContent: string
+
+ if (possibleTag.startsWith("")) {
+ tagContent = possibleTag.slice(2).trim()
+ } else {
+ tagContent = possibleTag.slice(1).trim()
+ }
+
+ // Check if tagContent is likely an incomplete tag name
+ // (letters and underscores only).
+ const isLikelyTagName = /^[a-zA-Z_]+$/.test(tagContent)
+
+ // Preemptively remove < or to keep from these
+ // artifacts showing up in chat (also handles closing
+ // thinking tags).
+ const isOpeningOrClosing = possibleTag === "<" || possibleTag === ""
+
+ // If the tag is incomplete and at the end, remove it
+ // from the content.
+ if (isOpeningOrClosing || isLikelyTagName) {
+ content = content.slice(0, lastOpenBracketIndex).trim()
+ }
+ }
+ }
+ }
+
+ await cline.say("text", content, undefined, block.partial)
+ break
+ }
+ case "tool_use":
+ const toolDescription = (): string => {
+ switch (block.name) {
+ case "execute_command":
+ return `[${block.name} for '${block.params.command}']`
+ case "read_file":
+ return `[${block.name} for '${block.params.path}']`
+ case "fetch_instructions":
+ return `[${block.name} for '${block.params.task}']`
+ case "write_to_file":
+ return `[${block.name} for '${block.params.path}']`
+ case "apply_diff":
+ return `[${block.name} for '${block.params.path}']`
+ case "search_files":
+ return `[${block.name} for '${block.params.regex}'${
+ block.params.file_pattern ? ` in '${block.params.file_pattern}'` : ""
+ }]`
+ case "insert_content":
+ return `[${block.name} for '${block.params.path}']`
+ case "search_and_replace":
+ return `[${block.name} for '${block.params.path}']`
+ case "list_files":
+ return `[${block.name} for '${block.params.path}']`
+ case "list_code_definition_names":
+ return `[${block.name} for '${block.params.path}']`
+ case "browser_action":
+ return `[${block.name} for '${block.params.action}']`
+ case "use_mcp_tool":
+ return `[${block.name} for '${block.params.server_name}']`
+ case "access_mcp_resource":
+ return `[${block.name} for '${block.params.server_name}']`
+ case "ask_followup_question":
+ return `[${block.name} for '${block.params.question}']`
+ case "attempt_completion":
+ return `[${block.name}]`
+ case "switch_mode":
+ return `[${block.name} to '${block.params.mode_slug}'${block.params.reason ? ` because: ${block.params.reason}` : ""}]`
+ case "new_task": {
+ const mode = block.params.mode ?? defaultModeSlug
+ const message = block.params.message ?? "(no message)"
+ const modeName = getModeBySlug(mode, customModes)?.name ?? mode
+ return `[${block.name} in ${modeName} mode: '${message}']`
+ }
+ }
+ }
+
+ if (cline.didRejectTool) {
+ // Ignore any tool content after user has rejected tool once.
+ if (!block.partial) {
+ cline.userMessageContent.push({
+ type: "text",
+ text: `Skipping tool ${toolDescription()} due to user rejecting a previous tool.`,
+ })
+ } else {
+ // Partial tool after user rejected a previous tool.
+ cline.userMessageContent.push({
+ type: "text",
+ text: `Tool ${toolDescription()} was interrupted and not executed due to user rejecting a previous tool.`,
+ })
+ }
+
+ break
+ }
+
+ if (cline.didAlreadyUseTool) {
+ // Ignore any content after a tool has already been used.
+ cline.userMessageContent.push({
+ type: "text",
+ text: `Tool [${block.name}] was not executed because a tool has already been used in this message. Only one tool may be used per message. You must assess the first tool's result before proceeding to use the next tool.`,
+ })
+
+ break
+ }
+
+ const pushToolResult = (content: ToolResponse) => {
+ cline.userMessageContent.push({ type: "text", text: `${toolDescription()} Result:` })
+
+ if (typeof content === "string") {
+ cline.userMessageContent.push({ type: "text", text: content || "(tool did not return anything)" })
+ } else {
+ cline.userMessageContent.push(...content)
+ }
+
+ // Once a tool result has been collected, ignore all other tool
+ // uses since we should only ever present one tool result per
+ // message.
+ cline.didAlreadyUseTool = true
+ }
+
+ const askApproval = async (
+ type: ClineAsk,
+ partialMessage?: string,
+ progressStatus?: ToolProgressStatus,
+ ) => {
+ const { response, text, images } = await cline.ask(type, partialMessage, false, progressStatus)
+
+ if (response !== "yesButtonClicked") {
+ // Handle both messageResponse and noButtonClicked with text.
+ if (text) {
+ await cline.say("user_feedback", text, images)
+ pushToolResult(formatResponse.toolResult(formatResponse.toolDeniedWithFeedback(text), images))
+ } else {
+ pushToolResult(formatResponse.toolDenied())
+ }
+ cline.didRejectTool = true
+ return false
+ }
+
+ // Handle yesButtonClicked with text.
+ if (text) {
+ await cline.say("user_feedback", text, images)
+ pushToolResult(formatResponse.toolResult(formatResponse.toolApprovedWithFeedback(text), images))
+ }
+
+ return true
+ }
+
+ const askFinishSubTaskApproval = async () => {
+ // Ask the user to approve this task has completed, and he has
+ // reviewed it, and we can declare task is finished and return
+ // control to the parent task to continue running the rest of
+ // the sub-tasks.
+ const toolMessage = JSON.stringify({ tool: "finishTask" })
+ return await askApproval("tool", toolMessage)
+ }
+
+ const handleError = async (action: string, error: Error) => {
+ const errorString = `Error ${action}: ${JSON.stringify(serializeError(error))}`
+
+ await cline.say(
+ "error",
+ `Error ${action}:\n${error.message ?? JSON.stringify(serializeError(error), null, 2)}`,
+ )
+
+ pushToolResult(formatResponse.toolError(errorString))
+ }
+
+ // If block is partial, remove partial closing tag so its not
+ // presented to user.
+ const removeClosingTag = (tag: ToolParamName, text?: string): string => {
+ if (!block.partial) {
+ return text || ""
+ }
+
+ if (!text) {
+ return ""
+ }
+
+ // This regex dynamically constructs a pattern to match the
+ // closing tag:
+ // - Optionally matches whitespace before the tag.
+ // - Matches '<' or '' optionally followed by any subset of
+ // characters from the tag name.
+ const tagRegex = new RegExp(
+ `\\s?<\/?${tag
+ .split("")
+ .map((char) => `(?:${char})?`)
+ .join("")}$`,
+ "g",
+ )
+
+ return text.replace(tagRegex, "")
+ }
+
+ if (block.name !== "browser_action") {
+ await cline.browserSession.closeBrowser()
+ }
+
+ if (!block.partial) {
+ cline.recordToolUsage(block.name)
+ telemetryService.captureToolUsage(cline.taskId, block.name)
+ }
+
+ // Validate tool use before execution.
+ const { mode, customModes } = (await cline.providerRef.deref()?.getState()) ?? {}
+
+ try {
+ validateToolUse(
+ block.name as ToolName,
+ mode ?? defaultModeSlug,
+ customModes ?? [],
+ { apply_diff: cline.diffEnabled },
+ block.params,
+ )
+ } catch (error) {
+ cline.consecutiveMistakeCount++
+ pushToolResult(formatResponse.toolError(error.message))
+ break
+ }
+
+ // Check for identical consecutive tool calls.
+ if (!block.partial) {
+ // Use the detector to check for repetition, passing the ToolUse
+ // block directly.
+ const repetitionCheck = cline.toolRepetitionDetector.check(block)
+
+ // If execution is not allowed, notify user and break.
+ if (!repetitionCheck.allowExecution && repetitionCheck.askUser) {
+ // Handle repetition similar to mistake_limit_reached pattern.
+ const { response, text, images } = await cline.ask(
+ repetitionCheck.askUser.messageKey as ClineAsk,
+ repetitionCheck.askUser.messageDetail.replace("{toolName}", block.name),
+ )
+
+ if (response === "messageResponse") {
+ // Add user feedback to userContent.
+ cline.userMessageContent.push(
+ {
+ type: "text" as const,
+ text: `Tool repetition limit reached. User feedback: ${text}`,
+ },
+ ...formatResponse.imageBlocks(images),
+ )
+
+ // Add user feedback to chat.
+ await cline.say("user_feedback", text, images)
+
+ // Track tool repetition in telemetry.
+ telemetryService.captureConsecutiveMistakeError(cline.taskId)
+ }
+
+ // Return tool result message about the repetition
+ pushToolResult(
+ formatResponse.toolError(
+ `Tool call repetition limit reached for ${block.name}. Please try a different approach.`,
+ ),
+ )
+ break
+ }
+ }
+
+ switch (block.name) {
+ case "write_to_file":
+ await writeToFileTool(cline, block, askApproval, handleError, pushToolResult, removeClosingTag)
+ break
+ case "apply_diff":
+ await applyDiffTool(cline, block, askApproval, handleError, pushToolResult, removeClosingTag)
+ break
+ case "insert_content":
+ await insertContentTool(cline, block, askApproval, handleError, pushToolResult, removeClosingTag)
+ break
+ case "search_and_replace":
+ await searchAndReplaceTool(cline, block, askApproval, handleError, pushToolResult, removeClosingTag)
+ break
+ case "read_file":
+ await readFileTool(cline, block, askApproval, handleError, pushToolResult, removeClosingTag)
+
+ break
+ case "fetch_instructions":
+ await fetchInstructionsTool(cline, block, askApproval, handleError, pushToolResult)
+ break
+ case "list_files":
+ await listFilesTool(cline, block, askApproval, handleError, pushToolResult, removeClosingTag)
+ break
+ case "list_code_definition_names":
+ await listCodeDefinitionNamesTool(
+ cline,
+ block,
+ askApproval,
+ handleError,
+ pushToolResult,
+ removeClosingTag,
+ )
+ break
+ case "search_files":
+ await searchFilesTool(cline, block, askApproval, handleError, pushToolResult, removeClosingTag)
+ break
+ case "browser_action":
+ await browserActionTool(cline, block, askApproval, handleError, pushToolResult, removeClosingTag)
+ break
+ case "execute_command":
+ await executeCommandTool(cline, block, askApproval, handleError, pushToolResult, removeClosingTag)
+ break
+ case "use_mcp_tool":
+ await useMcpToolTool(cline, block, askApproval, handleError, pushToolResult, removeClosingTag)
+ break
+ case "access_mcp_resource":
+ await accessMcpResourceTool(
+ cline,
+ block,
+ askApproval,
+ handleError,
+ pushToolResult,
+ removeClosingTag,
+ )
+ break
+ case "ask_followup_question":
+ await askFollowupQuestionTool(
+ cline,
+ block,
+ askApproval,
+ handleError,
+ pushToolResult,
+ removeClosingTag,
+ )
+ break
+ case "switch_mode":
+ await switchModeTool(cline, block, askApproval, handleError, pushToolResult, removeClosingTag)
+ break
+ case "new_task":
+ await newTaskTool(cline, block, askApproval, handleError, pushToolResult, removeClosingTag)
+ break
+ case "attempt_completion":
+ await attemptCompletionTool(
+ cline,
+ block,
+ askApproval,
+ handleError,
+ pushToolResult,
+ removeClosingTag,
+ toolDescription,
+ askFinishSubTaskApproval,
+ )
+ break
+ }
+
+ break
+ }
+
+ const recentlyModifiedFiles = cline.fileContextTracker.getAndClearCheckpointPossibleFile()
+
+ if (recentlyModifiedFiles.length > 0) {
+ // TODO: We can track what file changes were made and only
+ // checkpoint those files, this will be save storage.
+ await checkpointSave(cline)
+ }
+
+ // Seeing out of bounds is fine, it means that the next too call is being
+ // built up and ready to add to assistantMessageContent to present.
+ // When you see the UI inactive during this, it means that a tool is
+ // breaking without presenting any UI. For example the write_to_file tool
+ // was breaking when relpath was undefined, and for invalid relpath it never
+ // presented UI.
+ // This needs to be placed here, if not then calling
+ // cline.presentAssistantMessage below would fail (sometimes) since it's
+ // locked.
+ cline.presentAssistantMessageLocked = false
+
+ // NOTE: When tool is rejected, iterator stream is interrupted and it waits
+ // for `userMessageContentReady` to be true. Future calls to present will
+ // skip execution since `didRejectTool` and iterate until `contentIndex` is
+ // set to message length and it sets userMessageContentReady to true itself
+ // (instead of preemptively doing it in iterator).
+ if (!block.partial || cline.didRejectTool || cline.didAlreadyUseTool) {
+ // Block is finished streaming and executing.
+ if (cline.currentStreamingContentIndex === cline.assistantMessageContent.length - 1) {
+ // It's okay that we increment if !didCompleteReadingStream, it'll
+ // just return because out of bounds and as streaming continues it
+ // will call `presentAssitantMessage` if a new block is ready. If
+ // streaming is finished then we set `userMessageContentReady` to
+ // true when out of bounds. This gracefully allows the stream to
+ // continue on and all potential content blocks be presented.
+ // Last block is complete and it is finished executing
+ cline.userMessageContentReady = true // Will allow `pWaitFor` to continue.
+ }
+
+ // Call next block if it exists (if not then read stream will call it
+ // when it's ready).
+ // Need to increment regardless, so when read stream calls this function
+ // again it will be streaming the next block.
+ cline.currentStreamingContentIndex++
+
+ if (cline.currentStreamingContentIndex < cline.assistantMessageContent.length) {
+ // There are already more content blocks to stream, so we'll call
+ // this function ourselves.
+ presentAssistantMessage(cline)
+ return
+ }
+ }
+
+ // Block is partial, but the read stream may have finished.
+ if (cline.presentAssistantMessageHasPendingUpdates) {
+ presentAssistantMessage(cline)
+ }
+}
diff --git a/src/core/checkpoints/index.ts b/src/core/checkpoints/index.ts
new file mode 100644
index 0000000000..3a5c2dde45
--- /dev/null
+++ b/src/core/checkpoints/index.ts
@@ -0,0 +1,296 @@
+import pWaitFor from "p-wait-for"
+import * as vscode from "vscode"
+
+import { Task } from "../task/Task"
+
+import { getWorkspacePath } from "../../utils/path"
+
+import { ClineApiReqInfo } from "../../shared/ExtensionMessage"
+import { getApiMetrics } from "../../shared/getApiMetrics"
+
+import { DIFF_VIEW_URI_SCHEME } from "../../integrations/editor/DiffViewProvider"
+
+import { telemetryService } from "../../services/telemetry/TelemetryService"
+import { CheckpointServiceOptions, RepoPerTaskCheckpointService } from "../../services/checkpoints"
+
+export function getCheckpointService(cline: Task) {
+ if (!cline.enableCheckpoints) {
+ return undefined
+ }
+
+ if (cline.checkpointService) {
+ return cline.checkpointService
+ }
+
+ if (cline.checkpointServiceInitializing) {
+ console.log("[Cline#getCheckpointService] checkpoint service is still initializing")
+ return undefined
+ }
+
+ const provider = cline.providerRef.deref()
+
+ const log = (message: string) => {
+ console.log(message)
+
+ try {
+ provider?.log(message)
+ } catch (err) {
+ // NO-OP
+ }
+ }
+
+ console.log("[Cline#getCheckpointService] initializing checkpoints service")
+
+ try {
+ const workspaceDir = getWorkspacePath()
+
+ if (!workspaceDir) {
+ log("[Cline#getCheckpointService] workspace folder not found, disabling checkpoints")
+ cline.enableCheckpoints = false
+ return undefined
+ }
+
+ const globalStorageDir = provider?.context.globalStorageUri.fsPath
+
+ if (!globalStorageDir) {
+ log("[Cline#getCheckpointService] globalStorageDir not found, disabling checkpoints")
+ cline.enableCheckpoints = false
+ return undefined
+ }
+
+ const options: CheckpointServiceOptions = {
+ taskId: cline.taskId,
+ workspaceDir,
+ shadowDir: globalStorageDir,
+ log,
+ }
+
+ const service = RepoPerTaskCheckpointService.create(options)
+
+ cline.checkpointServiceInitializing = true
+
+ service.on("initialize", () => {
+ log("[Cline#getCheckpointService] service initialized")
+
+ try {
+ const isCheckpointNeeded =
+ typeof cline.clineMessages.find(({ say }) => say === "checkpoint_saved") === "undefined"
+
+ cline.checkpointService = service
+ cline.checkpointServiceInitializing = false
+
+ if (isCheckpointNeeded) {
+ log("[Cline#getCheckpointService] no checkpoints found, saving initial checkpoint")
+ checkpointSave(cline)
+ }
+ } catch (err) {
+ log("[Cline#getCheckpointService] caught error in on('initialize'), disabling checkpoints")
+ cline.enableCheckpoints = false
+ }
+ })
+
+ service.on("checkpoint", ({ isFirst, fromHash: from, toHash: to }) => {
+ try {
+ provider?.postMessageToWebview({ type: "currentCheckpointUpdated", text: to })
+
+ cline
+ .say("checkpoint_saved", to, undefined, undefined, { isFirst, from, to }, undefined, {
+ isNonInteractive: true,
+ })
+ .catch((err) => {
+ log("[Cline#getCheckpointService] caught unexpected error in say('checkpoint_saved')")
+ console.error(err)
+ })
+ } catch (err) {
+ log("[Cline#getCheckpointService] caught unexpected error in on('checkpoint'), disabling checkpoints")
+ console.error(err)
+ cline.enableCheckpoints = false
+ }
+ })
+
+ log("[Cline#getCheckpointService] initializing shadow git")
+
+ service.initShadowGit().catch((err) => {
+ log(
+ `[Cline#getCheckpointService] caught unexpected error in initShadowGit, disabling checkpoints (${err.message})`,
+ )
+
+ console.error(err)
+ cline.enableCheckpoints = false
+ })
+
+ return service
+ } catch (err) {
+ log("[Cline#getCheckpointService] caught unexpected error, disabling checkpoints")
+ cline.enableCheckpoints = false
+ return undefined
+ }
+}
+
+async function getInitializedCheckpointService(
+ cline: Task,
+ { interval = 250, timeout = 15_000 }: { interval?: number; timeout?: number } = {},
+) {
+ const service = getCheckpointService(cline)
+
+ if (!service || service.isInitialized) {
+ return service
+ }
+
+ try {
+ await pWaitFor(
+ () => {
+ console.log("[Cline#getCheckpointService] waiting for service to initialize")
+ return service.isInitialized
+ },
+ { interval, timeout },
+ )
+
+ return service
+ } catch (err) {
+ return undefined
+ }
+}
+
+export async function checkpointSave(cline: Task) {
+ const service = getCheckpointService(cline)
+
+ if (!service) {
+ return
+ }
+
+ if (!service.isInitialized) {
+ const provider = cline.providerRef.deref()
+ provider?.log("[checkpointSave] checkpoints didn't initialize in time, disabling checkpoints for this task")
+ cline.enableCheckpoints = false
+ return
+ }
+
+ telemetryService.captureCheckpointCreated(cline.taskId)
+
+ // Start the checkpoint process in the background.
+ return service.saveCheckpoint(`Task: ${cline.taskId}, Time: ${Date.now()}`).catch((err) => {
+ console.error("[Cline#checkpointSave] caught unexpected error, disabling checkpoints", err)
+ cline.enableCheckpoints = false
+ })
+}
+
+export type CheckpointRestoreOptions = {
+ ts: number
+ commitHash: string
+ mode: "preview" | "restore"
+}
+
+export async function checkpointRestore(cline: Task, { ts, commitHash, mode }: CheckpointRestoreOptions) {
+ const service = await getInitializedCheckpointService(cline)
+
+ if (!service) {
+ return
+ }
+
+ const index = cline.clineMessages.findIndex((m) => m.ts === ts)
+
+ if (index === -1) {
+ return
+ }
+
+ const provider = cline.providerRef.deref()
+
+ try {
+ await service.restoreCheckpoint(commitHash)
+ telemetryService.captureCheckpointRestored(cline.taskId)
+ await provider?.postMessageToWebview({ type: "currentCheckpointUpdated", text: commitHash })
+
+ if (mode === "restore") {
+ await cline.overwriteApiConversationHistory(cline.apiConversationHistory.filter((m) => !m.ts || m.ts < ts))
+
+ const deletedMessages = cline.clineMessages.slice(index + 1)
+
+ const { totalTokensIn, totalTokensOut, totalCacheWrites, totalCacheReads, totalCost } = getApiMetrics(
+ cline.combineMessages(deletedMessages),
+ )
+
+ await cline.overwriteClineMessages(cline.clineMessages.slice(0, index + 1))
+
+ // TODO: Verify that this is working as expected.
+ await cline.say(
+ "api_req_deleted",
+ JSON.stringify({
+ tokensIn: totalTokensIn,
+ tokensOut: totalTokensOut,
+ cacheWrites: totalCacheWrites,
+ cacheReads: totalCacheReads,
+ cost: totalCost,
+ } satisfies ClineApiReqInfo),
+ )
+ }
+
+ // The task is already cancelled by the provider beforehand, but we
+ // need to re-init to get the updated messages.
+ //
+ // This was take from Cline's implementation of the checkpoints
+ // feature. The cline instance will hang if we don't cancel twice,
+ // so this is currently necessary, but it seems like a complicated
+ // and hacky solution to a problem that I don't fully understand.
+ // I'd like to revisit this in the future and try to improve the
+ // task flow and the communication between the webview and the
+ // Cline instance.
+ provider?.cancelTask()
+ } catch (err) {
+ provider?.log("[checkpointRestore] disabling checkpoints for this task")
+ cline.enableCheckpoints = false
+ }
+}
+
+export type CheckpointDiffOptions = {
+ ts: number
+ previousCommitHash?: string
+ commitHash: string
+ mode: "full" | "checkpoint"
+}
+
+export async function checkpointDiff(cline: Task, { ts, previousCommitHash, commitHash, mode }: CheckpointDiffOptions) {
+ const service = await getInitializedCheckpointService(cline)
+
+ if (!service) {
+ return
+ }
+
+ telemetryService.captureCheckpointDiffed(cline.taskId)
+
+ if (!previousCommitHash && mode === "checkpoint") {
+ const previousCheckpoint = cline.clineMessages
+ .filter(({ say }) => say === "checkpoint_saved")
+ .sort((a, b) => b.ts - a.ts)
+ .find((message) => message.ts < ts)
+
+ previousCommitHash = previousCheckpoint?.text
+ }
+
+ try {
+ const changes = await service.getDiff({ from: previousCommitHash, to: commitHash })
+
+ if (!changes?.length) {
+ vscode.window.showInformationMessage("No changes found.")
+ return
+ }
+
+ await vscode.commands.executeCommand(
+ "vscode.changes",
+ mode === "full" ? "Changes since task started" : "Changes since previous checkpoint",
+ changes.map((change) => [
+ vscode.Uri.file(change.paths.absolute),
+ vscode.Uri.parse(`${DIFF_VIEW_URI_SCHEME}:${change.paths.relative}`).with({
+ query: Buffer.from(change.content.before ?? "").toString("base64"),
+ }),
+ vscode.Uri.parse(`${DIFF_VIEW_URI_SCHEME}:${change.paths.relative}`).with({
+ query: Buffer.from(change.content.after ?? "").toString("base64"),
+ }),
+ ]),
+ )
+ } catch (err) {
+ const provider = cline.providerRef.deref()
+ provider?.log("[checkpointDiff] disabling checkpoints for this task")
+ cline.enableCheckpoints = false
+ }
+}
diff --git a/src/core/condense/__tests__/index.test.ts b/src/core/condense/__tests__/index.test.ts
new file mode 100644
index 0000000000..86afe2a8c4
--- /dev/null
+++ b/src/core/condense/__tests__/index.test.ts
@@ -0,0 +1,279 @@
+import { describe, expect, it, jest, beforeEach } from "@jest/globals"
+import { ApiHandler } from "../../../api"
+import { ApiMessage } from "../../task-persistence/apiMessages"
+import { maybeRemoveImageBlocks } from "../../../api/transform/image-cleaning"
+import { summarizeConversation, getMessagesSinceLastSummary, N_MESSAGES_TO_KEEP } from "../index"
+
+// Mock dependencies
+jest.mock("../../../api/transform/image-cleaning", () => ({
+ maybeRemoveImageBlocks: jest.fn((messages: ApiMessage[], _apiHandler: ApiHandler) => [...messages]),
+}))
+
+describe("getMessagesSinceLastSummary", () => {
+ it("should return all messages when there is no summary", () => {
+ const messages: ApiMessage[] = [
+ { role: "user", content: "Hello", ts: 1 },
+ { role: "assistant", content: "Hi there", ts: 2 },
+ { role: "user", content: "How are you?", ts: 3 },
+ ]
+
+ const result = getMessagesSinceLastSummary(messages)
+ expect(result).toEqual(messages)
+ })
+
+ it("should return messages since the last summary", () => {
+ const messages: ApiMessage[] = [
+ { role: "user", content: "Hello", ts: 1 },
+ { role: "assistant", content: "Hi there", ts: 2 },
+ { role: "assistant", content: "Summary of conversation", ts: 3, isSummary: true },
+ { role: "user", content: "How are you?", ts: 4 },
+ { role: "assistant", content: "I'm good", ts: 5 },
+ ]
+
+ const result = getMessagesSinceLastSummary(messages)
+ expect(result).toEqual([
+ { role: "assistant", content: "Summary of conversation", ts: 3, isSummary: true },
+ { role: "user", content: "How are you?", ts: 4 },
+ { role: "assistant", content: "I'm good", ts: 5 },
+ ])
+ })
+
+ it("should handle multiple summary messages and return since the last one", () => {
+ const messages: ApiMessage[] = [
+ { role: "user", content: "Hello", ts: 1 },
+ { role: "assistant", content: "First summary", ts: 2, isSummary: true },
+ { role: "user", content: "How are you?", ts: 3 },
+ { role: "assistant", content: "Second summary", ts: 4, isSummary: true },
+ { role: "user", content: "What's new?", ts: 5 },
+ ]
+
+ const result = getMessagesSinceLastSummary(messages)
+ expect(result).toEqual([
+ { role: "assistant", content: "Second summary", ts: 4, isSummary: true },
+ { role: "user", content: "What's new?", ts: 5 },
+ ])
+ })
+
+ it("should handle empty messages array", () => {
+ const result = getMessagesSinceLastSummary([])
+ expect(result).toEqual([])
+ })
+})
+
+describe("summarizeConversation", () => {
+ // Mock ApiHandler
+ let mockApiHandler: ApiHandler
+ let mockStream: AsyncGenerator
+
+ beforeEach(() => {
+ // Reset mocks
+ jest.clearAllMocks()
+
+ // Setup mock stream with usage information
+ mockStream = (async function* () {
+ yield { type: "text" as const, text: "This is " }
+ yield { type: "text" as const, text: "a summary" }
+ yield { type: "usage" as const, totalCost: 0.05, outputTokens: 150 }
+ })()
+
+ // Setup mock API handler
+ mockApiHandler = {
+ createMessage: jest.fn().mockReturnValue(mockStream),
+ countTokens: jest.fn().mockImplementation(() => Promise.resolve(100)),
+ getModel: jest.fn().mockReturnValue({
+ id: "test-model",
+ info: {
+ contextWindow: 8000,
+ supportsImages: true,
+ supportsComputerUse: true,
+ supportsVision: true,
+ maxTokens: 4000,
+ supportsPromptCache: true,
+ maxCachePoints: 10,
+ minTokensPerCachePoint: 100,
+ cachableFields: ["system", "messages"],
+ },
+ }),
+ } as unknown as ApiHandler
+ })
+
+ // Default system prompt for tests
+ const defaultSystemPrompt = "You are a helpful assistant."
+
+ it("should not summarize when there are not enough messages", async () => {
+ const messages: ApiMessage[] = [
+ { role: "user", content: "Hello", ts: 1 },
+ { role: "assistant", content: "Hi there", ts: 2 },
+ ]
+
+ const result = await summarizeConversation(messages, mockApiHandler, defaultSystemPrompt)
+ expect(result.messages).toEqual(messages)
+ expect(result.cost).toBe(0)
+ expect(result.summary).toBe("")
+ expect(result.newContextTokens).toBeUndefined()
+ expect(mockApiHandler.createMessage).not.toHaveBeenCalled()
+ })
+
+ it("should not summarize when there was a recent summary", async () => {
+ const messages: ApiMessage[] = [
+ { role: "user", content: "Hello", ts: 1 },
+ { role: "assistant", content: "Hi there", ts: 2 },
+ { role: "user", content: "How are you?", ts: 3 },
+ { role: "assistant", content: "I'm good", ts: 4 },
+ { role: "user", content: "What's new?", ts: 5 },
+ { role: "assistant", content: "Not much", ts: 6, isSummary: true }, // Recent summary
+ { role: "user", content: "Tell me more", ts: 7 },
+ ]
+
+ const result = await summarizeConversation(messages, mockApiHandler, defaultSystemPrompt)
+ expect(result.messages).toEqual(messages)
+ expect(result.cost).toBe(0)
+ expect(result.summary).toBe("")
+ expect(result.newContextTokens).toBeUndefined()
+ expect(mockApiHandler.createMessage).not.toHaveBeenCalled()
+ })
+
+ it("should summarize conversation and insert summary message", async () => {
+ const messages: ApiMessage[] = [
+ { role: "user", content: "Hello", ts: 1 },
+ { role: "assistant", content: "Hi there", ts: 2 },
+ { role: "user", content: "How are you?", ts: 3 },
+ { role: "assistant", content: "I'm good", ts: 4 },
+ { role: "user", content: "What's new?", ts: 5 },
+ { role: "assistant", content: "Not much", ts: 6 },
+ { role: "user", content: "Tell me more", ts: 7 },
+ ]
+
+ const result = await summarizeConversation(messages, mockApiHandler, defaultSystemPrompt)
+
+ // Check that the API was called correctly
+ expect(mockApiHandler.createMessage).toHaveBeenCalled()
+ expect(maybeRemoveImageBlocks).toHaveBeenCalled()
+
+ // Verify the structure of the result
+ // The result should be: original messages (except last N) + summary + last N messages
+ expect(result.messages.length).toBe(messages.length + 1) // Original + summary
+
+ // Check that the summary message was inserted correctly
+ const summaryMessage = result.messages[result.messages.length - N_MESSAGES_TO_KEEP - 1]
+ expect(summaryMessage.role).toBe("assistant")
+ expect(summaryMessage.content).toBe("This is a summary")
+ expect(summaryMessage.isSummary).toBe(true)
+
+ // Check that the last N_MESSAGES_TO_KEEP messages are preserved
+ const lastMessages = messages.slice(-N_MESSAGES_TO_KEEP)
+ expect(result.messages.slice(-N_MESSAGES_TO_KEEP)).toEqual(lastMessages)
+
+ // Check the cost and token counts
+ expect(result.cost).toBe(0.05)
+ expect(result.summary).toBe("This is a summary")
+ expect(result.newContextTokens).toBe(250) // 150 output tokens + 100 from countTokens
+ })
+
+ it("should handle empty summary response", async () => {
+ // We need enough messages to trigger summarization
+ const messages: ApiMessage[] = [
+ { role: "user", content: "Hello", ts: 1 },
+ { role: "assistant", content: "Hi there", ts: 2 },
+ { role: "user", content: "How are you?", ts: 3 },
+ { role: "assistant", content: "I'm good", ts: 4 },
+ { role: "user", content: "What's new?", ts: 5 },
+ { role: "assistant", content: "Not much", ts: 6 },
+ { role: "user", content: "Tell me more", ts: 7 },
+ ]
+
+ // Mock console.warn before we call the function
+ const originalWarn = console.warn
+ const mockWarn = jest.fn()
+ console.warn = mockWarn
+
+ // Setup empty summary response with usage information
+ const emptyStream = (async function* () {
+ yield { type: "text" as const, text: "" }
+ yield { type: "usage" as const, totalCost: 0.02, outputTokens: 0 }
+ })()
+
+ // Create a new mock for createMessage that returns empty stream
+ const createMessageMock = jest.fn().mockReturnValue(emptyStream)
+ mockApiHandler.createMessage = createMessageMock as any
+
+ // We need to mock maybeRemoveImageBlocks to return the expected messages
+ ;(maybeRemoveImageBlocks as jest.Mock).mockImplementationOnce((messages: any) => {
+ return messages.map(({ role, content }: { role: string; content: any }) => ({ role, content }))
+ })
+
+ const result = await summarizeConversation(messages, mockApiHandler, defaultSystemPrompt)
+
+ // Should return original messages when summary is empty
+ expect(result.messages).toEqual(messages)
+ expect(result.cost).toBe(0.02)
+ expect(result.summary).toBe("")
+ expect(mockWarn).toHaveBeenCalledWith("Received empty summary from API")
+
+ // Restore console.warn
+ console.warn = originalWarn
+ })
+
+ it("should correctly format the request to the API", async () => {
+ const messages: ApiMessage[] = [
+ { role: "user", content: "Hello", ts: 1 },
+ { role: "assistant", content: "Hi there", ts: 2 },
+ { role: "user", content: "How are you?", ts: 3 },
+ { role: "assistant", content: "I'm good", ts: 4 },
+ { role: "user", content: "What's new?", ts: 5 },
+ { role: "assistant", content: "Not much", ts: 6 },
+ { role: "user", content: "Tell me more", ts: 7 },
+ ]
+
+ await summarizeConversation(messages, mockApiHandler, defaultSystemPrompt)
+
+ // Verify the final request message
+ const expectedFinalMessage = {
+ role: "user",
+ content: "Summarize the conversation so far, as described in the prompt instructions.",
+ }
+
+ // Verify that createMessage was called with the correct prompt
+ expect(mockApiHandler.createMessage).toHaveBeenCalledWith(
+ expect.stringContaining("Your task is to create a detailed summary of the conversation"),
+ expect.any(Array),
+ )
+
+ // Check that maybeRemoveImageBlocks was called with the correct messages
+ const mockCallArgs = (maybeRemoveImageBlocks as jest.Mock).mock.calls[0][0] as any[]
+ expect(mockCallArgs[mockCallArgs.length - 1]).toEqual(expectedFinalMessage)
+ })
+
+ it("should calculate newContextTokens correctly with systemPrompt", async () => {
+ const messages: ApiMessage[] = [
+ { role: "user", content: "Hello", ts: 1 },
+ { role: "assistant", content: "Hi there", ts: 2 },
+ { role: "user", content: "How are you?", ts: 3 },
+ { role: "assistant", content: "I'm good", ts: 4 },
+ { role: "user", content: "What's new?", ts: 5 },
+ { role: "assistant", content: "Not much", ts: 6 },
+ { role: "user", content: "Tell me more", ts: 7 },
+ ]
+
+ const systemPrompt = "You are a helpful assistant."
+
+ // Create a stream with usage information
+ const streamWithUsage = (async function* () {
+ yield { type: "text" as const, text: "This is a summary with system prompt" }
+ yield { type: "usage" as const, totalCost: 0.06, outputTokens: 200 }
+ })()
+
+ // Override the mock for this test
+ mockApiHandler.createMessage = jest.fn().mockReturnValue(streamWithUsage) as any
+
+ const result = await summarizeConversation(messages, mockApiHandler, systemPrompt)
+
+ // Verify that countTokens was called with the correct messages including system prompt
+ expect(mockApiHandler.countTokens).toHaveBeenCalled()
+
+ // Check the newContextTokens calculation includes system prompt
+ expect(result.newContextTokens).toBe(300) // 200 output tokens + 100 from countTokens
+ expect(result.cost).toBe(0.06)
+ expect(result.summary).toBe("This is a summary with system prompt")
+ })
+})
diff --git a/src/core/condense/index.ts b/src/core/condense/index.ts
new file mode 100644
index 0000000000..c5ab931069
--- /dev/null
+++ b/src/core/condense/index.ts
@@ -0,0 +1,134 @@
+import Anthropic from "@anthropic-ai/sdk"
+import { ApiHandler } from "../../api"
+import { ApiMessage } from "../task-persistence/apiMessages"
+import { maybeRemoveImageBlocks } from "../../api/transform/image-cleaning"
+
+export const N_MESSAGES_TO_KEEP = 3
+
+const SUMMARY_PROMPT = `\
+Your task is to create a detailed summary of the conversation so far, paying close attention to the user's explicit requests and your previous actions.
+This summary should be thorough in capturing technical details, code patterns, and architectural decisions that would be essential for continuing with the conversation and supporting any continuing tasks.
+
+Your summary should be structured as follows:
+Context: The context to continue the conversation with. If applicable based on the current task, this should include:
+ 1. Previous Conversation: High level details about what was discussed throughout the entire conversation with the user. This should be written to allow someone to be able to follow the general overarching conversation flow.
+ 2. Current Work: Describe in detail what was being worked on prior to this request to summarize the conversation. Pay special attention to the more recent messages in the conversation.
+ 3. Key Technical Concepts: List all important technical concepts, technologies, coding conventions, and frameworks discussed, which might be relevant for continuing with this work.
+ 4. Relevant Files and Code: If applicable, enumerate specific files and code sections examined, modified, or created for the task continuation. Pay special attention to the most recent messages and changes.
+ 5. Problem Solving: Document problems solved thus far and any ongoing troubleshooting efforts.
+ 6. Pending Tasks and Next Steps: Outline all pending tasks that you have explicitly been asked to work on, as well as list the next steps you will take for all outstanding work, if applicable. Include code snippets where they add clarity. For any next steps, include direct quotes from the most recent conversation showing exactly what task you were working on and where you left off. This should be verbatim to ensure there's no information loss in context between tasks.
+
+Example summary structure:
+1. Previous Conversation:
+ [Detailed description]
+2. Current Work:
+ [Detailed description]
+3. Key Technical Concepts:
+ - [Concept 1]
+ - [Concept 2]
+ - [...]
+4. Relevant Files and Code:
+ - [File Name 1]
+ - [Summary of why this file is important]
+ - [Summary of the changes made to this file, if any]
+ - [Important Code Snippet]
+ - [File Name 2]
+ - [Important Code Snippet]
+ - [...]
+5. Problem Solving:
+ [Detailed description]
+6. Pending Tasks and Next Steps:
+ - [Task 1 details & next steps]
+ - [Task 2 details & next steps]
+ - [...]
+
+Output only the summary of the conversation so far, without any additional commentary or explanation.
+`
+
+export type SummarizeResponse = {
+ messages: ApiMessage[] // The messages after summarization
+ summary: string // The summary text; empty string for no summary
+ cost: number // The cost of the summarization operation
+ newContextTokens?: number // The number of tokens in the context for the next API request
+}
+
+/**
+ * Summarizes the conversation messages using an LLM call
+ *
+ * @param {ApiMessage[]} messages - The conversation messages
+ * @param {ApiHandler} apiHandler - The API handler to use for token counting.
+ * @param {string} systemPrompt - The system prompt for API requests, which should be considered in the context token count
+ * @returns {SummarizeResponse} - The result of the summarization operation (see above)
+ */
+export async function summarizeConversation(
+ messages: ApiMessage[],
+ apiHandler: ApiHandler,
+ systemPrompt: string,
+): Promise {
+ const response: SummarizeResponse = { messages, cost: 0, summary: "" }
+ const messagesToSummarize = getMessagesSinceLastSummary(messages.slice(0, -N_MESSAGES_TO_KEEP))
+ if (messagesToSummarize.length <= 1) {
+ return response // Not enough messages to warrant a summary
+ }
+ const keepMessages = messages.slice(-N_MESSAGES_TO_KEEP)
+ // Check if there's a recent summary in the messages we're keeping
+ const recentSummaryExists = keepMessages.some((message) => message.isSummary)
+ if (recentSummaryExists) {
+ return response // We recently summarized these messages; it's too soon to summarize again.
+ }
+ const finalRequestMessage: Anthropic.MessageParam = {
+ role: "user",
+ content: "Summarize the conversation so far, as described in the prompt instructions.",
+ }
+ const requestMessages = maybeRemoveImageBlocks([...messagesToSummarize, finalRequestMessage], apiHandler).map(
+ ({ role, content }) => ({ role, content }),
+ )
+ // Note: this doesn't need to be a stream, consider using something like apiHandler.completePrompt
+ const stream = apiHandler.createMessage(SUMMARY_PROMPT, requestMessages)
+ let summary = ""
+ let cost = 0
+ let outputTokens = 0
+ for await (const chunk of stream) {
+ if (chunk.type === "text") {
+ summary += chunk.text
+ } else if (chunk.type === "usage") {
+ // Record final usage chunk only
+ cost = chunk.totalCost ?? 0
+ outputTokens = chunk.outputTokens ?? 0
+ }
+ }
+ summary = summary.trim()
+ if (summary.length === 0) {
+ console.warn("Received empty summary from API")
+ return { ...response, cost }
+ }
+ const summaryMessage: ApiMessage = {
+ role: "assistant",
+ content: summary,
+ ts: keepMessages[0].ts,
+ isSummary: true,
+ }
+ const newMessages = [...messages.slice(0, -N_MESSAGES_TO_KEEP), summaryMessage, ...keepMessages]
+
+ // Count the tokens in the context for the next API request
+ // We only estimate the tokens in summaryMesage if outputTokens is 0, otherwise we use outputTokens
+ const systemPromptMessage: ApiMessage = { role: "user", content: systemPrompt }
+ const contextMessages = outputTokens
+ ? [systemPromptMessage, ...keepMessages]
+ : [systemPromptMessage, summaryMessage, ...keepMessages]
+ const contextBlocks = contextMessages.flatMap((message) =>
+ typeof message.content === "string" ? [{ text: message.content, type: "text" as const }] : message.content,
+ )
+ const newContextTokens = outputTokens + (await apiHandler.countTokens(contextBlocks))
+ return { messages: newMessages, summary, cost, newContextTokens }
+}
+
+/* Returns the list of all messages since the last summary message, including the summary. Returns all messages if there is no summary. */
+export function getMessagesSinceLastSummary(messages: ApiMessage[]): ApiMessage[] {
+ let lastSummaryIndexReverse = [...messages].reverse().findIndex((message) => message.isSummary)
+ if (lastSummaryIndexReverse === -1) {
+ return messages
+ }
+ const lastSummaryIndex = messages.length - lastSummaryIndexReverse - 1
+ return messages.slice(lastSummaryIndex)
+}
diff --git a/src/core/config/ContextProxy.ts b/src/core/config/ContextProxy.ts
index 6b017503d5..c2373ccad2 100644
--- a/src/core/config/ContextProxy.ts
+++ b/src/core/config/ContextProxy.ts
@@ -192,6 +192,16 @@ export class ContextProxy {
// If a value is not present in the new configuration, then it is assumed
// that the setting's value should be `undefined` and therefore we
// need to remove it from the state cache if it exists.
+
+ // Ensure openAiHeaders is always an object even when empty
+ // This is critical for proper serialization/deserialization through IPC
+ if (values.openAiHeaders !== undefined) {
+ // Check if it's empty or null
+ if (!values.openAiHeaders || Object.keys(values.openAiHeaders).length === 0) {
+ values.openAiHeaders = {}
+ }
+ }
+
await this.setValues({
...PROVIDER_SETTINGS_KEYS.filter((key) => !isSecretStateKey(key))
.filter((key) => !!this.stateCache[key])
diff --git a/src/core/config/CustomModesManager.ts b/src/core/config/CustomModesManager.ts
index efa3366aee..f763ff0abb 100644
--- a/src/core/config/CustomModesManager.ts
+++ b/src/core/config/CustomModesManager.ts
@@ -7,13 +7,19 @@ import { fileExistsAtPath } from "../../utils/fs"
import { arePathsEqual, getWorkspacePath } from "../../utils/path"
import { logger } from "../../utils/logging"
import { GlobalFileNames } from "../../shared/globalFileNames"
+import * as yaml from "yaml"
+import { ensureSettingsDirectoryExists } from "../../utils/globalContext"
const ROOMODES_FILENAME = ".roomodes"
export class CustomModesManager {
+ private static readonly cacheTTL = 10_000
+
private disposables: vscode.Disposable[] = []
private isWriting = false
private writeQueue: Array<() => Promise> = []
+ private cachedModes: ModeConfig[] | null = null
+ private cachedAt: number = 0
constructor(
private readonly context: vscode.ExtensionContext,
@@ -25,6 +31,7 @@ export class CustomModesManager {
private async queueWrite(operation: () => Promise): Promise {
this.writeQueue.push(operation)
+
if (!this.isWriting) {
await this.processWriteQueue()
}
@@ -36,9 +43,11 @@ export class CustomModesManager {
}
this.isWriting = true
+
try {
while (this.writeQueue.length > 0) {
const operation = this.writeQueue.shift()
+
if (operation) {
await operation()
}
@@ -50,9 +59,11 @@ export class CustomModesManager {
private async getWorkspaceRoomodes(): Promise {
const workspaceFolders = vscode.workspace.workspaceFolders
+
if (!workspaceFolders || workspaceFolders.length === 0) {
return undefined
}
+
const workspaceRoot = getWorkspacePath()
const roomodesPath = path.join(workspaceRoot, ROOMODES_FILENAME)
const exists = await fileExistsAtPath(roomodesPath)
@@ -62,7 +73,7 @@ export class CustomModesManager {
private async loadModesFromFile(filePath: string): Promise {
try {
const content = await fs.readFile(filePath, "utf-8")
- const settings = JSON.parse(content)
+ const settings = yaml.parse(content)
const result = customModesSettingsSchema.safeParse(settings)
if (!result.success) {
return []
@@ -73,10 +84,7 @@ export class CustomModesManager {
const source = isRoomodes ? ("project" as const) : ("global" as const)
// Add source to each mode
- return result.data.customModes.map((mode) => ({
- ...mode,
- source,
- }))
+ return result.data.customModes.map((mode) => ({ ...mode, source }))
} catch (error) {
const errorMsg = `Failed to load modes from ${filePath}: ${error instanceof Error ? error.message : String(error)}`
console.error(`[CustomModesManager] ${errorMsg}`)
@@ -92,10 +100,7 @@ export class CustomModesManager {
for (const mode of projectModes) {
if (!slugs.has(mode.slug)) {
slugs.add(mode.slug)
- merged.push({
- ...mode,
- source: "project",
- })
+ merged.push({ ...mode, source: "project" })
}
}
@@ -103,25 +108,22 @@ export class CustomModesManager {
for (const mode of globalModes) {
if (!slugs.has(mode.slug)) {
slugs.add(mode.slug)
- merged.push({
- ...mode,
- source: "global",
- })
+ merged.push({ ...mode, source: "global" })
}
}
return merged
}
- async getCustomModesFilePath(): Promise {
- const settingsDir = await this.ensureSettingsDirectoryExists()
+ public async getCustomModesFilePath(): Promise {
+ const settingsDir = await ensureSettingsDirectoryExists(this.context)
const filePath = path.join(settingsDir, GlobalFileNames.customModes)
const fileExists = await fileExistsAtPath(filePath)
+
if (!fileExists) {
- await this.queueWrite(async () => {
- await fs.writeFile(filePath, JSON.stringify({ customModes: [] }, null, 2))
- })
+ await this.queueWrite(() => fs.writeFile(filePath, yaml.stringify({ customModes: [] })))
}
+
return filePath
}
@@ -133,12 +135,14 @@ export class CustomModesManager {
vscode.workspace.onDidSaveTextDocument(async (document) => {
if (arePathsEqual(document.uri.fsPath, settingsPath)) {
const content = await fs.readFile(settingsPath, "utf-8")
+
const errorMessage =
- "Invalid custom modes format. Please ensure your settings follow the correct JSON format."
+ "Invalid custom modes format. Please ensure your settings follow the correct YAML format."
let config: any
+
try {
- config = JSON.parse(content)
+ config = yaml.parse(content)
} catch (error) {
console.error(error)
vscode.window.showErrorMessage(errorMessage)
@@ -159,6 +163,7 @@ export class CustomModesManager {
// Merge modes from both sources (.roomodes takes precedence)
const mergedModes = await this.mergeCustomModes(roomodesModes, result.data.customModes)
await this.context.globalState.update("customModes", mergedModes)
+ this.clearCache()
await this.onUpdate()
}
}),
@@ -166,6 +171,7 @@ export class CustomModesManager {
// Watch .roomodes file if it exists
const roomodesPath = await this.getWorkspaceRoomodes()
+
if (roomodesPath) {
this.disposables.push(
vscode.workspace.onDidSaveTextDocument(async (document) => {
@@ -175,6 +181,7 @@ export class CustomModesManager {
// .roomodes takes precedence
const mergedModes = await this.mergeCustomModes(roomodesModes, settingsModes)
await this.context.globalState.update("customModes", mergedModes)
+ this.clearCache()
await this.onUpdate()
}
}),
@@ -182,32 +189,39 @@ export class CustomModesManager {
}
}
- async getCustomModes(): Promise {
- // Get modes from settings file
+ public async getCustomModes(): Promise {
+ // Check if we have a valid cached result.
+ const now = Date.now()
+
+ if (this.cachedModes && now - this.cachedAt < CustomModesManager.cacheTTL) {
+ return this.cachedModes
+ }
+
+ // Get modes from settings file.
const settingsPath = await this.getCustomModesFilePath()
const settingsModes = await this.loadModesFromFile(settingsPath)
- // Get modes from .roomodes if it exists
+ // Get modes from .roomodes if it exists.
const roomodesPath = await this.getWorkspaceRoomodes()
const roomodesModes = roomodesPath ? await this.loadModesFromFile(roomodesPath) : []
- // Create maps to store modes by source
+ // Create maps to store modes by source.
const projectModes = new Map()
const globalModes = new Map()
- // Add project modes (they take precedence)
+ // Add project modes (they take precedence).
for (const mode of roomodesModes) {
projectModes.set(mode.slug, { ...mode, source: "project" as const })
}
- // Add global modes
+ // Add global modes.
for (const mode of settingsModes) {
if (!projectModes.has(mode.slug)) {
globalModes.set(mode.slug, { ...mode, source: "global" as const })
}
}
- // Combine modes in the correct order: project modes first, then global modes
+ // Combine modes in the correct order: project modes first, then global modes.
const mergedModes = [
...roomodesModes.map((mode) => ({ ...mode, source: "project" as const })),
...settingsModes
@@ -216,22 +230,30 @@ export class CustomModesManager {
]
await this.context.globalState.update("customModes", mergedModes)
+
+ this.cachedModes = mergedModes
+ this.cachedAt = now
+
return mergedModes
}
- async updateCustomMode(slug: string, config: ModeConfig): Promise {
+
+ public async updateCustomMode(slug: string, config: ModeConfig): Promise {
try {
const isProjectMode = config.source === "project"
let targetPath: string
if (isProjectMode) {
const workspaceFolders = vscode.workspace.workspaceFolders
+
if (!workspaceFolders || workspaceFolders.length === 0) {
logger.error("Failed to update project mode: No workspace folder found", { slug })
throw new Error("No workspace folder found for project-specific mode")
}
+
const workspaceRoot = getWorkspacePath()
targetPath = path.join(workspaceRoot, ROOMODES_FILENAME)
const exists = await fileExistsAtPath(targetPath)
+
logger.info(`${exists ? "Updating" : "Creating"} project mode in ${ROOMODES_FILENAME}`, {
slug,
workspace: workspaceRoot,
@@ -241,7 +263,7 @@ export class CustomModesManager {
}
await this.queueWrite(async () => {
- // Ensure source is set correctly based on target file
+ // Ensure source is set correctly based on target file.
const modeWithSource = {
...config,
source: isProjectMode ? ("project" as const) : ("global" as const),
@@ -253,6 +275,7 @@ export class CustomModesManager {
return updatedModes
})
+ this.clearCache()
await this.refreshMergedState()
})
} catch (error) {
@@ -261,24 +284,28 @@ export class CustomModesManager {
vscode.window.showErrorMessage(`Failed to update custom mode: ${errorMessage}`)
}
}
+
private async updateModesInFile(filePath: string, operation: (modes: ModeConfig[]) => ModeConfig[]): Promise {
let content = "{}"
+
try {
content = await fs.readFile(filePath, "utf-8")
} catch (error) {
- // File might not exist yet
- content = JSON.stringify({ customModes: [] })
+ // File might not exist yet.
+ content = yaml.stringify({ customModes: [] })
}
let settings
+
try {
- settings = JSON.parse(content)
+ settings = yaml.parse(content)
} catch (error) {
- console.error(`[CustomModesManager] Failed to parse JSON from ${filePath}:`, error)
+ console.error(`[CustomModesManager] Failed to parse YAML from ${filePath}:`, error)
settings = { customModes: [] }
}
+
settings.customModes = operation(settings.customModes || [])
- await fs.writeFile(filePath, JSON.stringify(settings, null, 2), "utf-8")
+ await fs.writeFile(filePath, yaml.stringify(settings), "utf-8")
}
private async refreshMergedState(): Promise {
@@ -290,10 +317,13 @@ export class CustomModesManager {
const mergedModes = await this.mergeCustomModes(roomodesModes, settingsModes)
await this.context.globalState.update("customModes", mergedModes)
+
+ this.clearCache()
+
await this.onUpdate()
}
- async deleteCustomMode(slug: string): Promise {
+ public async deleteCustomMode(slug: string): Promise {
try {
const settingsPath = await this.getCustomModesFilePath()
const roomodesPath = await this.getWorkspaceRoomodes()
@@ -320,6 +350,8 @@ export class CustomModesManager {
await this.updateModesInFile(settingsPath, (modes) => modes.filter((m) => m.slug !== slug))
}
+ // Clear cache when modes are deleted
+ this.clearCache()
await this.refreshMergedState()
})
} catch (error) {
@@ -329,17 +361,12 @@ export class CustomModesManager {
}
}
- private async ensureSettingsDirectoryExists(): Promise {
- const settingsDir = path.join(this.context.globalStorageUri.fsPath, "settings")
- await fs.mkdir(settingsDir, { recursive: true })
- return settingsDir
- }
-
- async resetCustomModes(): Promise {
+ public async resetCustomModes(): Promise {
try {
const filePath = await this.getCustomModesFilePath()
- await fs.writeFile(filePath, JSON.stringify({ customModes: [] }, null, 2))
+ await fs.writeFile(filePath, yaml.stringify({ customModes: [] }))
await this.context.globalState.update("customModes", [])
+ this.clearCache()
await this.onUpdate()
} catch (error) {
vscode.window.showErrorMessage(
@@ -348,10 +375,16 @@ export class CustomModesManager {
}
}
+ private clearCache(): void {
+ this.cachedModes = null
+ this.cachedAt = 0
+ }
+
dispose(): void {
for (const disposable of this.disposables) {
disposable.dispose()
}
+
this.disposables = []
}
}
diff --git a/src/core/config/ProviderSettingsManager.ts b/src/core/config/ProviderSettingsManager.ts
index 7df34e7844..d22a87e097 100644
--- a/src/core/config/ProviderSettingsManager.ts
+++ b/src/core/config/ProviderSettingsManager.ts
@@ -1,11 +1,14 @@
import { ExtensionContext } from "vscode"
import { z, ZodError } from "zod"
-import { providerSettingsSchema, ApiConfigMeta } from "../../schemas"
+import { providerSettingsSchema, ProviderSettingsEntry, providerSettingsSchemaDiscriminated } from "../../schemas"
import { Mode, modes } from "../../shared/modes"
import { telemetryService } from "../../services/telemetry/TelemetryService"
const providerSettingsWithIdSchema = providerSettingsSchema.extend({ id: z.string().optional() })
+const discriminatedProviderSettingsWithIdSchema = providerSettingsSchemaDiscriminated.and(
+ z.object({ id: z.string().optional() }),
+)
type ProviderSettingsWithId = z.infer
@@ -17,6 +20,7 @@ export const providerProfilesSchema = z.object({
.object({
rateLimitSecondsMigrated: z.boolean().optional(),
diffSettingsMigrated: z.boolean().optional(),
+ openAiHeadersMigrated: z.boolean().optional(),
})
.optional(),
})
@@ -38,6 +42,7 @@ export class ProviderSettingsManager {
migrations: {
rateLimitSecondsMigrated: true, // Mark as migrated on fresh installs
diffSettingsMigrated: true, // Mark as migrated on fresh installs
+ openAiHeadersMigrated: true, // Mark as migrated on fresh installs
},
}
@@ -77,6 +82,18 @@ export class ProviderSettingsManager {
let isDirty = false
+ // Migrate existing installs to have per-mode API config map
+ if (!providerProfiles.modeApiConfigs) {
+ // Use the currently selected config for all modes initially
+ const currentName = providerProfiles.currentApiConfigName
+ const seedId =
+ providerProfiles.apiConfigs[currentName]?.id ??
+ Object.values(providerProfiles.apiConfigs)[0]?.id ??
+ this.defaultConfigId
+ providerProfiles.modeApiConfigs = Object.fromEntries(modes.map((m) => [m.slug, seedId]))
+ isDirty = true
+ }
+
// Ensure all configs have IDs.
for (const [_name, apiConfig] of Object.entries(providerProfiles.apiConfigs)) {
if (!apiConfig.id) {
@@ -90,6 +107,7 @@ export class ProviderSettingsManager {
providerProfiles.migrations = {
rateLimitSecondsMigrated: false,
diffSettingsMigrated: false,
+ openAiHeadersMigrated: false,
} // Initialize with default values
isDirty = true
}
@@ -106,6 +124,12 @@ export class ProviderSettingsManager {
isDirty = true
}
+ if (!providerProfiles.migrations.openAiHeadersMigrated) {
+ await this.migrateOpenAiHeaders(providerProfiles)
+ providerProfiles.migrations.openAiHeadersMigrated = true
+ isDirty = true
+ }
+
if (isDirty) {
await this.store(providerProfiles)
}
@@ -175,10 +199,34 @@ export class ProviderSettingsManager {
}
}
+ private async migrateOpenAiHeaders(providerProfiles: ProviderProfiles) {
+ try {
+ for (const [_name, apiConfig] of Object.entries(providerProfiles.apiConfigs)) {
+ // Use type assertion to access the deprecated property safely
+ const configAny = apiConfig as any
+
+ // Check if openAiHostHeader exists but openAiHeaders doesn't
+ if (
+ configAny.openAiHostHeader &&
+ (!apiConfig.openAiHeaders || Object.keys(apiConfig.openAiHeaders || {}).length === 0)
+ ) {
+ // Create the headers object with the Host value
+ apiConfig.openAiHeaders = { Host: configAny.openAiHostHeader }
+
+ // Delete the old property to prevent re-migration
+ // This prevents the header from reappearing after deletion
+ configAny.openAiHostHeader = undefined
+ }
+ }
+ } catch (error) {
+ console.error(`[MigrateOpenAiHeaders] Failed to migrate OpenAI headers:`, error)
+ }
+ }
+
/**
* List all available configs with metadata.
*/
- public async listConfig(): Promise {
+ public async listConfig(): Promise {
try {
return await this.lock(async () => {
const providerProfiles = await this.load()
@@ -199,66 +247,81 @@ export class ProviderSettingsManager {
* Preserves the ID from the input 'config' object if it exists,
* otherwise generates a new one (for creation scenarios).
*/
- public async saveConfig(name: string, config: ProviderSettingsWithId) {
+ public async saveConfig(name: string, config: ProviderSettingsWithId): Promise {
try {
return await this.lock(async () => {
const providerProfiles = await this.load()
// Preserve the existing ID if this is an update to an existing config.
const existingId = providerProfiles.apiConfigs[name]?.id
- providerProfiles.apiConfigs[name] = { ...config, id: config.id || existingId || this.generateId() }
+ const id = config.id || existingId || this.generateId()
+
+ // Filter out settings from other providers.
+ const filteredConfig = providerSettingsSchemaDiscriminated.parse(config)
+ providerProfiles.apiConfigs[name] = { ...filteredConfig, id }
await this.store(providerProfiles)
+ return id
})
} catch (error) {
throw new Error(`Failed to save config: ${error}`)
}
}
- /**
- * Load a config by name and set it as the current config.
- */
- public async loadConfig(name: string) {
+ public async getProfile(
+ params: { name: string } | { id: string },
+ ): Promise {
try {
return await this.lock(async () => {
const providerProfiles = await this.load()
- const providerSettings = providerProfiles.apiConfigs[name]
+ let name: string
+ let providerSettings: ProviderSettingsWithId
- if (!providerSettings) {
- throw new Error(`Config '${name}' not found`)
- }
+ if ("name" in params) {
+ name = params.name
- providerProfiles.currentApiConfigName = name
- await this.store(providerProfiles)
+ if (!providerProfiles.apiConfigs[name]) {
+ throw new Error(`Config with name '${name}' not found`)
+ }
+
+ providerSettings = providerProfiles.apiConfigs[name]
+ } else {
+ const id = params.id
+
+ const entry = Object.entries(providerProfiles.apiConfigs).find(
+ ([_, apiConfig]) => apiConfig.id === id,
+ )
+
+ if (!entry) {
+ throw new Error(`Config with ID '${id}' not found`)
+ }
+
+ name = entry[0]
+ providerSettings = entry[1]
+ }
- return providerSettings
+ return { name, ...providerSettings }
})
} catch (error) {
- throw new Error(`Failed to load config: ${error}`)
+ throw new Error(`Failed to get profile: ${error instanceof Error ? error.message : error}`)
}
}
/**
- * Load a config by ID and set it as the current config.
+ * Activate a profile by name or ID.
*/
- public async loadConfigById(id: string) {
+ public async activateProfile(
+ params: { name: string } | { id: string },
+ ): Promise {
+ const { name, ...providerSettings } = await this.getProfile(params)
+
try {
return await this.lock(async () => {
const providerProfiles = await this.load()
- const providerSettings = Object.entries(providerProfiles.apiConfigs).find(
- ([_, apiConfig]) => apiConfig.id === id,
- )
-
- if (!providerSettings) {
- throw new Error(`Config with ID '${id}' not found`)
- }
-
- const [name, apiConfig] = providerSettings
providerProfiles.currentApiConfigName = name
await this.store(providerProfiles)
-
- return { config: apiConfig, name }
+ return { name, ...providerSettings }
})
} catch (error) {
- throw new Error(`Failed to load config by ID: ${error}`)
+ throw new Error(`Failed to activate profile: ${error instanceof Error ? error.message : error}`)
}
}
@@ -307,8 +370,12 @@ export class ProviderSettingsManager {
try {
return await this.lock(async () => {
const providerProfiles = await this.load()
- const { modeApiConfigs = {} } = providerProfiles
- modeApiConfigs[mode] = configId
+ // Ensure the per-mode config map exists
+ if (!providerProfiles.modeApiConfigs) {
+ providerProfiles.modeApiConfigs = {}
+ }
+ // Assign the chosen config ID to this mode
+ providerProfiles.modeApiConfigs[mode] = configId
await this.store(providerProfiles)
})
} catch (error) {
@@ -332,7 +399,15 @@ export class ProviderSettingsManager {
public async export() {
try {
- return await this.lock(async () => providerProfilesSchema.parse(await this.load()))
+ return await this.lock(async () => {
+ const profiles = providerProfilesSchema.parse(await this.load())
+ const configs = profiles.apiConfigs
+ for (const name in configs) {
+ // Avoid leaking properties from other providers.
+ configs[name] = discriminatedProviderSettingsWithIdSchema.parse(configs[name])
+ }
+ return profiles
+ })
} catch (error) {
throw new Error(`Failed to export provider profiles: ${error}`)
}
diff --git a/src/core/config/__tests__/CustomModesManager.test.ts b/src/core/config/__tests__/CustomModesManager.test.ts
index a5de141483..15bf244726 100644
--- a/src/core/config/__tests__/CustomModesManager.test.ts
+++ b/src/core/config/__tests__/CustomModesManager.test.ts
@@ -8,6 +8,7 @@ import { ModeConfig } from "../../../shared/modes"
import { fileExistsAtPath } from "../../../utils/fs"
import { getWorkspacePath, arePathsEqual } from "../../../utils/path"
import { GlobalFileNames } from "../../../shared/globalFileNames"
+import * as yaml from "yaml"
jest.mock("vscode")
jest.mock("fs/promises")
@@ -47,7 +48,7 @@ describe("CustomModesManager", () => {
;(fs.mkdir as jest.Mock).mockResolvedValue(undefined)
;(fs.readFile as jest.Mock).mockImplementation(async (path: string) => {
if (path === mockSettingsPath) {
- return JSON.stringify({ customModes: [] })
+ return yaml.stringify({ customModes: [] })
}
throw new Error("File not found")
})
@@ -60,6 +61,26 @@ describe("CustomModesManager", () => {
})
describe("getCustomModes", () => {
+ it("should handle valid YAML in .roomodes file and JSON for global customModes", async () => {
+ const settingsModes = [{ slug: "mode1", name: "Mode 1", roleDefinition: "Role 1", groups: ["read"] }]
+
+ const roomodesModes = [{ slug: "mode2", name: "Mode 2", roleDefinition: "Role 2", groups: ["read"] }]
+
+ ;(fs.readFile as jest.Mock).mockImplementation(async (path: string) => {
+ if (path === mockSettingsPath) {
+ return yaml.stringify({ customModes: settingsModes })
+ }
+ if (path === mockRoomodes) {
+ return yaml.stringify({ customModes: roomodesModes })
+ }
+ throw new Error("File not found")
+ })
+
+ const modes = await manager.getCustomModes()
+
+ expect(modes).toHaveLength(2)
+ })
+
it("should merge modes with .roomodes taking precedence", async () => {
const settingsModes = [
{ slug: "mode1", name: "Mode 1", roleDefinition: "Role 1", groups: ["read"] },
@@ -73,10 +94,10 @@ describe("CustomModesManager", () => {
;(fs.readFile as jest.Mock).mockImplementation(async (path: string) => {
if (path === mockSettingsPath) {
- return JSON.stringify({ customModes: settingsModes })
+ return yaml.stringify({ customModes: settingsModes })
}
if (path === mockRoomodes) {
- return JSON.stringify({ customModes: roomodesModes })
+ return yaml.stringify({ customModes: roomodesModes })
}
throw new Error("File not found")
})
@@ -101,7 +122,7 @@ describe("CustomModesManager", () => {
})
;(fs.readFile as jest.Mock).mockImplementation(async (path: string) => {
if (path === mockSettingsPath) {
- return JSON.stringify({ customModes: settingsModes })
+ return yaml.stringify({ customModes: settingsModes })
}
throw new Error("File not found")
})
@@ -112,15 +133,15 @@ describe("CustomModesManager", () => {
expect(modes[0].slug).toBe("mode1")
})
- it("should handle invalid JSON in .roomodes", async () => {
+ it("should handle invalid YAML in .roomodes", async () => {
const settingsModes = [{ slug: "mode1", name: "Mode 1", roleDefinition: "Role 1", groups: ["read"] }]
;(fs.readFile as jest.Mock).mockImplementation(async (path: string) => {
if (path === mockSettingsPath) {
- return JSON.stringify({ customModes: settingsModes })
+ return yaml.stringify({ customModes: settingsModes })
}
if (path === mockRoomodes) {
- return "invalid json"
+ return "invalid yaml content"
}
throw new Error("File not found")
})
@@ -131,6 +152,257 @@ describe("CustomModesManager", () => {
expect(modes).toHaveLength(1)
expect(modes[0].slug).toBe("mode1")
})
+
+ it("should memoize results for 10 seconds", async () => {
+ // Setup test data
+ const settingsModes = [{ slug: "mode1", name: "Mode 1", roleDefinition: "Role 1", groups: ["read"] }]
+ ;(fs.readFile as jest.Mock).mockImplementation(async (path: string) => {
+ if (path === mockSettingsPath) {
+ return yaml.stringify({ customModes: settingsModes })
+ }
+ throw new Error("File not found")
+ })
+
+ // Mock fileExistsAtPath to only return true for settings path
+ ;(fileExistsAtPath as jest.Mock).mockImplementation(async (path: string) => {
+ return path === mockSettingsPath
+ })
+
+ // First call should read from file
+ const firstResult = await manager.getCustomModes()
+
+ // Reset mock to verify it's not called again
+ jest.clearAllMocks()
+
+ // Setup mocks again for second call
+ ;(fileExistsAtPath as jest.Mock).mockImplementation(async (path: string) => {
+ return path === mockSettingsPath
+ })
+ ;(fs.readFile as jest.Mock).mockImplementation(async (path: string) => {
+ if (path === mockSettingsPath) {
+ return yaml.stringify({ customModes: settingsModes })
+ }
+ throw new Error("File not found")
+ })
+
+ // Second call should use cached result
+ const secondResult = await manager.getCustomModes()
+ expect(fs.readFile).not.toHaveBeenCalled()
+ expect(secondResult).toHaveLength(1)
+ expect(secondResult[0].slug).toBe("mode1")
+
+ // Results should be the same object (not just equal)
+ expect(secondResult).toBe(firstResult)
+ })
+
+ it("should invalidate cache when modes are updated", async () => {
+ // Setup initial data
+ const settingsModes = [{ slug: "mode1", name: "Mode 1", roleDefinition: "Role 1", groups: ["read"] }]
+ ;(fs.readFile as jest.Mock).mockImplementation(async (path: string) => {
+ if (path === mockSettingsPath) {
+ return yaml.stringify({ customModes: settingsModes })
+ }
+ throw new Error("File not found")
+ })
+ ;(fs.writeFile as jest.Mock).mockResolvedValue(undefined)
+
+ // First call to cache the result
+ await manager.getCustomModes()
+
+ // Reset mocks to track new calls
+ jest.clearAllMocks()
+
+ // Update a mode
+ const updatedMode: ModeConfig = {
+ slug: "mode1",
+ name: "Updated Mode 1",
+ roleDefinition: "Updated Role 1",
+ groups: ["read"],
+ source: "global",
+ }
+
+ // Mock the updated file content
+ const updatedSettingsModes = [updatedMode]
+ ;(fs.readFile as jest.Mock).mockImplementation(async (path: string) => {
+ if (path === mockSettingsPath) {
+ return yaml.stringify({ customModes: updatedSettingsModes })
+ }
+ throw new Error("File not found")
+ })
+
+ // Update the mode
+ await manager.updateCustomMode("mode1", updatedMode)
+
+ // Reset mocks again
+ jest.clearAllMocks()
+
+ // Next call should read from file again (cache invalidated)
+ await manager.getCustomModes()
+ expect(fs.readFile).toHaveBeenCalled()
+ })
+
+ it("should invalidate cache when modes are deleted", async () => {
+ // Setup initial data
+ const settingsModes = [{ slug: "mode1", name: "Mode 1", roleDefinition: "Role 1", groups: ["read"] }]
+ ;(fs.readFile as jest.Mock).mockImplementation(async (path: string) => {
+ if (path === mockSettingsPath) {
+ return yaml.stringify({ customModes: settingsModes })
+ }
+ throw new Error("File not found")
+ })
+ ;(fs.writeFile as jest.Mock).mockResolvedValue(undefined)
+
+ // First call to cache the result
+ await manager.getCustomModes()
+
+ // Reset mocks to track new calls
+ jest.clearAllMocks()
+
+ // Delete a mode
+ await manager.deleteCustomMode("mode1")
+
+ // Mock the updated file content (empty)
+ ;(fs.readFile as jest.Mock).mockImplementation(async (path: string) => {
+ if (path === mockSettingsPath) {
+ return yaml.stringify({ customModes: [] })
+ }
+ throw new Error("File not found")
+ })
+
+ // Reset mocks again
+ jest.clearAllMocks()
+
+ // Next call should read from file again (cache invalidated)
+ await manager.getCustomModes()
+ expect(fs.readFile).toHaveBeenCalled()
+ })
+
+ it("should invalidate cache when modes are updated (simulating file changes)", async () => {
+ // Setup initial data
+ const settingsModes = [{ slug: "mode1", name: "Mode 1", roleDefinition: "Role 1", groups: ["read"] }]
+ ;(fs.readFile as jest.Mock).mockImplementation(async (path: string) => {
+ if (path === mockSettingsPath) {
+ return yaml.stringify({ customModes: settingsModes })
+ }
+ throw new Error("File not found")
+ })
+ ;(fileExistsAtPath as jest.Mock).mockImplementation(async (path: string) => {
+ return path === mockSettingsPath
+ })
+ ;(fs.writeFile as jest.Mock).mockResolvedValue(undefined)
+
+ // First call to cache the result
+ await manager.getCustomModes()
+
+ // Reset mocks to track new calls
+ jest.clearAllMocks()
+
+ // Setup for update
+ const updatedMode: ModeConfig = {
+ slug: "mode1",
+ name: "Updated Mode 1",
+ roleDefinition: "Updated Role 1",
+ groups: ["read"],
+ source: "global",
+ }
+
+ // Mock the updated file content
+ const updatedSettingsModes = [updatedMode]
+ ;(fs.readFile as jest.Mock).mockImplementation(async (path: string) => {
+ if (path === mockSettingsPath) {
+ return yaml.stringify({ customModes: updatedSettingsModes })
+ }
+ throw new Error("File not found")
+ })
+
+ // Simulate a file change by updating a mode
+ // This should invalidate the cache
+ await manager.updateCustomMode("mode1", updatedMode)
+
+ // Reset mocks again
+ jest.clearAllMocks()
+
+ // Setup mocks again
+ ;(fileExistsAtPath as jest.Mock).mockImplementation(async (path: string) => {
+ return path === mockSettingsPath
+ })
+ ;(fs.readFile as jest.Mock).mockImplementation(async (path: string) => {
+ if (path === mockSettingsPath) {
+ return yaml.stringify({ customModes: updatedSettingsModes })
+ }
+ throw new Error("File not found")
+ })
+
+ // Next call should read from file again (cache was invalidated by the update)
+ await manager.getCustomModes()
+ expect(fs.readFile).toHaveBeenCalled()
+ })
+
+ it("should refresh cache after TTL expires", async () => {
+ // Setup test data
+ const settingsModes = [{ slug: "mode1", name: "Mode 1", roleDefinition: "Role 1", groups: ["read"] }]
+ ;(fs.readFile as jest.Mock).mockImplementation(async (path: string) => {
+ if (path === mockSettingsPath) {
+ return yaml.stringify({ customModes: settingsModes })
+ }
+ throw new Error("File not found")
+ })
+ ;(fileExistsAtPath as jest.Mock).mockImplementation(async (path: string) => {
+ return path === mockSettingsPath
+ })
+
+ // Mock Date.now to control time
+ const originalDateNow = Date.now
+ let currentTime = 1000
+ Date.now = jest.fn(() => currentTime)
+
+ try {
+ // First call should read from file
+ await manager.getCustomModes()
+
+ // Reset mock to verify it's not called again
+ jest.clearAllMocks()
+
+ // Setup mocks again for second call
+ ;(fileExistsAtPath as jest.Mock).mockImplementation(async (path: string) => {
+ return path === mockSettingsPath
+ })
+ ;(fs.readFile as jest.Mock).mockImplementation(async (path: string) => {
+ if (path === mockSettingsPath) {
+ return yaml.stringify({ customModes: settingsModes })
+ }
+ throw new Error("File not found")
+ })
+
+ // Second call within TTL should use cache
+ await manager.getCustomModes()
+ expect(fs.readFile).not.toHaveBeenCalled()
+
+ // Advance time beyond TTL (10 seconds)
+ currentTime += 11000
+
+ // Reset mocks again
+ jest.clearAllMocks()
+
+ // Setup mocks again for third call
+ ;(fileExistsAtPath as jest.Mock).mockImplementation(async (path: string) => {
+ return path === mockSettingsPath
+ })
+ ;(fs.readFile as jest.Mock).mockImplementation(async (path: string) => {
+ if (path === mockSettingsPath) {
+ return yaml.stringify({ customModes: settingsModes })
+ }
+ throw new Error("File not found")
+ })
+
+ // Call after TTL should read from file again
+ await manager.getCustomModes()
+ expect(fs.readFile).toHaveBeenCalled()
+ } finally {
+ // Restore original Date.now
+ Date.now = originalDateNow
+ }
+ })
})
describe("updateCustomMode", () => {
@@ -162,20 +434,20 @@ describe("CustomModesManager", () => {
;(fs.readFile as jest.Mock).mockImplementation(async (path: string) => {
if (path === mockRoomodes) {
- return JSON.stringify(roomodesContent)
+ return yaml.stringify(roomodesContent)
}
if (path === mockSettingsPath) {
- return JSON.stringify(settingsContent)
+ return yaml.stringify(settingsContent)
}
throw new Error("File not found")
})
;(fs.writeFile as jest.Mock).mockImplementation(
async (path: string, content: string, _encoding?: string) => {
if (path === mockSettingsPath) {
- settingsContent = JSON.parse(content)
+ settingsContent = yaml.parse(content)
}
if (path === mockRoomodes) {
- roomodesContent = JSON.parse(content)
+ roomodesContent = yaml.parse(content)
}
return Promise.resolve()
},
@@ -188,7 +460,7 @@ describe("CustomModesManager", () => {
// Verify the content of the write
const writeCall = (fs.writeFile as jest.Mock).mock.calls[0]
- const content = JSON.parse(writeCall[1])
+ const content = yaml.parse(writeCall[1])
expect(content.customModes).toContainEqual(
expect.objectContaining({
slug: "mode1",
@@ -230,19 +502,19 @@ describe("CustomModesManager", () => {
})
;(fs.readFile as jest.Mock).mockImplementation(async (path: string) => {
if (path === mockSettingsPath) {
- return JSON.stringify({ customModes: [] })
+ return yaml.stringify({ customModes: [] })
}
if (path === mockRoomodes) {
if (!roomodesContent) {
throw new Error("File not found")
}
- return JSON.stringify(roomodesContent)
+ return yaml.stringify(roomodesContent)
}
throw new Error("File not found")
})
;(fs.writeFile as jest.Mock).mockImplementation(async (path: string, content: string) => {
if (path === mockRoomodes) {
- roomodesContent = JSON.parse(content)
+ roomodesContent = yaml.parse(content)
}
return Promise.resolve()
})
@@ -292,14 +564,14 @@ describe("CustomModesManager", () => {
let settingsContent = { customModes: [] }
;(fs.readFile as jest.Mock).mockImplementation(async (path: string) => {
if (path === mockSettingsPath) {
- return JSON.stringify(settingsContent)
+ return yaml.stringify(settingsContent)
}
throw new Error("File not found")
})
;(fs.writeFile as jest.Mock).mockImplementation(
async (path: string, content: string, _encoding?: string) => {
if (path === mockSettingsPath) {
- settingsContent = JSON.parse(content)
+ settingsContent = yaml.parse(content)
}
return Promise.resolve()
},
@@ -357,16 +629,13 @@ describe("CustomModesManager", () => {
await manager.getCustomModesFilePath()
- expect(fs.writeFile).toHaveBeenCalledWith(
- settingsPath,
- expect.stringMatching(/^\{\s+"customModes":\s+\[\s*\]\s*\}$/),
- )
+ expect(fs.writeFile).toHaveBeenCalledWith(settingsPath, expect.stringMatching(/^customModes: \[\]/))
})
it("watches file for changes", async () => {
const configPath = path.join(mockStoragePath, "settings", GlobalFileNames.customModes)
- ;(fs.readFile as jest.Mock).mockResolvedValue(JSON.stringify({ customModes: [] }))
+ ;(fs.readFile as jest.Mock).mockResolvedValue(yaml.stringify({ customModes: [] }))
;(arePathsEqual as jest.Mock).mockImplementation((path1: string, path2: string) => {
return path.normalize(path1) === path.normalize(path2)
})
@@ -401,14 +670,14 @@ describe("CustomModesManager", () => {
let settingsContent = { customModes: [existingMode] }
;(fs.readFile as jest.Mock).mockImplementation(async (path: string) => {
if (path === mockSettingsPath) {
- return JSON.stringify(settingsContent)
+ return yaml.stringify(settingsContent)
}
throw new Error("File not found")
})
;(fs.writeFile as jest.Mock).mockImplementation(
async (path: string, content: string, encoding?: string) => {
if (path === mockSettingsPath && encoding === "utf-8") {
- settingsContent = JSON.parse(content)
+ settingsContent = yaml.parse(content)
}
return Promise.resolve()
},
@@ -446,9 +715,9 @@ describe("CustomModesManager", () => {
})
describe("updateModesInFile", () => {
- it("handles corrupted JSON content gracefully", async () => {
- const corruptedJson = "{ invalid json content"
- ;(fs.readFile as jest.Mock).mockResolvedValue(corruptedJson)
+ it("handles corrupted YAML content gracefully", async () => {
+ const corruptedYaml = "customModes: [invalid yaml content"
+ ;(fs.readFile as jest.Mock).mockResolvedValue(corruptedYaml)
const newMode: ModeConfig = {
slug: "test-mode",
@@ -460,9 +729,9 @@ describe("CustomModesManager", () => {
await manager.updateCustomMode("test-mode", newMode)
- // Verify that a valid JSON structure was written
+ // Verify that a valid YAML structure was written
const writeCall = (fs.writeFile as jest.Mock).mock.calls[0]
- const writtenContent = JSON.parse(writeCall[1])
+ const writtenContent = yaml.parse(writeCall[1])
expect(writtenContent).toEqual({
customModes: [
expect.objectContaining({
diff --git a/src/core/config/__tests__/ProviderSettingsManager.test.ts b/src/core/config/__tests__/ProviderSettingsManager.test.ts
index ade40c29d3..3eb436a079 100644
--- a/src/core/config/__tests__/ProviderSettingsManager.test.ts
+++ b/src/core/config/__tests__/ProviderSettingsManager.test.ts
@@ -53,9 +53,11 @@ describe("ProviderSettingsManager", () => {
fuzzyMatchThreshold: 1.0,
},
},
+ modeApiConfigs: {},
migrations: {
rateLimitSecondsMigrated: true,
diffSettingsMigrated: true,
+ openAiHeadersMigrated: true,
},
}),
)
@@ -219,8 +221,9 @@ describe("ProviderSettingsManager", () => {
)
const newConfig: ProviderSettings = {
- apiProvider: "anthropic",
- apiKey: "test-key",
+ apiProvider: "vertex",
+ apiModelId: "gemini-2.5-flash-preview-05-20",
+ vertexKeyFile: "test-key-file",
}
await providerSettingsManager.saveConfig("test", newConfig)
@@ -245,10 +248,58 @@ describe("ProviderSettingsManager", () => {
},
}
- expect(mockSecrets.store).toHaveBeenCalledWith(
- "roo_cline_config_api_config",
- JSON.stringify(expectedConfig, null, 2),
+ expect(mockSecrets.store.mock.calls[0][0]).toEqual("roo_cline_config_api_config")
+ expect(storedConfig).toEqual(expectedConfig)
+ })
+
+ it("should only save provider relevant settings", async () => {
+ mockSecrets.get.mockResolvedValue(
+ JSON.stringify({
+ currentApiConfigName: "default",
+ apiConfigs: {
+ default: {},
+ },
+ modeApiConfigs: {
+ code: "default",
+ architect: "default",
+ ask: "default",
+ },
+ }),
)
+
+ const newConfig: ProviderSettings = {
+ apiProvider: "anthropic",
+ apiKey: "test-key",
+ }
+ const newConfigWithExtra: ProviderSettings = {
+ ...newConfig,
+ openRouterApiKey: "another-key",
+ }
+
+ await providerSettingsManager.saveConfig("test", newConfigWithExtra)
+
+ // Get the actual stored config to check the generated ID
+ const storedConfig = JSON.parse(mockSecrets.store.mock.lastCall[1])
+ const testConfigId = storedConfig.apiConfigs.test.id
+
+ const expectedConfig = {
+ currentApiConfigName: "default",
+ apiConfigs: {
+ default: {},
+ test: {
+ ...newConfig,
+ id: testConfigId,
+ },
+ },
+ modeApiConfigs: {
+ code: "default",
+ architect: "default",
+ ask: "default",
+ },
+ }
+
+ expect(mockSecrets.store.mock.calls[0][0]).toEqual("roo_cline_config_api_config")
+ expect(storedConfig).toEqual(expectedConfig)
})
it("should update existing config", async () => {
@@ -289,10 +340,9 @@ describe("ProviderSettingsManager", () => {
},
}
- expect(mockSecrets.store).toHaveBeenCalledWith(
- "roo_cline_config_api_config",
- JSON.stringify(expectedConfig, null, 2),
- )
+ const storedConfig = JSON.parse(mockSecrets.store.mock.lastCall[1])
+ expect(mockSecrets.store.mock.lastCall[0]).toEqual("roo_cline_config_api_config")
+ expect(storedConfig).toEqual(expectedConfig)
})
it("should throw error if secrets storage fails", async () => {
@@ -389,17 +439,15 @@ describe("ProviderSettingsManager", () => {
mockGlobalState.get.mockResolvedValue(42)
mockSecrets.get.mockResolvedValue(JSON.stringify(existingConfig))
- const config = await providerSettingsManager.loadConfig("test")
+ const { name, ...providerSettings } = await providerSettingsManager.activateProfile({ name: "test" })
- expect(config).toEqual({
- apiProvider: "anthropic",
- apiKey: "test-key",
- id: "test-id",
- })
+ expect(name).toBe("test")
+ expect(providerSettings).toEqual({ apiProvider: "anthropic", apiKey: "test-key", id: "test-id" })
- // Get the stored config to check the structure
+ // Get the stored config to check the structure.
const storedConfig = JSON.parse(mockSecrets.store.mock.calls[1][1])
expect(storedConfig.currentApiConfigName).toBe("test")
+
expect(storedConfig.apiConfigs.test).toEqual({
apiProvider: "anthropic",
apiKey: "test-key",
@@ -411,17 +459,12 @@ describe("ProviderSettingsManager", () => {
mockSecrets.get.mockResolvedValue(
JSON.stringify({
currentApiConfigName: "default",
- apiConfigs: {
- default: {
- config: {},
- id: "default",
- },
- },
+ apiConfigs: { default: { config: {}, id: "default" } },
}),
)
- await expect(providerSettingsManager.loadConfig("nonexistent")).rejects.toThrow(
- "Config 'nonexistent' not found",
+ await expect(providerSettingsManager.activateProfile({ name: "nonexistent" })).rejects.toThrow(
+ "Config with name 'nonexistent' not found",
)
})
@@ -429,20 +472,13 @@ describe("ProviderSettingsManager", () => {
mockSecrets.get.mockResolvedValue(
JSON.stringify({
currentApiConfigName: "default",
- apiConfigs: {
- test: {
- config: {
- apiProvider: "anthropic",
- },
- id: "test-id",
- },
- },
+ apiConfigs: { test: { config: { apiProvider: "anthropic" }, id: "test-id" } },
}),
)
mockSecrets.store.mockRejectedValueOnce(new Error("Storage failed"))
- await expect(providerSettingsManager.loadConfig("test")).rejects.toThrow(
- "Failed to load config: Error: Failed to write provider profiles to secrets: Error: Storage failed",
+ await expect(providerSettingsManager.activateProfile({ name: "test" })).rejects.toThrow(
+ "Failed to activate profile: Failed to write provider profiles to secrets: Error: Storage failed",
)
})
@@ -492,12 +528,7 @@ describe("ProviderSettingsManager", () => {
mockSecrets.get.mockResolvedValue(
JSON.stringify({
currentApiConfigName: "test",
- apiConfigs: {
- test: {
- apiProvider: "anthropic",
- id: "test-id",
- },
- },
+ apiConfigs: { test: { apiProvider: "anthropic", id: "test-id" } },
}),
)
@@ -512,18 +543,8 @@ describe("ProviderSettingsManager", () => {
it("should return true for existing config", async () => {
const existingConfig: ProviderProfiles = {
currentApiConfigName: "default",
- apiConfigs: {
- default: {
- id: "default",
- },
- test: {
- apiProvider: "anthropic",
- id: "test-id",
- },
- },
- migrations: {
- rateLimitSecondsMigrated: false,
- },
+ apiConfigs: { default: { id: "default" }, test: { apiProvider: "anthropic", id: "test-id" } },
+ migrations: { rateLimitSecondsMigrated: false },
}
mockSecrets.get.mockResolvedValue(JSON.stringify(existingConfig))
@@ -534,10 +555,7 @@ describe("ProviderSettingsManager", () => {
it("should return false for non-existent config", async () => {
mockSecrets.get.mockResolvedValue(
- JSON.stringify({
- currentApiConfigName: "default",
- apiConfigs: { default: {} },
- }),
+ JSON.stringify({ currentApiConfigName: "default", apiConfigs: { default: {} } }),
)
const hasConfig = await providerSettingsManager.hasConfig("nonexistent")
diff --git a/src/core/config/__tests__/importExport.test.ts b/src/core/config/__tests__/importExport.test.ts
index 8330187b3b..0d32e87dea 100644
--- a/src/core/config/__tests__/importExport.test.ts
+++ b/src/core/config/__tests__/importExport.test.ts
@@ -11,7 +11,6 @@ import { ProviderSettingsManager } from "../ProviderSettingsManager"
import { ContextProxy } from "../ContextProxy"
import { CustomModesManager } from "../CustomModesManager"
-// Mock VSCode modules
jest.mock("vscode", () => ({
window: {
showOpenDialog: jest.fn(),
@@ -22,14 +21,12 @@ jest.mock("vscode", () => ({
},
}))
-// Mock fs/promises
jest.mock("fs/promises", () => ({
readFile: jest.fn(),
mkdir: jest.fn(),
writeFile: jest.fn(),
}))
-// Mock os module
jest.mock("os", () => ({
homedir: jest.fn(() => "/mock/home"),
}))
@@ -41,27 +38,22 @@ describe("importExport", () => {
let mockCustomModesManager: jest.Mocked
beforeEach(() => {
- // Reset all mocks
jest.clearAllMocks()
- // Setup providerSettingsManager mock
mockProviderSettingsManager = {
export: jest.fn(),
import: jest.fn(),
listConfig: jest.fn(),
} as unknown as jest.Mocked
- // Setup contextProxy mock with properly typed export method
mockContextProxy = {
setValues: jest.fn(),
setValue: jest.fn(),
export: jest.fn().mockImplementation(() => Promise.resolve({})),
+ setProviderSettings: jest.fn(),
} as unknown as jest.Mocked
- // Setup customModesManager mock
- mockCustomModesManager = {
- updateCustomMode: jest.fn(),
- } as unknown as jest.Mocked
+ mockCustomModesManager = { updateCustomMode: jest.fn() } as unknown as jest.Mocked
const map = new Map()
@@ -75,7 +67,6 @@ describe("importExport", () => {
describe("importSettings", () => {
it("should return success: false when user cancels file selection", async () => {
- // Mock user canceling file selection
;(vscode.window.showOpenDialog as jest.Mock).mockResolvedValue(undefined)
const result = await importSettings({
@@ -85,63 +76,43 @@ describe("importExport", () => {
})
expect(result).toEqual({ success: false })
+
expect(vscode.window.showOpenDialog).toHaveBeenCalledWith({
filters: { JSON: ["json"] },
canSelectMany: false,
})
+
expect(fs.readFile).not.toHaveBeenCalled()
expect(mockProviderSettingsManager.import).not.toHaveBeenCalled()
expect(mockContextProxy.setValues).not.toHaveBeenCalled()
})
it("should import settings successfully from a valid file", async () => {
- // Mock successful file selection
;(vscode.window.showOpenDialog as jest.Mock).mockResolvedValue([{ fsPath: "/mock/path/settings.json" }])
- // Valid settings content
const mockFileContent = JSON.stringify({
providerProfiles: {
currentApiConfigName: "test",
- apiConfigs: {
- test: {
- apiProvider: "openai" as ProviderName,
- apiKey: "test-key",
- id: "test-id",
- },
- },
- },
- globalSettings: {
- mode: "code",
- autoApprovalEnabled: true,
+ apiConfigs: { test: { apiProvider: "openai" as ProviderName, apiKey: "test-key", id: "test-id" } },
},
+ globalSettings: { mode: "code", autoApprovalEnabled: true },
})
- // Mock reading file
;(fs.readFile as jest.Mock).mockResolvedValue(mockFileContent)
- // Mock export returning previous provider profiles
const previousProviderProfiles = {
currentApiConfigName: "default",
- apiConfigs: {
- default: {
- apiProvider: "anthropic" as ProviderName,
- id: "default-id",
- },
- },
+ apiConfigs: { default: { apiProvider: "anthropic" as ProviderName, id: "default-id" } },
}
mockProviderSettingsManager.export.mockResolvedValue(previousProviderProfiles)
- // Mock listConfig
mockProviderSettingsManager.listConfig.mockResolvedValue([
{ name: "test", id: "test-id", apiProvider: "openai" as ProviderName },
{ name: "default", id: "default-id", apiProvider: "anthropic" as ProviderName },
])
- // Mock contextProxy.export
- mockContextProxy.export.mockResolvedValue({
- mode: "code",
- })
+ mockContextProxy.export.mockResolvedValue({ mode: "code" })
const result = await importSettings({
providerSettingsManager: mockProviderSettingsManager,
@@ -152,22 +123,16 @@ describe("importExport", () => {
expect(result.success).toBe(true)
expect(fs.readFile).toHaveBeenCalledWith("/mock/path/settings.json", "utf-8")
expect(mockProviderSettingsManager.export).toHaveBeenCalled()
+
expect(mockProviderSettingsManager.import).toHaveBeenCalledWith({
...previousProviderProfiles,
currentApiConfigName: "test",
- apiConfigs: {
- test: {
- apiProvider: "openai" as ProviderName,
- apiKey: "test-key",
- id: "test-id",
- },
- },
- })
- expect(mockContextProxy.setValues).toHaveBeenCalledWith({
- mode: "code",
- autoApprovalEnabled: true,
+ apiConfigs: { test: { apiProvider: "openai" as ProviderName, apiKey: "test-key", id: "test-id" } },
})
+
+ expect(mockContextProxy.setValues).toHaveBeenCalledWith({ mode: "code", autoApprovalEnabled: true })
expect(mockContextProxy.setValue).toHaveBeenCalledWith("currentApiConfigName", "test")
+
expect(mockContextProxy.setValue).toHaveBeenCalledWith("listApiConfigMeta", [
{ name: "test", id: "test-id", apiProvider: "openai" as ProviderName },
{ name: "default", id: "default-id", apiProvider: "anthropic" as ProviderName },
@@ -175,16 +140,14 @@ describe("importExport", () => {
})
it("should return success: false when file content is invalid", async () => {
- // Mock successful file selection
;(vscode.window.showOpenDialog as jest.Mock).mockResolvedValue([{ fsPath: "/mock/path/settings.json" }])
- // Invalid content (missing required fields)
+ // Invalid content (missing required fields).
const mockInvalidContent = JSON.stringify({
providerProfiles: { apiConfigs: {} },
globalSettings: {},
})
- // Mock reading file
;(fs.readFile as jest.Mock).mockResolvedValue(mockInvalidContent)
const result = await importSettings({
@@ -193,20 +156,67 @@ describe("importExport", () => {
customModesManager: mockCustomModesManager,
})
- expect(result).toEqual({ success: false })
+ expect(result).toEqual({ success: false, error: "[providerProfiles.currentApiConfigName]: Required" })
expect(fs.readFile).toHaveBeenCalledWith("/mock/path/settings.json", "utf-8")
expect(mockProviderSettingsManager.import).not.toHaveBeenCalled()
expect(mockContextProxy.setValues).not.toHaveBeenCalled()
})
- it("should return success: false when file content is not valid JSON", async () => {
- // Mock successful file selection
+ it("should import settings successfully when globalSettings key is missing", async () => {
;(vscode.window.showOpenDialog as jest.Mock).mockResolvedValue([{ fsPath: "/mock/path/settings.json" }])
- // Invalid JSON
- const mockInvalidJson = "{ this is not valid JSON }"
+ const mockFileContent = JSON.stringify({
+ providerProfiles: {
+ currentApiConfigName: "test",
+ apiConfigs: { test: { apiProvider: "openai" as ProviderName, apiKey: "test-key", id: "test-id" } },
+ },
+ })
+
+ ;(fs.readFile as jest.Mock).mockResolvedValue(mockFileContent)
+
+ const previousProviderProfiles = {
+ currentApiConfigName: "default",
+ apiConfigs: { default: { apiProvider: "anthropic" as ProviderName, id: "default-id" } },
+ }
+
+ mockProviderSettingsManager.export.mockResolvedValue(previousProviderProfiles)
- // Mock reading file
+ mockProviderSettingsManager.listConfig.mockResolvedValue([
+ { name: "test", id: "test-id", apiProvider: "openai" as ProviderName },
+ { name: "default", id: "default-id", apiProvider: "anthropic" as ProviderName },
+ ])
+
+ mockContextProxy.export.mockResolvedValue({ mode: "code" })
+
+ const result = await importSettings({
+ providerSettingsManager: mockProviderSettingsManager,
+ contextProxy: mockContextProxy,
+ customModesManager: mockCustomModesManager,
+ })
+
+ expect(result.success).toBe(true)
+ expect(fs.readFile).toHaveBeenCalledWith("/mock/path/settings.json", "utf-8")
+ expect(mockProviderSettingsManager.export).toHaveBeenCalled()
+ expect(mockProviderSettingsManager.import).toHaveBeenCalledWith({
+ ...previousProviderProfiles,
+ currentApiConfigName: "test",
+ apiConfigs: {
+ test: { apiProvider: "openai" as ProviderName, apiKey: "test-key", id: "test-id" },
+ },
+ })
+
+ // Should call setValues with an empty object since globalSettings is missing.
+ expect(mockContextProxy.setValues).toHaveBeenCalledWith({})
+ expect(mockContextProxy.setValue).toHaveBeenCalledWith("currentApiConfigName", "test")
+ expect(mockContextProxy.setValue).toHaveBeenCalledWith("listApiConfigMeta", [
+ { name: "test", id: "test-id", apiProvider: "openai" as ProviderName },
+ { name: "default", id: "default-id", apiProvider: "anthropic" as ProviderName },
+ ])
+ })
+
+ it("should return success: false when file content is not valid JSON", async () => {
+ ;(vscode.window.showOpenDialog as jest.Mock).mockResolvedValue([{ fsPath: "/mock/path/settings.json" }])
+ const mockInvalidJson = "{ this is not valid JSON }"
;(fs.readFile as jest.Mock).mockResolvedValue(mockInvalidJson)
const result = await importSettings({
@@ -215,17 +225,14 @@ describe("importExport", () => {
customModesManager: mockCustomModesManager,
})
- expect(result).toEqual({ success: false })
+ expect(result).toEqual({ success: false, error: "Expected property name or '}' in JSON at position 2" })
expect(fs.readFile).toHaveBeenCalledWith("/mock/path/settings.json", "utf-8")
expect(mockProviderSettingsManager.import).not.toHaveBeenCalled()
expect(mockContextProxy.setValues).not.toHaveBeenCalled()
})
it("should return success: false when reading file fails", async () => {
- // Mock successful file selection
;(vscode.window.showOpenDialog as jest.Mock).mockResolvedValue([{ fsPath: "/mock/path/settings.json" }])
-
- // Mock file read error
;(fs.readFile as jest.Mock).mockRejectedValue(new Error("File read error"))
const result = await importSettings({
@@ -234,7 +241,7 @@ describe("importExport", () => {
customModesManager: mockCustomModesManager,
})
- expect(result).toEqual({ success: false })
+ expect(result).toEqual({ success: false, error: "File read error" })
expect(fs.readFile).toHaveBeenCalledWith("/mock/path/settings.json", "utf-8")
expect(mockProviderSettingsManager.import).not.toHaveBeenCalled()
expect(mockContextProxy.setValues).not.toHaveBeenCalled()
@@ -276,43 +283,35 @@ describe("importExport", () => {
it("should call updateCustomMode for each custom mode in config", async () => {
;(vscode.window.showOpenDialog as jest.Mock).mockResolvedValue([{ fsPath: "/mock/path/settings.json" }])
+
const customModes = [
- {
- slug: "mode1",
- name: "Mode One",
- roleDefinition: "Custom role one",
- groups: [],
- },
- {
- slug: "mode2",
- name: "Mode Two",
- roleDefinition: "Custom role two",
- groups: [],
- },
+ { slug: "mode1", name: "Mode One", roleDefinition: "Custom role one", groups: [] },
+ { slug: "mode2", name: "Mode Two", roleDefinition: "Custom role two", groups: [] },
]
+
const mockFileContent = JSON.stringify({
- providerProfiles: {
- currentApiConfigName: "test",
- apiConfigs: {},
- },
- globalSettings: {
- mode: "code",
- customModes,
- },
+ providerProfiles: { currentApiConfigName: "test", apiConfigs: {} },
+ globalSettings: { mode: "code", customModes },
})
+
;(fs.readFile as jest.Mock).mockResolvedValue(mockFileContent)
+
mockProviderSettingsManager.export.mockResolvedValue({
currentApiConfigName: "test",
apiConfigs: {},
})
+
mockProviderSettingsManager.listConfig.mockResolvedValue([])
+
const result = await importSettings({
providerSettingsManager: mockProviderSettingsManager,
contextProxy: mockContextProxy,
customModesManager: mockCustomModesManager,
})
+
expect(result.success).toBe(true)
expect(mockCustomModesManager.updateCustomMode).toHaveBeenCalledTimes(customModes.length)
+
customModes.forEach((mode) => {
expect(mockCustomModesManager.updateCustomMode).toHaveBeenCalledWith(mode.slug, mode)
})
@@ -320,7 +319,6 @@ describe("importExport", () => {
describe("exportSettings", () => {
it("should not export settings when user cancels file selection", async () => {
- // Mock user canceling file selection
;(vscode.window.showSaveDialog as jest.Mock).mockResolvedValue(undefined)
await exportSettings({
@@ -332,37 +330,25 @@ describe("importExport", () => {
filters: { JSON: ["json"] },
defaultUri: expect.anything(),
})
+
expect(mockProviderSettingsManager.export).not.toHaveBeenCalled()
expect(mockContextProxy.export).not.toHaveBeenCalled()
expect(fs.writeFile).not.toHaveBeenCalled()
})
it("should export settings to the selected file location", async () => {
- // Mock successful file location selection
;(vscode.window.showSaveDialog as jest.Mock).mockResolvedValue({
fsPath: "/mock/path/roo-code-settings.json",
})
- // Mock providerProfiles data
const mockProviderProfiles = {
currentApiConfigName: "test",
- apiConfigs: {
- test: {
- apiProvider: "openai" as ProviderName,
- id: "test-id",
- },
- },
- migrations: {
- rateLimitSecondsMigrated: false,
- },
+ apiConfigs: { test: { apiProvider: "openai" as ProviderName, id: "test-id" } },
+ migrations: { rateLimitSecondsMigrated: false },
}
- mockProviderSettingsManager.export.mockResolvedValue(mockProviderProfiles)
- // Mock globalSettings data
- const mockGlobalSettings = {
- mode: "code",
- autoApprovalEnabled: true,
- }
+ mockProviderSettingsManager.export.mockResolvedValue(mockProviderProfiles)
+ const mockGlobalSettings = { mode: "code", autoApprovalEnabled: true }
mockContextProxy.export.mockResolvedValue(mockGlobalSettings)
await exportSettings({
@@ -374,52 +360,32 @@ describe("importExport", () => {
filters: { JSON: ["json"] },
defaultUri: expect.anything(),
})
+
expect(mockProviderSettingsManager.export).toHaveBeenCalled()
expect(mockContextProxy.export).toHaveBeenCalled()
expect(fs.mkdir).toHaveBeenCalledWith("/mock/path", { recursive: true })
+
expect(fs.writeFile).toHaveBeenCalledWith(
"/mock/path/roo-code-settings.json",
- JSON.stringify(
- {
- providerProfiles: mockProviderProfiles,
- globalSettings: mockGlobalSettings,
- },
- null,
- 2,
- ),
+ JSON.stringify({ providerProfiles: mockProviderProfiles, globalSettings: mockGlobalSettings }, null, 2),
"utf-8",
)
})
it("should handle errors during the export process", async () => {
- // Mock successful file location selection
;(vscode.window.showSaveDialog as jest.Mock).mockResolvedValue({
fsPath: "/mock/path/roo-code-settings.json",
})
- // Mock provider profiles
mockProviderSettingsManager.export.mockResolvedValue({
currentApiConfigName: "test",
- apiConfigs: {
- test: {
- apiProvider: "openai" as ProviderName,
- id: "test-id",
- },
- },
- migrations: {
- rateLimitSecondsMigrated: false,
- },
+ apiConfigs: { test: { apiProvider: "openai" as ProviderName, id: "test-id" } },
+ migrations: { rateLimitSecondsMigrated: false },
})
- // Mock global settings
- mockContextProxy.export.mockResolvedValue({
- mode: "code",
- })
-
- // Mock file write error
+ mockContextProxy.export.mockResolvedValue({ mode: "code" })
;(fs.writeFile as jest.Mock).mockRejectedValue(new Error("Write error"))
- // The function catches errors internally and doesn't throw or return anything
await exportSettings({
providerSettingsManager: mockProviderSettingsManager,
contextProxy: mockContextProxy,
@@ -430,38 +396,23 @@ describe("importExport", () => {
expect(mockContextProxy.export).toHaveBeenCalled()
expect(fs.mkdir).toHaveBeenCalledWith("/mock/path", { recursive: true })
expect(fs.writeFile).toHaveBeenCalled()
- // The error is caught and the function exits silently
+ // The error is caught and the function exits silently.
})
it("should handle errors during directory creation", async () => {
- // Mock successful file location selection
;(vscode.window.showSaveDialog as jest.Mock).mockResolvedValue({
fsPath: "/mock/path/roo-code-settings.json",
})
- // Mock provider profiles
mockProviderSettingsManager.export.mockResolvedValue({
currentApiConfigName: "test",
- apiConfigs: {
- test: {
- apiProvider: "openai" as ProviderName,
- id: "test-id",
- },
- },
- migrations: {
- rateLimitSecondsMigrated: false,
- },
+ apiConfigs: { test: { apiProvider: "openai" as ProviderName, id: "test-id" } },
+ migrations: { rateLimitSecondsMigrated: false },
})
- // Mock global settings
- mockContextProxy.export.mockResolvedValue({
- mode: "code",
- })
-
- // Mock directory creation error
+ mockContextProxy.export.mockResolvedValue({ mode: "code" })
;(fs.mkdir as jest.Mock).mockRejectedValue(new Error("Directory creation error"))
- // The function catches errors internally and doesn't throw or return anything
await exportSettings({
providerSettingsManager: mockProviderSettingsManager,
contextProxy: mockContextProxy,
@@ -471,26 +422,22 @@ describe("importExport", () => {
expect(mockProviderSettingsManager.export).toHaveBeenCalled()
expect(mockContextProxy.export).toHaveBeenCalled()
expect(fs.mkdir).toHaveBeenCalled()
- expect(fs.writeFile).not.toHaveBeenCalled() // Should not be called since mkdir failed
+ expect(fs.writeFile).not.toHaveBeenCalled() // Should not be called since mkdir failed.
})
it("should use the correct default save location", async () => {
- // Mock user cancels to avoid full execution
;(vscode.window.showSaveDialog as jest.Mock).mockResolvedValue(undefined)
- // Call the function
await exportSettings({
providerSettingsManager: mockProviderSettingsManager,
contextProxy: mockContextProxy,
})
- // Verify the default save location
expect(vscode.window.showSaveDialog).toHaveBeenCalledWith({
filters: { JSON: ["json"] },
defaultUri: expect.anything(),
})
- // Verify Uri.file was called with the correct path
expect(vscode.Uri.file).toHaveBeenCalledWith(path.join("/mock/home", "Documents", "roo-code-settings.json"))
})
})
diff --git a/src/core/config/importExport.ts b/src/core/config/importExport.ts
index e16314cb6c..457e91fa37 100644
--- a/src/core/config/importExport.ts
+++ b/src/core/config/importExport.ts
@@ -3,12 +3,14 @@ import * as path from "path"
import fs from "fs/promises"
import * as vscode from "vscode"
-import { z } from "zod"
+import { z, ZodError } from "zod"
import { globalSettingsSchema } from "../../schemas"
+
import { ProviderSettingsManager, providerProfilesSchema } from "./ProviderSettingsManager"
import { ContextProxy } from "./ContextProxy"
import { CustomModesManager } from "./CustomModesManager"
+import { telemetryService } from "../../services/telemetry/TelemetryService"
type ImportOptions = {
providerSettingsManager: ProviderSettingsManager
@@ -33,14 +35,14 @@ export const importSettings = async ({ providerSettingsManager, contextProxy, cu
const schema = z.object({
providerProfiles: providerProfilesSchema,
- globalSettings: globalSettingsSchema,
+ globalSettings: globalSettingsSchema.optional(),
})
try {
const previousProviderProfiles = await providerSettingsManager.export()
- const { providerProfiles: newProviderProfiles, globalSettings } = schema.parse(
- JSON.parse(await fs.readFile(uris[0].fsPath, "utf-8")),
- )
+
+ const data = JSON.parse(await fs.readFile(uris[0].fsPath, "utf-8"))
+ const { providerProfiles: newProviderProfiles, globalSettings = {} } = schema.parse(data)
const providerProfiles = {
currentApiConfigName: newProviderProfiles.currentApiConfigName,
@@ -59,14 +61,34 @@ export const importSettings = async ({ providerSettingsManager, contextProxy, cu
)
await providerSettingsManager.import(newProviderProfiles)
-
await contextProxy.setValues(globalSettings)
- contextProxy.setValue("currentApiConfigName", providerProfiles.currentApiConfigName)
+
+ // Set the current provider.
+ const currentProviderName = providerProfiles.currentApiConfigName
+ const currentProvider = providerProfiles.apiConfigs[currentProviderName]
+ contextProxy.setValue("currentApiConfigName", currentProviderName)
+
+ // TODO: It seems like we don't need to have the provider settings in
+ // the proxy; we can just use providerSettingsManager as the source of
+ // truth.
+ if (currentProvider) {
+ contextProxy.setProviderSettings(currentProvider)
+ }
+
contextProxy.setValue("listApiConfigMeta", await providerSettingsManager.listConfig())
return { providerProfiles, globalSettings, success: true }
} catch (e) {
- return { success: false }
+ let error = "Unknown error"
+
+ if (e instanceof ZodError) {
+ error = e.issues.map((issue) => `[${issue.path.join(".")}]: ${issue.message}`).join("\n")
+ telemetryService.captureSchemaValidationError({ schemaName: "ImportExport", error: e })
+ } else if (e instanceof Error) {
+ error = e.message
+ }
+
+ return { success: false, error }
}
}
@@ -84,6 +106,14 @@ export const exportSettings = async ({ providerSettingsManager, contextProxy }:
const providerProfiles = await providerSettingsManager.export()
const globalSettings = await contextProxy.export()
+ // It's okay if there are no global settings, but if there are no
+ // provider profile configured then don't export. If we wanted to
+ // support this case then the `importSettings` function would need to
+ // be updated to handle the case where there are no provider profiles.
+ if (typeof providerProfiles === "undefined") {
+ return
+ }
+
const dirname = path.dirname(uri.fsPath)
await fs.mkdir(dirname, { recursive: true })
await fs.writeFile(uri.fsPath, JSON.stringify({ providerProfiles, globalSettings }, null, 2), "utf-8")
diff --git a/src/core/context-tracking/FileContextTracker.ts b/src/core/context-tracking/FileContextTracker.ts
index 18eee94326..4e96ede654 100644
--- a/src/core/context-tracking/FileContextTracker.ts
+++ b/src/core/context-tracking/FileContextTracker.ts
@@ -1,6 +1,6 @@
import * as path from "path"
import * as vscode from "vscode"
-import { getTaskDirectoryPath } from "../../shared/storagePathManager"
+import { getTaskDirectoryPath } from "../../utils/storage"
import { GlobalFileNames } from "../../shared/globalFileNames"
import { fileExistsAtPath } from "../../utils/fs"
import fs from "fs/promises"
diff --git a/src/core/diff/strategies/__tests__/multi-search-replace.test.ts b/src/core/diff/strategies/__tests__/multi-search-replace.test.ts
index 37edcccb62..dc971d3a10 100644
--- a/src/core/diff/strategies/__tests__/multi-search-replace.test.ts
+++ b/src/core/diff/strategies/__tests__/multi-search-replace.test.ts
@@ -2332,6 +2332,54 @@ function two() {
function three() {
return "three";
+}`)
+ }
+ })
+
+ it("should deduce start_line when include line number in search and replace content", async () => {
+ const originalContent = `
+function one() {
+ return 1;
+}
+
+function process() {
+ return "target";
+}
+
+function process() {
+ return "target";
+}
+
+function two() {
+ return 2;
+}
+`.trim()
+ const diffContent = `test.ts
+<<<<<<< SEARCH
+9 | function process() {
+10 | return "target";
+=======
+9 | function process2() {
+10 | return "target222";
+>>>>>>> REPLACE`
+
+ const result = await strategy.applyDiff(originalContent, diffContent)
+ expect(result.success).toBe(true)
+ if (result.success) {
+ expect(result.content).toBe(`function one() {
+ return 1;
+}
+
+function process() {
+ return "target";
+}
+
+function process2() {
+ return "target222";
+}
+
+function two() {
+ return 2;
}`)
}
})
diff --git a/src/core/diff/strategies/multi-search-replace.ts b/src/core/diff/strategies/multi-search-replace.ts
index 7c07f06ba0..144b794f75 100644
--- a/src/core/diff/strategies/multi-search-replace.ts
+++ b/src/core/diff/strategies/multi-search-replace.ts
@@ -380,6 +380,10 @@ Only use a single line of '=======' between search and replacement content, beca
(everyLineHasLineNumbers(searchContent) && everyLineHasLineNumbers(replaceContent)) ||
(everyLineHasLineNumbers(searchContent) && replaceContent.trim() === "")
+ if (hasAllLineNumbers && startLine === 0) {
+ startLine = parseInt(searchContent.split("\n")[0].split("|")[0])
+ }
+
if (hasAllLineNumbers) {
searchContent = stripLineNumbers(searchContent)
replaceContent = stripLineNumbers(replaceContent)
diff --git a/src/core/environment/__tests__/getEnvironmentDetails.test.ts b/src/core/environment/__tests__/getEnvironmentDetails.test.ts
new file mode 100644
index 0000000000..008b0de14e
--- /dev/null
+++ b/src/core/environment/__tests__/getEnvironmentDetails.test.ts
@@ -0,0 +1,316 @@
+// npx jest src/core/environment/__tests__/getEnvironmentDetails.test.ts
+
+import pWaitFor from "p-wait-for"
+import delay from "delay"
+
+import { getEnvironmentDetails } from "../getEnvironmentDetails"
+import { EXPERIMENT_IDS, experiments } from "../../../shared/experiments"
+import { defaultModeSlug, getFullModeDetails, getModeBySlug, isToolAllowedForMode } from "../../../shared/modes"
+import { getApiMetrics } from "../../../shared/getApiMetrics"
+import { listFiles } from "../../../services/glob/list-files"
+import { TerminalRegistry } from "../../../integrations/terminal/TerminalRegistry"
+import { Terminal } from "../../../integrations/terminal/Terminal"
+import { arePathsEqual } from "../../../utils/path"
+import { FileContextTracker } from "../../context-tracking/FileContextTracker"
+import { ApiHandler } from "../../../api/index"
+import { ClineProvider } from "../../webview/ClineProvider"
+import { RooIgnoreController } from "../../ignore/RooIgnoreController"
+import { formatResponse } from "../../prompts/responses"
+import { Task } from "../../task/Task"
+
+jest.mock("vscode", () => ({
+ window: {
+ tabGroups: { all: [], onDidChangeTabs: jest.fn() },
+ visibleTextEditors: [],
+ },
+ env: {
+ language: "en-US",
+ },
+}))
+
+jest.mock("p-wait-for")
+
+jest.mock("delay")
+
+jest.mock("execa", () => ({
+ execa: jest.fn(),
+}))
+
+jest.mock("../../../shared/experiments")
+jest.mock("../../../shared/modes")
+jest.mock("../../../shared/getApiMetrics")
+jest.mock("../../../services/glob/list-files")
+jest.mock("../../../integrations/terminal/TerminalRegistry")
+jest.mock("../../../integrations/terminal/Terminal")
+jest.mock("../../../utils/path")
+jest.mock("../../prompts/responses")
+
+describe("getEnvironmentDetails", () => {
+ const mockCwd = "/test/path"
+ const mockTaskId = "test-task-id"
+
+ type MockTerminal = {
+ id: string
+ getLastCommand: jest.Mock
+ getProcessesWithOutput: jest.Mock
+ cleanCompletedProcessQueue?: jest.Mock
+ }
+
+ let mockCline: Partial
+ let mockProvider: any
+ let mockState: any
+
+ beforeEach(() => {
+ jest.clearAllMocks()
+
+ mockState = {
+ terminalOutputLineLimit: 100,
+ maxWorkspaceFiles: 50,
+ maxOpenTabsContext: 10,
+ mode: "code",
+ customModes: [],
+ experiments: {},
+ customInstructions: "test instructions",
+ language: "en",
+ showRooIgnoredFiles: true,
+ }
+
+ mockProvider = {
+ getState: jest.fn().mockResolvedValue(mockState),
+ }
+
+ mockCline = {
+ cwd: mockCwd,
+ taskId: mockTaskId,
+ didEditFile: false,
+ fileContextTracker: {
+ getAndClearRecentlyModifiedFiles: jest.fn().mockReturnValue([]),
+ } as unknown as FileContextTracker,
+ rooIgnoreController: {
+ filterPaths: jest.fn((paths: string[]) => paths.join("\n")),
+ cwd: mockCwd,
+ ignoreInstance: {},
+ disposables: [],
+ rooIgnoreContent: "",
+ isPathIgnored: jest.fn(),
+ getIgnoreContent: jest.fn(),
+ updateIgnoreContent: jest.fn(),
+ addToIgnore: jest.fn(),
+ removeFromIgnore: jest.fn(),
+ dispose: jest.fn(),
+ } as unknown as RooIgnoreController,
+ clineMessages: [],
+ api: {
+ getModel: jest.fn().mockReturnValue({ id: "test-model", info: { contextWindow: 100000 } }),
+ createMessage: jest.fn(),
+ countTokens: jest.fn(),
+ } as unknown as ApiHandler,
+ diffEnabled: true,
+ providerRef: {
+ deref: jest.fn().mockReturnValue(mockProvider),
+ [Symbol.toStringTag]: "WeakRef",
+ } as unknown as WeakRef,
+ }
+
+ // Mock other dependencies.
+ ;(getApiMetrics as jest.Mock).mockReturnValue({ contextTokens: 50000, totalCost: 0.25 })
+ ;(getFullModeDetails as jest.Mock).mockResolvedValue({
+ name: "💻 Code",
+ roleDefinition: "You are a code assistant",
+ customInstructions: "Custom instructions",
+ })
+ ;(isToolAllowedForMode as jest.Mock).mockReturnValue(true)
+ ;(listFiles as jest.Mock).mockResolvedValue([["file1.ts", "file2.ts"], false])
+ ;(formatResponse.formatFilesList as jest.Mock).mockReturnValue("file1.ts\nfile2.ts")
+ ;(arePathsEqual as jest.Mock).mockReturnValue(false)
+ ;(Terminal.compressTerminalOutput as jest.Mock).mockImplementation((output: string) => output)
+ ;(TerminalRegistry.getTerminals as jest.Mock).mockReturnValue([])
+ ;(TerminalRegistry.getBackgroundTerminals as jest.Mock).mockReturnValue([])
+ ;(TerminalRegistry.isProcessHot as jest.Mock).mockReturnValue(false)
+ ;(TerminalRegistry.getUnretrievedOutput as jest.Mock).mockReturnValue("")
+ ;(pWaitFor as unknown as jest.Mock).mockResolvedValue(undefined)
+ ;(delay as jest.Mock).mockResolvedValue(undefined)
+ })
+
+ it("should return basic environment details", async () => {
+ const result = await getEnvironmentDetails(mockCline as Task)
+
+ expect(result).toContain("")
+ expect(result).toContain("")
+ expect(result).toContain("# VSCode Visible Files")
+ expect(result).toContain("# VSCode Open Tabs")
+ expect(result).toContain("# Current Time")
+ expect(result).toContain("# Current Context Size (Tokens)")
+ expect(result).toContain("# Current Cost")
+ expect(result).toContain("# Current Mode")
+ expect(result).toContain("test-model")
+
+ expect(mockProvider.getState).toHaveBeenCalled()
+
+ expect(getFullModeDetails).toHaveBeenCalledWith("code", [], undefined, {
+ cwd: mockCwd,
+ globalCustomInstructions: "test instructions",
+ language: "en",
+ })
+
+ expect(getApiMetrics).toHaveBeenCalledWith(mockCline.clineMessages)
+ })
+
+ it("should include file details when includeFileDetails is true", async () => {
+ const result = await getEnvironmentDetails(mockCline as Task, true)
+ expect(result).toContain("# Current Workspace Directory")
+ expect(result).toContain("Files")
+
+ expect(listFiles).toHaveBeenCalledWith(mockCwd, true, 50)
+
+ expect(formatResponse.formatFilesList).toHaveBeenCalledWith(
+ mockCwd,
+ ["file1.ts", "file2.ts"],
+ false,
+ mockCline.rooIgnoreController,
+ true,
+ )
+ })
+
+ it("should not include file details when includeFileDetails is false", async () => {
+ await getEnvironmentDetails(mockCline as Task, false)
+ expect(listFiles).not.toHaveBeenCalled()
+ expect(formatResponse.formatFilesList).not.toHaveBeenCalled()
+ })
+
+ it("should handle desktop directory specially", async () => {
+ ;(arePathsEqual as jest.Mock).mockReturnValue(true)
+ const result = await getEnvironmentDetails(mockCline as Task, true)
+ expect(result).toContain("Desktop files not shown automatically")
+ expect(listFiles).not.toHaveBeenCalled()
+ })
+
+ it("should include recently modified files if any", async () => {
+ ;(mockCline.fileContextTracker!.getAndClearRecentlyModifiedFiles as jest.Mock).mockReturnValue([
+ "modified1.ts",
+ "modified2.ts",
+ ])
+
+ const result = await getEnvironmentDetails(mockCline as Task)
+
+ expect(result).toContain("# Recently Modified Files")
+ expect(result).toContain("modified1.ts")
+ expect(result).toContain("modified2.ts")
+ })
+
+ it("should include active terminal information", async () => {
+ const mockActiveTerminal = {
+ id: "terminal-1",
+ getLastCommand: jest.fn().mockReturnValue("npm test"),
+ getProcessesWithOutput: jest.fn().mockReturnValue([]),
+ } as MockTerminal
+
+ ;(TerminalRegistry.getTerminals as jest.Mock).mockReturnValue([mockActiveTerminal])
+ ;(TerminalRegistry.getUnretrievedOutput as jest.Mock).mockReturnValue("Test output")
+
+ const result = await getEnvironmentDetails(mockCline as Task)
+
+ expect(result).toContain("# Actively Running Terminals")
+ expect(result).toContain("Original command: `npm test`")
+ expect(result).toContain("Test output")
+
+ mockCline.didEditFile = true
+ await getEnvironmentDetails(mockCline as Task)
+ expect(delay).toHaveBeenCalledWith(300)
+
+ expect(pWaitFor).toHaveBeenCalled()
+ })
+
+ it("should include inactive terminals with output", async () => {
+ const mockProcess = {
+ command: "npm build",
+ getUnretrievedOutput: jest.fn().mockReturnValue("Build output"),
+ }
+
+ const mockInactiveTerminal = {
+ id: "terminal-2",
+ getProcessesWithOutput: jest.fn().mockReturnValue([mockProcess]),
+ cleanCompletedProcessQueue: jest.fn(),
+ } as MockTerminal
+
+ ;(TerminalRegistry.getTerminals as jest.Mock).mockImplementation((active: boolean) =>
+ active ? [] : [mockInactiveTerminal],
+ )
+
+ const result = await getEnvironmentDetails(mockCline as Task)
+
+ expect(result).toContain("# Inactive Terminals with Completed Process Output")
+ expect(result).toContain("Terminal terminal-2")
+ expect(result).toContain("Command: `npm build`")
+ expect(result).toContain("Build output")
+
+ expect(mockInactiveTerminal.cleanCompletedProcessQueue).toHaveBeenCalled()
+ })
+
+ it("should include warning when file writing is not allowed", async () => {
+ ;(isToolAllowedForMode as jest.Mock).mockReturnValue(false)
+ ;(getModeBySlug as jest.Mock).mockImplementation((slug: string) => {
+ if (slug === "code") {
+ return { name: "💻 Code" }
+ }
+
+ if (slug === defaultModeSlug) {
+ return { name: "Default Mode" }
+ }
+
+ return null
+ })
+
+ const result = await getEnvironmentDetails(mockCline as Task)
+
+ expect(result).toContain("NOTE: You are currently in '💻 Code' mode, which does not allow write operations")
+ })
+
+ it("should include experiment-specific details when Power Steering is enabled", async () => {
+ mockState.experiments = { [EXPERIMENT_IDS.POWER_STEERING]: true }
+ ;(experiments.isEnabled as jest.Mock).mockReturnValue(true)
+
+ const result = await getEnvironmentDetails(mockCline as Task)
+
+ expect(result).toContain("You are a code assistant")
+ expect(result).toContain("Custom instructions")
+ })
+
+ it("should handle missing provider or state", async () => {
+ // Mock provider to return null.
+ mockCline.providerRef!.deref = jest.fn().mockReturnValue(null)
+
+ const result = await getEnvironmentDetails(mockCline as Task)
+
+ // Verify the function still returns a result.
+ expect(result).toContain("")
+ expect(result).toContain("")
+
+ // Mock provider to return null state.
+ mockCline.providerRef!.deref = jest.fn().mockReturnValue({
+ getState: jest.fn().mockResolvedValue(null),
+ })
+
+ const result2 = await getEnvironmentDetails(mockCline as Task)
+
+ // Verify the function still returns a result.
+ expect(result2).toContain("")
+ expect(result2).toContain("")
+ })
+
+ it("should handle errors gracefully", async () => {
+ ;(pWaitFor as unknown as jest.Mock).mockRejectedValue(new Error("Test error"))
+
+ const mockErrorTerminal = {
+ id: "terminal-1",
+ getLastCommand: jest.fn().mockReturnValue("npm test"),
+ getProcessesWithOutput: jest.fn().mockReturnValue([]),
+ } as MockTerminal
+
+ ;(TerminalRegistry.getTerminals as jest.Mock).mockReturnValue([mockErrorTerminal])
+ ;(TerminalRegistry.getBackgroundTerminals as jest.Mock).mockReturnValue([])
+ ;(mockCline.fileContextTracker!.getAndClearRecentlyModifiedFiles as jest.Mock).mockReturnValue([])
+
+ await expect(getEnvironmentDetails(mockCline as Task)).resolves.not.toThrow()
+ })
+})
diff --git a/src/core/environment/getEnvironmentDetails.ts b/src/core/environment/getEnvironmentDetails.ts
new file mode 100644
index 0000000000..3d8a9cdbc3
--- /dev/null
+++ b/src/core/environment/getEnvironmentDetails.ts
@@ -0,0 +1,269 @@
+import path from "path"
+import os from "os"
+
+import * as vscode from "vscode"
+import pWaitFor from "p-wait-for"
+import delay from "delay"
+
+import { EXPERIMENT_IDS, experiments as Experiments, ExperimentId } from "../../shared/experiments"
+import { formatLanguage } from "../../shared/language"
+import { defaultModeSlug, getFullModeDetails, getModeBySlug, isToolAllowedForMode } from "../../shared/modes"
+import { getApiMetrics } from "../../shared/getApiMetrics"
+import { listFiles } from "../../services/glob/list-files"
+import { TerminalRegistry } from "../../integrations/terminal/TerminalRegistry"
+import { Terminal } from "../../integrations/terminal/Terminal"
+import { arePathsEqual } from "../../utils/path"
+import { formatResponse } from "../prompts/responses"
+
+import { Task } from "../task/Task"
+
+export async function getEnvironmentDetails(cline: Task, includeFileDetails: boolean = false) {
+ let details = ""
+
+ const clineProvider = cline.providerRef.deref()
+ const state = await clineProvider?.getState()
+ const { terminalOutputLineLimit = 500, maxWorkspaceFiles = 200 } = state ?? {}
+
+ // It could be useful for cline to know if the user went from one or no
+ // file to another between messages, so we always include this context.
+ details += "\n\n# VSCode Visible Files"
+
+ const visibleFilePaths = vscode.window.visibleTextEditors
+ ?.map((editor) => editor.document?.uri?.fsPath)
+ .filter(Boolean)
+ .map((absolutePath) => path.relative(cline.cwd, absolutePath))
+ .slice(0, maxWorkspaceFiles)
+
+ // Filter paths through rooIgnoreController
+ const allowedVisibleFiles = cline.rooIgnoreController
+ ? cline.rooIgnoreController.filterPaths(visibleFilePaths)
+ : visibleFilePaths.map((p) => p.toPosix()).join("\n")
+
+ if (allowedVisibleFiles) {
+ details += `\n${allowedVisibleFiles}`
+ } else {
+ details += "\n(No visible files)"
+ }
+
+ details += "\n\n# VSCode Open Tabs"
+ const { maxOpenTabsContext } = state ?? {}
+ const maxTabs = maxOpenTabsContext ?? 20
+ const openTabPaths = vscode.window.tabGroups.all
+ .flatMap((group) => group.tabs)
+ .map((tab) => (tab.input as vscode.TabInputText)?.uri?.fsPath)
+ .filter(Boolean)
+ .map((absolutePath) => path.relative(cline.cwd, absolutePath).toPosix())
+ .slice(0, maxTabs)
+
+ // Filter paths through rooIgnoreController
+ const allowedOpenTabs = cline.rooIgnoreController
+ ? cline.rooIgnoreController.filterPaths(openTabPaths)
+ : openTabPaths.map((p) => p.toPosix()).join("\n")
+
+ if (allowedOpenTabs) {
+ details += `\n${allowedOpenTabs}`
+ } else {
+ details += "\n(No open tabs)"
+ }
+
+ // Get task-specific and background terminals.
+ const busyTerminals = [
+ ...TerminalRegistry.getTerminals(true, cline.taskId),
+ ...TerminalRegistry.getBackgroundTerminals(true),
+ ]
+
+ const inactiveTerminals = [
+ ...TerminalRegistry.getTerminals(false, cline.taskId),
+ ...TerminalRegistry.getBackgroundTerminals(false),
+ ]
+
+ if (busyTerminals.length > 0) {
+ if (cline.didEditFile) {
+ await delay(300) // Delay after saving file to let terminals catch up.
+ }
+
+ // Wait for terminals to cool down.
+ await pWaitFor(() => busyTerminals.every((t) => !TerminalRegistry.isProcessHot(t.id)), {
+ interval: 100,
+ timeout: 5_000,
+ }).catch(() => {})
+ }
+
+ // Reset, this lets us know when to wait for saved files to update terminals.
+ cline.didEditFile = false
+
+ // Waiting for updated diagnostics lets terminal output be the most
+ // up-to-date possible.
+ let terminalDetails = ""
+
+ if (busyTerminals.length > 0) {
+ // Terminals are cool, let's retrieve their output.
+ terminalDetails += "\n\n# Actively Running Terminals"
+
+ for (const busyTerminal of busyTerminals) {
+ terminalDetails += `\n## Original command: \`${busyTerminal.getLastCommand()}\``
+ let newOutput = TerminalRegistry.getUnretrievedOutput(busyTerminal.id)
+
+ if (newOutput) {
+ newOutput = Terminal.compressTerminalOutput(newOutput, terminalOutputLineLimit)
+ terminalDetails += `\n### New Output\n${newOutput}`
+ }
+ }
+ }
+
+ // First check if any inactive terminals in this task have completed
+ // processes with output.
+ const terminalsWithOutput = inactiveTerminals.filter((terminal) => {
+ const completedProcesses = terminal.getProcessesWithOutput()
+ return completedProcesses.length > 0
+ })
+
+ // Only add the header if there are terminals with output.
+ if (terminalsWithOutput.length > 0) {
+ terminalDetails += "\n\n# Inactive Terminals with Completed Process Output"
+
+ // Process each terminal with output.
+ for (const inactiveTerminal of terminalsWithOutput) {
+ let terminalOutputs: string[] = []
+
+ // Get output from completed processes queue.
+ const completedProcesses = inactiveTerminal.getProcessesWithOutput()
+
+ for (const process of completedProcesses) {
+ let output = process.getUnretrievedOutput()
+
+ if (output) {
+ output = Terminal.compressTerminalOutput(output, terminalOutputLineLimit)
+ terminalOutputs.push(`Command: \`${process.command}\`\n${output}`)
+ }
+ }
+
+ // Clean the queue after retrieving output.
+ inactiveTerminal.cleanCompletedProcessQueue()
+
+ // Add this terminal's outputs to the details.
+ if (terminalOutputs.length > 0) {
+ terminalDetails += `\n## Terminal ${inactiveTerminal.id}`
+ terminalOutputs.forEach((output) => {
+ terminalDetails += `\n### New Output\n${output}`
+ })
+ }
+ }
+ }
+
+ // console.log(`[Cline#getEnvironmentDetails] terminalDetails: ${terminalDetails}`)
+
+ // Add recently modified files section.
+ const recentlyModifiedFiles = cline.fileContextTracker.getAndClearRecentlyModifiedFiles()
+
+ if (recentlyModifiedFiles.length > 0) {
+ details +=
+ "\n\n# Recently Modified Files\nThese files have been modified since you last accessed them (file was just edited so you may need to re-read it before editing):"
+ for (const filePath of recentlyModifiedFiles) {
+ details += `\n${filePath}`
+ }
+ }
+
+ if (terminalDetails) {
+ details += terminalDetails
+ }
+
+ // Add current time information with timezone.
+ const now = new Date()
+
+ const formatter = new Intl.DateTimeFormat(undefined, {
+ year: "numeric",
+ month: "numeric",
+ day: "numeric",
+ hour: "numeric",
+ minute: "numeric",
+ second: "numeric",
+ hour12: true,
+ })
+
+ const timeZone = formatter.resolvedOptions().timeZone
+ const timeZoneOffset = -now.getTimezoneOffset() / 60 // Convert to hours and invert sign to match conventional notation
+ const timeZoneOffsetHours = Math.floor(Math.abs(timeZoneOffset))
+ const timeZoneOffsetMinutes = Math.abs(Math.round((Math.abs(timeZoneOffset) - timeZoneOffsetHours) * 60))
+ const timeZoneOffsetStr = `${timeZoneOffset >= 0 ? "+" : "-"}${timeZoneOffsetHours}:${timeZoneOffsetMinutes.toString().padStart(2, "0")}`
+ details += `\n\n# Current Time\n${formatter.format(now)} (${timeZone}, UTC${timeZoneOffsetStr})`
+
+ // Add context tokens information.
+ const { contextTokens, totalCost } = getApiMetrics(cline.clineMessages)
+ const { id: modelId, info: modelInfo } = cline.api.getModel()
+ const contextWindow = modelInfo.contextWindow
+
+ const contextPercentage =
+ contextTokens && contextWindow ? Math.round((contextTokens / contextWindow) * 100) : undefined
+
+ details += `\n\n# Current Context Size (Tokens)\n${contextTokens ? `${contextTokens.toLocaleString()} (${contextPercentage}%)` : "(Not available)"}`
+ details += `\n\n# Current Cost\n${totalCost !== null ? `$${totalCost.toFixed(2)}` : "(Not available)"}`
+
+ // Add current mode and any mode-specific warnings.
+ const {
+ mode,
+ customModes,
+ customModePrompts,
+ experiments = {} as Record,
+ customInstructions: globalCustomInstructions,
+ language,
+ } = state ?? {}
+
+ const currentMode = mode ?? defaultModeSlug
+
+ const modeDetails = await getFullModeDetails(currentMode, customModes, customModePrompts, {
+ cwd: cline.cwd,
+ globalCustomInstructions,
+ language: language ?? formatLanguage(vscode.env.language),
+ })
+
+ details += `\n\n# Current Mode\n`
+ details += `${currentMode}\n`
+ details += `${modeDetails.name}\n`
+ details += `${modelId}\n`
+
+ if (Experiments.isEnabled(experiments ?? {}, EXPERIMENT_IDS.POWER_STEERING)) {
+ details += `${modeDetails.roleDefinition}\n`
+
+ if (modeDetails.customInstructions) {
+ details += `${modeDetails.customInstructions}\n`
+ }
+ }
+
+ // Add warning if not in code mode.
+ if (
+ !isToolAllowedForMode("write_to_file", currentMode, customModes ?? [], { apply_diff: cline.diffEnabled }) &&
+ !isToolAllowedForMode("apply_diff", currentMode, customModes ?? [], { apply_diff: cline.diffEnabled })
+ ) {
+ const currentModeName = getModeBySlug(currentMode, customModes)?.name ?? currentMode
+ const defaultModeName = getModeBySlug(defaultModeSlug, customModes)?.name ?? defaultModeSlug
+ details += `\n\nNOTE: You are currently in '${currentModeName}' mode, which does not allow write operations. To write files, the user will need to switch to a mode that supports file writing, such as '${defaultModeName}' mode.`
+ }
+
+ if (includeFileDetails) {
+ details += `\n\n# Current Workspace Directory (${cline.cwd.toPosix()}) Files\n`
+ const isDesktop = arePathsEqual(cline.cwd, path.join(os.homedir(), "Desktop"))
+
+ if (isDesktop) {
+ // Don't want to immediately access desktop since it would show
+ // permission popup.
+ details += "(Desktop files not shown automatically. Use list_files to explore if needed.)"
+ } else {
+ const maxFiles = maxWorkspaceFiles ?? 200
+ const [files, didHitLimit] = await listFiles(cline.cwd, true, maxFiles)
+ const { showRooIgnoredFiles = true } = state ?? {}
+
+ const result = formatResponse.formatFilesList(
+ cline.cwd,
+ files,
+ didHitLimit,
+ cline.rooIgnoreController,
+ showRooIgnoredFiles,
+ )
+
+ details += result
+ }
+ }
+
+ return `\n${details.trim()}\n`
+}
diff --git a/src/core/mentions/__tests__/index.test.ts b/src/core/mentions/__tests__/index.test.ts
index a85fe1f0a8..d9399bb47d 100644
--- a/src/core/mentions/__tests__/index.test.ts
+++ b/src/core/mentions/__tests__/index.test.ts
@@ -87,6 +87,24 @@ import * as git from "../../../utils/git"
import { getWorkspacePath } from "../../../utils/path"
;(getWorkspacePath as jest.Mock).mockReturnValue("/test/workspace")
+jest.mock("fs/promises", () => ({
+ stat: jest.fn(),
+ readdir: jest.fn(),
+}))
+import fs from "fs/promises"
+import * as path from "path"
+
+jest.mock("../../../integrations/misc/open-file", () => ({
+ openFile: jest.fn(),
+}))
+import { openFile } from "../../../integrations/misc/open-file"
+
+jest.mock("../../../integrations/misc/extract-text", () => ({
+ extractTextFromFile: jest.fn(),
+}))
+
+import * as vscode from "vscode"
+
describe("mentions", () => {
const mockCwd = "/test/workspace"
let mockUrlContentFetcher: UrlContentFetcher
@@ -112,6 +130,16 @@ describe("mentions", () => {
})
describe("parseMentions", () => {
+ let mockUrlFetcher: UrlContentFetcher
+
+ beforeEach(() => {
+ mockUrlFetcher = new (UrlContentFetcher as jest.Mock)()
+ ;(fs.stat as jest.Mock).mockResolvedValue({ isFile: () => true, isDirectory: () => false })
+ ;(require("../../../integrations/misc/extract-text").extractTextFromFile as jest.Mock).mockResolvedValue(
+ "Mock file content",
+ )
+ })
+
it("should parse git commit mentions", async () => {
const commitHash = "abc1234"
const commitInfo = `abc1234 Fix bug in parser
@@ -144,35 +172,72 @@ Detailed commit message with multiple lines
expect(result).toContain(``)
expect(result).toContain(`Error fetching commit info: ${errorMessage}`)
})
- })
- describe("openMention", () => {
- it("should handle file paths and problems", async () => {
- // Mock stat to simulate file not existing
- mockVscode.workspace.fs.stat.mockRejectedValueOnce(new Error("File does not exist"))
+ it("should correctly parse mentions with escaped spaces and fetch content", async () => {
+ const text = "Please check the file @/path/to/file\\ with\\ spaces.txt"
+ const expectedUnescaped = "path/to/file with spaces.txt" // Note: leading '/' removed by slice(1) in parseMentions
+ const expectedAbsPath = path.resolve(mockCwd, expectedUnescaped)
- // Call openMention and wait for it to complete
- await openMention("/path/to/file")
+ const result = await parseMentions(text, mockCwd, mockUrlFetcher)
- // Verify error handling
- expect(mockExecuteCommand).not.toHaveBeenCalled()
- expect(mockOpenExternal).not.toHaveBeenCalled()
- expect(mockVscode.window.showErrorMessage).toHaveBeenCalledWith("Could not open file: File does not exist")
+ // Check if fs.stat was called with the unescaped path
+ expect(fs.stat).toHaveBeenCalledWith(expectedAbsPath)
+ // Check if extractTextFromFile was called with the unescaped path
+ expect(require("../../../integrations/misc/extract-text").extractTextFromFile).toHaveBeenCalledWith(
+ expectedAbsPath,
+ )
- // Reset mocks for next test
- jest.clearAllMocks()
+ // Check the output format
+ expect(result).toContain(`'path/to/file\\ with\\ spaces.txt' (see below for file content)`)
+ expect(result).toContain(
+ `\nMock file content\n`,
+ )
+ })
- // Test problems command
- await openMention("problems")
- expect(mockExecuteCommand).toHaveBeenCalledWith("workbench.actions.view.problems")
+ it("should handle folder mentions with escaped spaces", async () => {
+ const text = "Look in @/my\\ documents/folder\\ name/"
+ const expectedUnescaped = "my documents/folder name/"
+ const expectedAbsPath = path.resolve(mockCwd, expectedUnescaped)
+ ;(fs.stat as jest.Mock).mockResolvedValue({ isFile: () => false, isDirectory: () => true })
+ ;(fs.readdir as jest.Mock).mockResolvedValue([]) // Empty directory
+
+ const result = await parseMentions(text, mockCwd, mockUrlFetcher)
+
+ expect(fs.stat).toHaveBeenCalledWith(expectedAbsPath)
+ expect(fs.readdir).toHaveBeenCalledWith(expectedAbsPath, { withFileTypes: true })
+ expect(result).toContain(`'my\\ documents/folder\\ name/' (see below for folder content)`)
+ expect(result).toContain(``) // Content check might be more complex
+ })
+
+ it("should handle errors when accessing paths with escaped spaces", async () => {
+ const text = "Check @/nonexistent\\ file.txt"
+ const expectedUnescaped = "nonexistent file.txt"
+ const expectedAbsPath = path.resolve(mockCwd, expectedUnescaped)
+ const mockError = new Error("ENOENT: no such file or directory")
+ ;(fs.stat as jest.Mock).mockRejectedValue(mockError)
+
+ const result = await parseMentions(text, mockCwd, mockUrlFetcher)
+
+ expect(fs.stat).toHaveBeenCalledWith(expectedAbsPath)
+ expect(result).toContain(
+ `\nError fetching content: Failed to access path "nonexistent\\ file.txt": ${mockError.message}\n`,
+ )
+ })
+
+ // Add more tests for parseMentions if needed (URLs, other mentions combined with escaped paths etc.)
+ })
+
+ describe("openMention", () => {
+ beforeEach(() => {
+ ;(getWorkspacePath as jest.Mock).mockReturnValue(mockCwd)
})
it("should handle URLs", async () => {
const url = "https://example.com"
await openMention(url)
- const mockUri = mockVscode.Uri.parse(url)
- expect(mockVscode.env.openExternal).toHaveBeenCalled()
- const calledArg = mockVscode.env.openExternal.mock.calls[0][0]
+ const mockUri = vscode.Uri.parse(url)
+ expect(vscode.env.openExternal).toHaveBeenCalled()
+ const calledArg = (vscode.env.openExternal as jest.Mock).mock.calls[0][0]
expect(calledArg).toEqual(
expect.objectContaining({
scheme: mockUri.scheme,
@@ -183,5 +248,62 @@ Detailed commit message with multiple lines
}),
)
})
+
+ it("should unescape file path before opening", async () => {
+ const mention = "/file\\ with\\ spaces.txt"
+ const expectedUnescaped = "file with spaces.txt"
+ const expectedAbsPath = path.resolve(mockCwd, expectedUnescaped)
+
+ await openMention(mention)
+
+ expect(openFile).toHaveBeenCalledWith(expectedAbsPath)
+ expect(vscode.commands.executeCommand).not.toHaveBeenCalled()
+ })
+
+ it("should unescape folder path before revealing", async () => {
+ const mention = "/folder\\ with\\ spaces/"
+ const expectedUnescaped = "folder with spaces/"
+ const expectedAbsPath = path.resolve(mockCwd, expectedUnescaped)
+ const expectedUri = { fsPath: expectedAbsPath } // From mock
+ ;(vscode.Uri.file as jest.Mock).mockReturnValue(expectedUri)
+
+ await openMention(mention)
+
+ expect(vscode.commands.executeCommand).toHaveBeenCalledWith("revealInExplorer", expectedUri)
+ expect(vscode.Uri.file).toHaveBeenCalledWith(expectedAbsPath)
+ expect(openFile).not.toHaveBeenCalled()
+ })
+
+ it("should handle mentions without paths correctly", async () => {
+ await openMention("problems")
+ expect(vscode.commands.executeCommand).toHaveBeenCalledWith("workbench.actions.view.problems")
+
+ await openMention("terminal")
+ expect(vscode.commands.executeCommand).toHaveBeenCalledWith("workbench.action.terminal.focus")
+
+ await openMention("http://example.com")
+ expect(vscode.env.openExternal).toHaveBeenCalled() // Check if called, specific URI mock might be needed for detailed check
+
+ await openMention("git-changes") // Assuming no specific action for this yet
+ // Add expectations if an action is defined for git-changes
+
+ await openMention("a1b2c3d") // Assuming no specific action for commit hashes yet
+ // Add expectations if an action is defined for commit hashes
+ })
+
+ it("should do nothing if mention is undefined or empty", async () => {
+ await openMention(undefined)
+ await openMention("")
+ expect(openFile).not.toHaveBeenCalled()
+ expect(vscode.commands.executeCommand).not.toHaveBeenCalled()
+ expect(vscode.env.openExternal).not.toHaveBeenCalled()
+ })
+
+ it("should do nothing if cwd is not available", async () => {
+ ;(getWorkspacePath as jest.Mock).mockReturnValue(undefined)
+ await openMention("/some\\ path.txt")
+ expect(openFile).not.toHaveBeenCalled()
+ expect(vscode.commands.executeCommand).not.toHaveBeenCalled()
+ })
})
})
diff --git a/src/core/mentions/index.ts b/src/core/mentions/index.ts
index 73b4e71613..d53a3ff3ed 100644
--- a/src/core/mentions/index.ts
+++ b/src/core/mentions/index.ts
@@ -1,15 +1,20 @@
-import * as vscode from "vscode"
-import * as path from "path"
-import { openFile } from "../../integrations/misc/open-file"
-import { UrlContentFetcher } from "../../services/browser/UrlContentFetcher"
-import { mentionRegexGlobal } from "../../shared/context-mentions"
import fs from "fs/promises"
-import { extractTextFromFile } from "../../integrations/misc/extract-text"
+import * as path from "path"
+
+import * as vscode from "vscode"
import { isBinaryFile } from "isbinaryfile"
-import { diagnosticsToProblemsString } from "../../integrations/diagnostics"
+
+import { mentionRegexGlobal, unescapeSpaces } from "../../shared/context-mentions"
+
import { getCommitInfo, getWorkingState } from "../../utils/git"
-import { getLatestTerminalOutput } from "../../integrations/terminal/get-latest-output"
import { getWorkspacePath } from "../../utils/path"
+
+import { openFile } from "../../integrations/misc/open-file"
+import { extractTextFromFile } from "../../integrations/misc/extract-text"
+import { diagnosticsToProblemsString } from "../../integrations/diagnostics"
+
+import { UrlContentFetcher } from "../../services/browser/UrlContentFetcher"
+
import { FileContextTracker } from "../context-tracking/FileContextTracker"
export async function openMention(mention?: string): Promise {
@@ -23,7 +28,8 @@ export async function openMention(mention?: string): Promise {
}
if (mention.startsWith("/")) {
- const relPath = mention.slice(1)
+ // Slice off the leading slash and unescape any spaces in the path
+ const relPath = unescapeSpaces(mention.slice(1))
const absPath = path.resolve(cwd, relPath)
if (mention.endsWith("/")) {
vscode.commands.executeCommand("revealInExplorer", vscode.Uri.file(absPath))
@@ -156,7 +162,9 @@ export async function parseMentions(
}
async function getFileOrFolderContent(mentionPath: string, cwd: string): Promise {
- const absPath = path.resolve(cwd, mentionPath)
+ // Unescape spaces in the path before resolving it
+ const unescapedPath = unescapeSpaces(mentionPath)
+ const absPath = path.resolve(cwd, unescapedPath)
try {
const stats = await fs.stat(absPath)
@@ -221,3 +229,53 @@ async function getWorkspaceProblems(cwd: string): Promise {
}
return result
}
+
+/**
+ * Gets the contents of the active terminal
+ * @returns The terminal contents as a string
+ */
+export async function getLatestTerminalOutput(): Promise {
+ // Store original clipboard content to restore later
+ const originalClipboard = await vscode.env.clipboard.readText()
+
+ try {
+ // Select terminal content
+ await vscode.commands.executeCommand("workbench.action.terminal.selectAll")
+
+ // Copy selection to clipboard
+ await vscode.commands.executeCommand("workbench.action.terminal.copySelection")
+
+ // Clear the selection
+ await vscode.commands.executeCommand("workbench.action.terminal.clearSelection")
+
+ // Get terminal contents from clipboard
+ let terminalContents = (await vscode.env.clipboard.readText()).trim()
+
+ // Check if there's actually a terminal open
+ if (terminalContents === originalClipboard) {
+ return ""
+ }
+
+ // Clean up command separation
+ const lines = terminalContents.split("\n")
+ const lastLine = lines.pop()?.trim()
+
+ if (lastLine) {
+ let i = lines.length - 1
+
+ while (i >= 0 && !lines[i].trim().startsWith(lastLine)) {
+ i--
+ }
+
+ terminalContents = lines.slice(Math.max(i, 0)).join("\n")
+ }
+
+ return terminalContents
+ } finally {
+ // Restore original clipboard content
+ await vscode.env.clipboard.writeText(originalClipboard)
+ }
+}
+
+// Export processUserContentMentions from its own file
+export { processUserContentMentions } from "./processUserContentMentions"
diff --git a/src/core/mentions/processUserContentMentions.ts b/src/core/mentions/processUserContentMentions.ts
new file mode 100644
index 0000000000..2b69486a86
--- /dev/null
+++ b/src/core/mentions/processUserContentMentions.ts
@@ -0,0 +1,81 @@
+import { Anthropic } from "@anthropic-ai/sdk"
+import { parseMentions } from "./index"
+import { UrlContentFetcher } from "../../services/browser/UrlContentFetcher"
+import { FileContextTracker } from "../context-tracking/FileContextTracker"
+
+/**
+ * Process mentions in user content, specifically within task and feedback tags
+ */
+export async function processUserContentMentions({
+ userContent,
+ cwd,
+ urlContentFetcher,
+ fileContextTracker,
+}: {
+ userContent: Anthropic.Messages.ContentBlockParam[]
+ cwd: string
+ urlContentFetcher: UrlContentFetcher
+ fileContextTracker: FileContextTracker
+}) {
+ // Process userContent array, which contains various block types:
+ // TextBlockParam, ImageBlockParam, ToolUseBlockParam, and ToolResultBlockParam.
+ // We need to apply parseMentions() to:
+ // 1. All TextBlockParam's text (first user message with task)
+ // 2. ToolResultBlockParam's content/context text arrays if it contains
+ // "" (see formatToolDeniedFeedback, attemptCompletion,
+ // executeCommand, and consecutiveMistakeCount >= 3) or ""
+ // (see askFollowupQuestion), we place all user generated content in
+ // these tags so they can effectively be used as markers for when we
+ // should parse mentions).
+ return Promise.all(
+ userContent.map(async (block) => {
+ const shouldProcessMentions = (text: string) => text.includes("") || text.includes("")
+
+ if (block.type === "text") {
+ if (shouldProcessMentions(block.text)) {
+ return {
+ ...block,
+ text: await parseMentions(block.text, cwd, urlContentFetcher, fileContextTracker),
+ }
+ }
+
+ return block
+ } else if (block.type === "tool_result") {
+ if (typeof block.content === "string") {
+ if (shouldProcessMentions(block.content)) {
+ return {
+ ...block,
+ content: await parseMentions(block.content, cwd, urlContentFetcher, fileContextTracker),
+ }
+ }
+
+ return block
+ } else if (Array.isArray(block.content)) {
+ const parsedContent = await Promise.all(
+ block.content.map(async (contentBlock) => {
+ if (contentBlock.type === "text" && shouldProcessMentions(contentBlock.text)) {
+ return {
+ ...contentBlock,
+ text: await parseMentions(
+ contentBlock.text,
+ cwd,
+ urlContentFetcher,
+ fileContextTracker,
+ ),
+ }
+ }
+
+ return contentBlock
+ }),
+ )
+
+ return { ...block, content: parsedContent }
+ }
+
+ return block
+ }
+
+ return block
+ }),
+ )
+}
diff --git a/src/core/prompts/__tests__/__snapshots__/system.test.ts.snap b/src/core/prompts/__tests__/__snapshots__/system.test.ts.snap
index ade0bd1f85..f0860e0e39 100644
--- a/src/core/prompts/__tests__/__snapshots__/system.test.ts.snap
+++ b/src/core/prompts/__tests__/__snapshots__/system.test.ts.snap
@@ -5,27 +5,33 @@ exports[`SYSTEM_PROMPT should exclude diff strategy tool description when diffEn
====
+MARKDOWN RULES
+
+ALL responses MUST show ANY \`language construct\` OR filename reterence as clickable, exactly as [\`filename OR language.declaration()\`](relative/file/path.ext:line); line is required for \`syntax\` and optional for filename links. This applies to ALL markdown responses and ALSO those in
+
+====
+
TOOL USE
You have access to a set of tools that are executed upon the user's approval. You can use one tool per message, and will receive the result of that tool use in the user's response. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use.
# Tool Use Formatting
-Tool use is formatted using XML-style tags. The tool name is enclosed in opening and closing tags, and each parameter is similarly enclosed within its own set of tags. Here's the structure:
+Tool uses are formatted using XML-style tags. The tool name itself becomes the XML tag name. Each parameter is enclosed within its own set of tags. Here's the structure:
-
+value1value2
...
-
+
-For example:
+For example, to use the read_file tool:
src/main.js
-Always adhere to this format for the tool use to ensure proper parsing and execution.
+Always use the actual tool name as the XML tag name for proper parsing and execution.
# Tools
@@ -335,10 +341,10 @@ Example: Requesting to switch to code mode
## new_task
-Description: Create a new task with a specified starting mode and initial message. This tool instructs the system to create a new Cline instance in the given mode with the provided message.
+Description: This will let you create a new task instance in the chosen mode using your provided message.
Parameters:
-- mode: (required) The slug of the mode to start the new task in (e.g., "code", "ask", "architect").
+- mode: (required) The slug of the mode to start the new task in (e.g., "code", "debug", "architect").
- message: (required) The initial user message or instructions for this new task.
Usage:
@@ -472,27 +478,33 @@ exports[`SYSTEM_PROMPT should exclude diff strategy tool description when diffEn
====
+MARKDOWN RULES
+
+ALL responses MUST show ANY \`language construct\` OR filename reterence as clickable, exactly as [\`filename OR language.declaration()\`](relative/file/path.ext:line); line is required for \`syntax\` and optional for filename links. This applies to ALL markdown responses and ALSO those in
+
+====
+
TOOL USE
You have access to a set of tools that are executed upon the user's approval. You can use one tool per message, and will receive the result of that tool use in the user's response. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use.
# Tool Use Formatting
-Tool use is formatted using XML-style tags. The tool name is enclosed in opening and closing tags, and each parameter is similarly enclosed within its own set of tags. Here's the structure:
+Tool uses are formatted using XML-style tags. The tool name itself becomes the XML tag name. Each parameter is enclosed within its own set of tags. Here's the structure:
-
+value1value2
...
-
+
-For example:
+For example, to use the read_file tool:
src/main.js
-Always adhere to this format for the tool use to ensure proper parsing and execution.
+Always use the actual tool name as the XML tag name for proper parsing and execution.
# Tools
@@ -802,10 +814,10 @@ Example: Requesting to switch to code mode
## new_task
-Description: Create a new task with a specified starting mode and initial message. This tool instructs the system to create a new Cline instance in the given mode with the provided message.
+Description: This will let you create a new task instance in the chosen mode using your provided message.
Parameters:
-- mode: (required) The slug of the mode to start the new task in (e.g., "code", "ask", "architect").
+- mode: (required) The slug of the mode to start the new task in (e.g., "code", "debug", "architect").
- message: (required) The initial user message or instructions for this new task.
Usage:
@@ -939,27 +951,33 @@ exports[`SYSTEM_PROMPT should explicitly handle undefined mcpHub 1`] = `
====
+MARKDOWN RULES
+
+ALL responses MUST show ANY \`language construct\` OR filename reterence as clickable, exactly as [\`filename OR language.declaration()\`](relative/file/path.ext:line); line is required for \`syntax\` and optional for filename links. This applies to ALL markdown responses and ALSO those in
+
+====
+
TOOL USE
You have access to a set of tools that are executed upon the user's approval. You can use one tool per message, and will receive the result of that tool use in the user's response. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use.
# Tool Use Formatting
-Tool use is formatted using XML-style tags. The tool name is enclosed in opening and closing tags, and each parameter is similarly enclosed within its own set of tags. Here's the structure:
+Tool uses are formatted using XML-style tags. The tool name itself becomes the XML tag name. Each parameter is enclosed within its own set of tags. Here's the structure:
-
+value1value2
...
-
+
-For example:
+For example, to use the read_file tool:
src/main.js
-Always adhere to this format for the tool use to ensure proper parsing and execution.
+Always use the actual tool name as the XML tag name for proper parsing and execution.
# Tools
@@ -1269,10 +1287,10 @@ Example: Requesting to switch to code mode
## new_task
-Description: Create a new task with a specified starting mode and initial message. This tool instructs the system to create a new Cline instance in the given mode with the provided message.
+Description: This will let you create a new task instance in the chosen mode using your provided message.
Parameters:
-- mode: (required) The slug of the mode to start the new task in (e.g., "code", "ask", "architect").
+- mode: (required) The slug of the mode to start the new task in (e.g., "code", "debug", "architect").
- message: (required) The initial user message or instructions for this new task.
Usage:
@@ -1406,27 +1424,33 @@ exports[`SYSTEM_PROMPT should handle different browser viewport sizes 1`] = `
====
+MARKDOWN RULES
+
+ALL responses MUST show ANY \`language construct\` OR filename reterence as clickable, exactly as [\`filename OR language.declaration()\`](relative/file/path.ext:line); line is required for \`syntax\` and optional for filename links. This applies to ALL markdown responses and ALSO those in
+
+====
+
TOOL USE
You have access to a set of tools that are executed upon the user's approval. You can use one tool per message, and will receive the result of that tool use in the user's response. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use.
# Tool Use Formatting
-Tool use is formatted using XML-style tags. The tool name is enclosed in opening and closing tags, and each parameter is similarly enclosed within its own set of tags. Here's the structure:
+Tool uses are formatted using XML-style tags. The tool name itself becomes the XML tag name. Each parameter is enclosed within its own set of tags. Here's the structure:
-
+value1value2
...
-
+
-For example:
+For example, to use the read_file tool:
src/main.js
-Always adhere to this format for the tool use to ensure proper parsing and execution.
+Always use the actual tool name as the XML tag name for proper parsing and execution.
# Tools
@@ -1789,10 +1813,10 @@ Example: Requesting to switch to code mode
## new_task
-Description: Create a new task with a specified starting mode and initial message. This tool instructs the system to create a new Cline instance in the given mode with the provided message.
+Description: This will let you create a new task instance in the chosen mode using your provided message.
Parameters:
-- mode: (required) The slug of the mode to start the new task in (e.g., "code", "ask", "architect").
+- mode: (required) The slug of the mode to start the new task in (e.g., "code", "debug", "architect").
- message: (required) The initial user message or instructions for this new task.
Usage:
@@ -1929,27 +1953,33 @@ exports[`SYSTEM_PROMPT should include MCP server info when mcpHub is provided 1`
====
+MARKDOWN RULES
+
+ALL responses MUST show ANY \`language construct\` OR filename reterence as clickable, exactly as [\`filename OR language.declaration()\`](relative/file/path.ext:line); line is required for \`syntax\` and optional for filename links. This applies to ALL markdown responses and ALSO those in
+
+====
+
TOOL USE
You have access to a set of tools that are executed upon the user's approval. You can use one tool per message, and will receive the result of that tool use in the user's response. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use.
# Tool Use Formatting
-Tool use is formatted using XML-style tags. The tool name is enclosed in opening and closing tags, and each parameter is similarly enclosed within its own set of tags. Here's the structure:
+Tool uses are formatted using XML-style tags. The tool name itself becomes the XML tag name. Each parameter is enclosed within its own set of tags. Here's the structure:
-
+value1value2
...
-
+
-For example:
+For example, to use the read_file tool:
src/main.js
-Always adhere to this format for the tool use to ensure proper parsing and execution.
+Always use the actual tool name as the XML tag name for proper parsing and execution.
# Tools
@@ -2308,10 +2338,10 @@ Example: Requesting to switch to code mode
## new_task
-Description: Create a new task with a specified starting mode and initial message. This tool instructs the system to create a new Cline instance in the given mode with the provided message.
+Description: This will let you create a new task instance in the chosen mode using your provided message.
Parameters:
-- mode: (required) The slug of the mode to start the new task in (e.g., "code", "ask", "architect").
+- mode: (required) The slug of the mode to start the new task in (e.g., "code", "debug", "architect").
- message: (required) The initial user message or instructions for this new task.
Usage:
@@ -2464,27 +2494,33 @@ exports[`SYSTEM_PROMPT should include browser actions when supportsComputerUse i
====
+MARKDOWN RULES
+
+ALL responses MUST show ANY \`language construct\` OR filename reterence as clickable, exactly as [\`filename OR language.declaration()\`](relative/file/path.ext:line); line is required for \`syntax\` and optional for filename links. This applies to ALL markdown responses and ALSO those in
+
+====
+
TOOL USE
You have access to a set of tools that are executed upon the user's approval. You can use one tool per message, and will receive the result of that tool use in the user's response. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use.
# Tool Use Formatting
-Tool use is formatted using XML-style tags. The tool name is enclosed in opening and closing tags, and each parameter is similarly enclosed within its own set of tags. Here's the structure:
+Tool uses are formatted using XML-style tags. The tool name itself becomes the XML tag name. Each parameter is enclosed within its own set of tags. Here's the structure:
-
+value1value2
...
-
+
-For example:
+For example, to use the read_file tool:
src/main.js
-Always adhere to this format for the tool use to ensure proper parsing and execution.
+Always use the actual tool name as the XML tag name for proper parsing and execution.
# Tools
@@ -2847,10 +2883,10 @@ Example: Requesting to switch to code mode
## new_task
-Description: Create a new task with a specified starting mode and initial message. This tool instructs the system to create a new Cline instance in the given mode with the provided message.
+Description: This will let you create a new task instance in the chosen mode using your provided message.
Parameters:
-- mode: (required) The slug of the mode to start the new task in (e.g., "code", "ask", "architect").
+- mode: (required) The slug of the mode to start the new task in (e.g., "code", "debug", "architect").
- message: (required) The initial user message or instructions for this new task.
Usage:
@@ -2987,27 +3023,33 @@ exports[`SYSTEM_PROMPT should include diff strategy tool description when diffEn
====
+MARKDOWN RULES
+
+ALL responses MUST show ANY \`language construct\` OR filename reterence as clickable, exactly as [\`filename OR language.declaration()\`](relative/file/path.ext:line); line is required for \`syntax\` and optional for filename links. This applies to ALL markdown responses and ALSO those in
+
+====
+
TOOL USE
You have access to a set of tools that are executed upon the user's approval. You can use one tool per message, and will receive the result of that tool use in the user's response. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use.
# Tool Use Formatting
-Tool use is formatted using XML-style tags. The tool name is enclosed in opening and closing tags, and each parameter is similarly enclosed within its own set of tags. Here's the structure:
+Tool uses are formatted using XML-style tags. The tool name itself becomes the XML tag name. Each parameter is enclosed within its own set of tags. Here's the structure:
-
+value1value2
...
-
+
-For example:
+For example, to use the read_file tool:
src/main.js
-Always adhere to this format for the tool use to ensure proper parsing and execution.
+Always use the actual tool name as the XML tag name for proper parsing and execution.
# Tools
@@ -3407,10 +3449,10 @@ Example: Requesting to switch to code mode
## new_task
-Description: Create a new task with a specified starting mode and initial message. This tool instructs the system to create a new Cline instance in the given mode with the provided message.
+Description: This will let you create a new task instance in the chosen mode using your provided message.
Parameters:
-- mode: (required) The slug of the mode to start the new task in (e.g., "code", "ask", "architect").
+- mode: (required) The slug of the mode to start the new task in (e.g., "code", "debug", "architect").
- message: (required) The initial user message or instructions for this new task.
Usage:
@@ -3544,27 +3586,33 @@ exports[`SYSTEM_PROMPT should maintain consistent system prompt 1`] = `
====
+MARKDOWN RULES
+
+ALL responses MUST show ANY \`language construct\` OR filename reterence as clickable, exactly as [\`filename OR language.declaration()\`](relative/file/path.ext:line); line is required for \`syntax\` and optional for filename links. This applies to ALL markdown responses and ALSO those in
+
+====
+
TOOL USE
You have access to a set of tools that are executed upon the user's approval. You can use one tool per message, and will receive the result of that tool use in the user's response. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use.
# Tool Use Formatting
-Tool use is formatted using XML-style tags. The tool name is enclosed in opening and closing tags, and each parameter is similarly enclosed within its own set of tags. Here's the structure:
+Tool uses are formatted using XML-style tags. The tool name itself becomes the XML tag name. Each parameter is enclosed within its own set of tags. Here's the structure:
-
+value1value2
...
-
+
-For example:
+For example, to use the read_file tool:
src/main.js
-Always adhere to this format for the tool use to ensure proper parsing and execution.
+Always use the actual tool name as the XML tag name for proper parsing and execution.
# Tools
@@ -3874,10 +3922,10 @@ Example: Requesting to switch to code mode
## new_task
-Description: Create a new task with a specified starting mode and initial message. This tool instructs the system to create a new Cline instance in the given mode with the provided message.
+Description: This will let you create a new task instance in the chosen mode using your provided message.
Parameters:
-- mode: (required) The slug of the mode to start the new task in (e.g., "code", "ask", "architect").
+- mode: (required) The slug of the mode to start the new task in (e.g., "code", "debug", "architect").
- message: (required) The initial user message or instructions for this new task.
Usage:
@@ -4053,27 +4101,33 @@ exports[`addCustomInstructions should exclude MCP server creation info when disa
====
+MARKDOWN RULES
+
+ALL responses MUST show ANY \`language construct\` OR filename reterence as clickable, exactly as [\`filename OR language.declaration()\`](relative/file/path.ext:line); line is required for \`syntax\` and optional for filename links. This applies to ALL markdown responses and ALSO those in
+
+====
+
TOOL USE
You have access to a set of tools that are executed upon the user's approval. You can use one tool per message, and will receive the result of that tool use in the user's response. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use.
# Tool Use Formatting
-Tool use is formatted using XML-style tags. The tool name is enclosed in opening and closing tags, and each parameter is similarly enclosed within its own set of tags. Here's the structure:
+Tool uses are formatted using XML-style tags. The tool name itself becomes the XML tag name. Each parameter is enclosed within its own set of tags. Here's the structure:
-
+value1value2
...
-
+
-For example:
+For example, to use the read_file tool:
src/main.js
-Always adhere to this format for the tool use to ensure proper parsing and execution.
+Always use the actual tool name as the XML tag name for proper parsing and execution.
# Tools
@@ -4432,10 +4486,10 @@ Example: Requesting to switch to code mode
## new_task
-Description: Create a new task with a specified starting mode and initial message. This tool instructs the system to create a new Cline instance in the given mode with the provided message.
+Description: This will let you create a new task instance in the chosen mode using your provided message.
Parameters:
-- mode: (required) The slug of the mode to start the new task in (e.g., "code", "ask", "architect").
+- mode: (required) The slug of the mode to start the new task in (e.g., "code", "debug", "architect").
- message: (required) The initial user message or instructions for this new task.
Usage:
@@ -4597,27 +4651,33 @@ exports[`addCustomInstructions should generate correct prompt for architect mode
====
+MARKDOWN RULES
+
+ALL responses MUST show ANY \`language construct\` OR filename reterence as clickable, exactly as [\`filename OR language.declaration()\`](relative/file/path.ext:line); line is required for \`syntax\` and optional for filename links. This applies to ALL markdown responses and ALSO those in
+
+====
+
TOOL USE
You have access to a set of tools that are executed upon the user's approval. You can use one tool per message, and will receive the result of that tool use in the user's response. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use.
# Tool Use Formatting
-Tool use is formatted using XML-style tags. The tool name is enclosed in opening and closing tags, and each parameter is similarly enclosed within its own set of tags. Here's the structure:
+Tool uses are formatted using XML-style tags. The tool name itself becomes the XML tag name. Each parameter is enclosed within its own set of tags. Here's the structure:
-
+value1value2
...
-
+
-For example:
+For example, to use the read_file tool:
src/main.js
-Always adhere to this format for the tool use to ensure proper parsing and execution.
+Always use the actual tool name as the XML tag name for proper parsing and execution.
# Tools
@@ -4905,10 +4965,10 @@ Example: Requesting to switch to code mode
## new_task
-Description: Create a new task with a specified starting mode and initial message. This tool instructs the system to create a new Cline instance in the given mode with the provided message.
+Description: This will let you create a new task instance in the chosen mode using your provided message.
Parameters:
-- mode: (required) The slug of the mode to start the new task in (e.g., "code", "ask", "architect").
+- mode: (required) The slug of the mode to start the new task in (e.g., "code", "debug", "architect").
- message: (required) The initial user message or instructions for this new task.
Usage:
@@ -5055,27 +5115,33 @@ exports[`addCustomInstructions should generate correct prompt for ask mode 1`] =
====
+MARKDOWN RULES
+
+ALL responses MUST show ANY \`language construct\` OR filename reterence as clickable, exactly as [\`filename OR language.declaration()\`](relative/file/path.ext:line); line is required for \`syntax\` and optional for filename links. This applies to ALL markdown responses and ALSO those in
+
+====
+
TOOL USE
You have access to a set of tools that are executed upon the user's approval. You can use one tool per message, and will receive the result of that tool use in the user's response. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use.
# Tool Use Formatting
-Tool use is formatted using XML-style tags. The tool name is enclosed in opening and closing tags, and each parameter is similarly enclosed within its own set of tags. Here's the structure:
+Tool uses are formatted using XML-style tags. The tool name itself becomes the XML tag name. Each parameter is enclosed within its own set of tags. Here's the structure:
-
+value1value2
...
-
+
-For example:
+For example, to use the read_file tool:
src/main.js
-Always adhere to this format for the tool use to ensure proper parsing and execution.
+Always use the actual tool name as the XML tag name for proper parsing and execution.
# Tools
@@ -5260,10 +5326,10 @@ Example: Requesting to switch to code mode
## new_task
-Description: Create a new task with a specified starting mode and initial message. This tool instructs the system to create a new Cline instance in the given mode with the provided message.
+Description: This will let you create a new task instance in the chosen mode using your provided message.
Parameters:
-- mode: (required) The slug of the mode to start the new task in (e.g., "code", "ask", "architect").
+- mode: (required) The slug of the mode to start the new task in (e.g., "code", "debug", "architect").
- message: (required) The initial user message or instructions for this new task.
Usage:
@@ -5386,7 +5452,7 @@ Language Preference:
You should always speak and think in the "en" language.
Mode-specific Instructions:
-You can analyze code, explain concepts, and access external resources. Make sure to answer the user's questions and don't rush to switch to implementing code. Include Mermaid diagrams if they help make your response clearer.
+You can analyze code, explain concepts, and access external resources. Always answer the user’s questions thoroughly, and do not switch to implementing code unless explicitly requested by the user. Include Mermaid diagrams when they clarify your response.
Rules:
# Rules from .clinerules-ask:
@@ -5430,27 +5496,33 @@ exports[`addCustomInstructions should include MCP server creation info when enab
====
+MARKDOWN RULES
+
+ALL responses MUST show ANY \`language construct\` OR filename reterence as clickable, exactly as [\`filename OR language.declaration()\`](relative/file/path.ext:line); line is required for \`syntax\` and optional for filename links. This applies to ALL markdown responses and ALSO those in
+
+====
+
TOOL USE
You have access to a set of tools that are executed upon the user's approval. You can use one tool per message, and will receive the result of that tool use in the user's response. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use.
# Tool Use Formatting
-Tool use is formatted using XML-style tags. The tool name is enclosed in opening and closing tags, and each parameter is similarly enclosed within its own set of tags. Here's the structure:
+Tool uses are formatted using XML-style tags. The tool name itself becomes the XML tag name. Each parameter is enclosed within its own set of tags. Here's the structure:
-
+value1value2
...
-
+
-For example:
+For example, to use the read_file tool:
src/main.js
-Always adhere to this format for the tool use to ensure proper parsing and execution.
+Always use the actual tool name as the XML tag name for proper parsing and execution.
# Tools
@@ -5809,10 +5881,10 @@ Example: Requesting to switch to code mode
## new_task
-Description: Create a new task with a specified starting mode and initial message. This tool instructs the system to create a new Cline instance in the given mode with the provided message.
+Description: This will let you create a new task instance in the chosen mode using your provided message.
Parameters:
-- mode: (required) The slug of the mode to start the new task in (e.g., "code", "ask", "architect").
+- mode: (required) The slug of the mode to start the new task in (e.g., "code", "debug", "architect").
- message: (required) The initial user message or instructions for this new task.
Usage:
diff --git a/src/core/prompts/instructions/create-mode.ts b/src/core/prompts/instructions/create-mode.ts
index 2540b4feab..47e998ff4c 100644
--- a/src/core/prompts/instructions/create-mode.ts
+++ b/src/core/prompts/instructions/create-mode.ts
@@ -25,28 +25,36 @@ If asked to create a project mode, create it in .roomodes in the workspace root.
* roleDefinition: A detailed description of the mode's role and capabilities
* groups: Array of allowed tool groups (can be empty). Each group can be specified either as a string (e.g., "edit" to allow editing any file) or with file restrictions (e.g., ["edit", { fileRegex: "\\.md$", description: "Markdown files only" }] to only allow editing markdown files)
-- The customInstructions field is optional.
+- The following fields are optional but highly recommended:
+ * whenToUse: A clear description of when this mode should be selected and what types of tasks it's best suited for. This helps the Orchestrator mode make better decisions.
+ * customInstructions: Additional instructions for how the mode should operate
- For multi-line text, include newline characters in the string like "This is the first line.\\nThis is the next line.\\n\\nThis is a double line break."
-Both files should follow this structure:
-{
- "customModes": [
- {
- "slug": "designer", // Required: unique slug with lowercase letters, numbers, and hyphens
- "name": "Designer", // Required: mode display name
- "roleDefinition": "You are Roo, a UI/UX expert specializing in design systems and frontend development. Your expertise includes:\\n- Creating and maintaining design systems\\n- Implementing responsive and accessible web interfaces\\n- Working with CSS, HTML, and modern frontend frameworks\\n- Ensuring consistent user experiences across platforms", // Required: non-empty
- "groups": [ // Required: array of tool groups (can be empty)
- "read", // Read files group (read_file, fetch_instructions, search_files, list_files, list_code_definition_names)
- "edit", // Edit files group (apply_diff, write_to_file) - allows editing any file
- // Or with file restrictions:
- // ["edit", { fileRegex: "\\.md$", description: "Markdown files only" }], // Edit group that only allows editing markdown files
- "browser", // Browser group (browser_action)
- "command", // Command group (execute_command)
- "mcp" // MCP group (use_mcp_tool, access_mcp_resource)
- ],
- "customInstructions": "Additional instructions for the Designer mode" // Optional
- }
- ]
-}`
+Both files should follow this structure (in YAML format):
+
+customModes:
+ - slug: designer # Required: unique slug with lowercase letters, numbers, and hyphens
+ name: Designer # Required: mode display name
+ roleDefinition: >-
+ You are Roo, a UI/UX expert specializing in design systems and frontend development. Your expertise includes:
+ - Creating and maintaining design systems
+ - Implementing responsive and accessible web interfaces
+ - Working with CSS, HTML, and modern frontend frameworks
+ - Ensuring consistent user experiences across platforms # Required: non-empty
+ whenToUse: >-
+ Use this mode when creating or modifying UI components, implementing design systems,
+ or ensuring responsive web interfaces. This mode is especially effective with CSS,
+ HTML, and modern frontend frameworks. # Optional but recommended
+ groups: # Required: array of tool groups (can be empty)
+ - read # Read files group (read_file, fetch_instructions, search_files, list_files, list_code_definition_names)
+ - edit # Edit files group (apply_diff, write_to_file) - allows editing any file
+ # Or with file restrictions:
+ # - - edit
+ # - fileRegex: \\.md$
+ # description: Markdown files only # Edit group that only allows editing markdown files
+ - browser # Browser group (browser_action)
+ - command # Command group (execute_command)
+ - mcp # MCP group (use_mcp_tool, access_mcp_resource)
+ customInstructions: Additional instructions for the Designer mode # Optional`
}
diff --git a/src/core/prompts/responses.ts b/src/core/prompts/responses.ts
index 314387171a..dc6e08c8a0 100644
--- a/src/core/prompts/responses.ts
+++ b/src/core/prompts/responses.ts
@@ -184,15 +184,15 @@ const formatImagesIntoBlocks = (images?: string[]): Anthropic.ImageBlockParam[]
const toolUseInstructionsReminder = `# Reminder: Instructions for Tool Use
-Tool uses are formatted using XML-style tags. The tool name is enclosed in opening and closing tags, and each parameter is similarly enclosed within its own set of tags. Here's the structure:
+Tool uses are formatted using XML-style tags. The tool name itself becomes the XML tag name. Each parameter is enclosed within its own set of tags. Here's the structure:
-
+value1value2
...
-
+
-For example:
+For example, to use the attempt_completion tool:
@@ -200,4 +200,4 @@ I have completed the task...
-Always adhere to this format for all tool uses to ensure proper parsing and execution.`
+Always use the actual tool name as the XML tag name for proper parsing and execution.`
diff --git a/src/core/prompts/sections/index.ts b/src/core/prompts/sections/index.ts
index a9f8c9e4c6..d06dbbfde1 100644
--- a/src/core/prompts/sections/index.ts
+++ b/src/core/prompts/sections/index.ts
@@ -7,3 +7,4 @@ export { getMcpServersSection } from "./mcp-servers"
export { getToolUseGuidelinesSection } from "./tool-use-guidelines"
export { getCapabilitiesSection } from "./capabilities"
export { getModesSection } from "./modes"
+export { markdownFormattingSection } from "./markdown-formatting"
diff --git a/src/core/prompts/sections/markdown-formatting.ts b/src/core/prompts/sections/markdown-formatting.ts
new file mode 100644
index 0000000000..fe152a1a41
--- /dev/null
+++ b/src/core/prompts/sections/markdown-formatting.ts
@@ -0,0 +1,7 @@
+export function markdownFormattingSection(): string {
+ return `====
+
+MARKDOWN RULES
+
+ALL responses MUST show ANY \`language construct\` OR filename reterence as clickable, exactly as [\`filename OR language.declaration()\`](relative/file/path.ext:line); line is required for \`syntax\` and optional for filename links. This applies to ALL markdown responses and ALSO those in `
+}
diff --git a/src/core/prompts/sections/modes.ts b/src/core/prompts/sections/modes.ts
index 0fa5e06576..ff12098d5e 100644
--- a/src/core/prompts/sections/modes.ts
+++ b/src/core/prompts/sections/modes.ts
@@ -16,7 +16,19 @@ export async function getModesSection(context: vscode.ExtensionContext): Promise
MODES
- These are the currently available modes:
-${allModes.map((mode: ModeConfig) => ` * "${mode.name}" mode (${mode.slug}) - ${mode.roleDefinition.split(".")[0]}`).join("\n")}`
+${allModes
+ .map((mode: ModeConfig) => {
+ let description: string
+ if (mode.whenToUse && mode.whenToUse.trim() !== "") {
+ // Use whenToUse as the primary description, indenting subsequent lines for readability
+ description = mode.whenToUse.replace(/\n/g, "\n ")
+ } else {
+ // Fallback to the first sentence of roleDefinition if whenToUse is not available
+ description = mode.roleDefinition.split(".")[0]
+ }
+ return ` * "${mode.name}" mode (${mode.slug}) - ${description}`
+ })
+ .join("\n")}`
modesContent += `
If the user asks you to create or edit a new mode for this project, you should read the instructions by using the fetch_instructions tool, like this:
diff --git a/src/core/prompts/sections/tool-use.ts b/src/core/prompts/sections/tool-use.ts
index e5671871ce..b75e4dad92 100644
--- a/src/core/prompts/sections/tool-use.ts
+++ b/src/core/prompts/sections/tool-use.ts
@@ -7,19 +7,19 @@ You have access to a set of tools that are executed upon the user's approval. Yo
# Tool Use Formatting
-Tool use is formatted using XML-style tags. The tool name is enclosed in opening and closing tags, and each parameter is similarly enclosed within its own set of tags. Here's the structure:
+Tool uses are formatted using XML-style tags. The tool name itself becomes the XML tag name. Each parameter is enclosed within its own set of tags. Here's the structure:
-
+value1value2
...
-
+
-For example:
+For example, to use the read_file tool:
src/main.js
-Always adhere to this format for the tool use to ensure proper parsing and execution.`
+Always use the actual tool name as the XML tag name for proper parsing and execution.`
}
diff --git a/src/core/prompts/system.ts b/src/core/prompts/system.ts
index f56a947663..0fce1765a9 100644
--- a/src/core/prompts/system.ts
+++ b/src/core/prompts/system.ts
@@ -8,7 +8,7 @@ import {
getModeBySlug,
getGroupName,
} from "../../shared/modes"
-import { PromptVariables } from "./sections/custom-system-prompt"
+import { PromptVariables, loadSystemPromptFile } from "./sections/custom-system-prompt"
import { DiffStrategy } from "../../shared/tools"
import { McpHub } from "../../services/mcp/McpHub"
import { getToolDescriptionsForMode } from "./tools"
@@ -24,8 +24,8 @@ import {
getCapabilitiesSection,
getModesSection,
addCustomInstructions,
+ markdownFormattingSection,
} from "./sections"
-import { loadSystemPromptFile } from "./sections/custom-system-prompt"
import { formatLanguage } from "../../shared/language"
async function generatePrompt(
@@ -65,6 +65,8 @@ async function generatePrompt(
const basePrompt = `${roleDefinition}
+${markdownFormattingSection()}
+
${getSharedToolUseSection()}
${getToolDescriptionsForMode(
diff --git a/src/core/prompts/tools/new-task.ts b/src/core/prompts/tools/new-task.ts
index 1bf8848aef..7301b7b422 100644
--- a/src/core/prompts/tools/new-task.ts
+++ b/src/core/prompts/tools/new-task.ts
@@ -2,10 +2,10 @@ import { ToolArgs } from "./types"
export function getNewTaskDescription(_args: ToolArgs): string {
return `## new_task
-Description: Create a new task with a specified starting mode and initial message. This tool instructs the system to create a new Cline instance in the given mode with the provided message.
+Description: This will let you create a new task instance in the chosen mode using your provided message.
Parameters:
-- mode: (required) The slug of the mode to start the new task in (e.g., "code", "ask", "architect").
+- mode: (required) The slug of the mode to start the new task in (e.g., "code", "debug", "architect").
- message: (required) The initial user message or instructions for this new task.
Usage:
diff --git a/src/core/sliding-window/__tests__/sliding-window.test.ts b/src/core/sliding-window/__tests__/sliding-window.test.ts
index 16af2d4630..810d899ae3 100644
--- a/src/core/sliding-window/__tests__/sliding-window.test.ts
+++ b/src/core/sliding-window/__tests__/sliding-window.test.ts
@@ -10,6 +10,8 @@ import {
truncateConversation,
truncateConversationIfNeeded,
} from "../index"
+import { ApiMessage } from "../../task-persistence/apiMessages"
+import * as condenseModule from "../../condense"
// Create a mock ApiHandler for testing
class MockApiHandler extends BaseProvider {
@@ -41,7 +43,7 @@ const mockApiHandler = new MockApiHandler()
*/
describe("truncateConversation", () => {
it("should retain the first message", () => {
- const messages: Anthropic.Messages.MessageParam[] = [
+ const messages: ApiMessage[] = [
{ role: "user", content: "First message" },
{ role: "assistant", content: "Second message" },
{ role: "user", content: "Third message" },
@@ -58,7 +60,7 @@ describe("truncateConversation", () => {
})
it("should remove the specified fraction of messages (rounded to even number)", () => {
- const messages: Anthropic.Messages.MessageParam[] = [
+ const messages: ApiMessage[] = [
{ role: "user", content: "First message" },
{ role: "assistant", content: "Second message" },
{ role: "user", content: "Third message" },
@@ -77,7 +79,7 @@ describe("truncateConversation", () => {
})
it("should round to an even number of messages to remove", () => {
- const messages: Anthropic.Messages.MessageParam[] = [
+ const messages: ApiMessage[] = [
{ role: "user", content: "First message" },
{ role: "assistant", content: "Second message" },
{ role: "user", content: "Third message" },
@@ -96,7 +98,7 @@ describe("truncateConversation", () => {
})
it("should handle edge case with fracToRemove = 0", () => {
- const messages: Anthropic.Messages.MessageParam[] = [
+ const messages: ApiMessage[] = [
{ role: "user", content: "First message" },
{ role: "assistant", content: "Second message" },
{ role: "user", content: "Third message" },
@@ -108,7 +110,7 @@ describe("truncateConversation", () => {
})
it("should handle edge case with fracToRemove = 1", () => {
- const messages: Anthropic.Messages.MessageParam[] = [
+ const messages: ApiMessage[] = [
{ role: "user", content: "First message" },
{ role: "assistant", content: "Second message" },
{ role: "user", content: "Third message" },
@@ -224,7 +226,7 @@ describe("truncateConversationIfNeeded", () => {
maxTokens,
})
- const messages: Anthropic.Messages.MessageParam[] = [
+ const messages: ApiMessage[] = [
{ role: "user", content: "First message" },
{ role: "assistant", content: "Second message" },
{ role: "user", content: "Third message" },
@@ -246,8 +248,16 @@ describe("truncateConversationIfNeeded", () => {
contextWindow: modelInfo.contextWindow,
maxTokens: modelInfo.maxTokens,
apiHandler: mockApiHandler,
+ systemPrompt: "System prompt",
+ })
+
+ // Check the new return type
+ expect(result).toEqual({
+ messages: messagesWithSmallContent,
+ summary: "",
+ cost: 0,
+ prevContextTokens: totalTokens,
})
- expect(result).toEqual(messagesWithSmallContent) // No truncation occurs
})
it("should truncate if tokens are above max tokens threshold", async () => {
@@ -259,7 +269,7 @@ describe("truncateConversationIfNeeded", () => {
// When truncating, always uses 0.5 fraction
// With 4 messages after the first, 0.5 fraction means remove 2 messages
- const expectedResult = [messagesWithSmallContent[0], messagesWithSmallContent[3], messagesWithSmallContent[4]]
+ const expectedMessages = [messagesWithSmallContent[0], messagesWithSmallContent[3], messagesWithSmallContent[4]]
const result = await truncateConversationIfNeeded({
messages: messagesWithSmallContent,
@@ -267,8 +277,15 @@ describe("truncateConversationIfNeeded", () => {
contextWindow: modelInfo.contextWindow,
maxTokens: modelInfo.maxTokens,
apiHandler: mockApiHandler,
+ systemPrompt: "System prompt",
+ })
+
+ expect(result).toEqual({
+ messages: expectedMessages,
+ summary: "",
+ cost: 0,
+ prevContextTokens: totalTokens,
})
- expect(result).toEqual(expectedResult)
})
it("should work with non-prompt caching models the same as prompt caching models", async () => {
@@ -287,6 +304,7 @@ describe("truncateConversationIfNeeded", () => {
contextWindow: modelInfo1.contextWindow,
maxTokens: modelInfo1.maxTokens,
apiHandler: mockApiHandler,
+ systemPrompt: "System prompt",
})
const result2 = await truncateConversationIfNeeded({
@@ -295,9 +313,13 @@ describe("truncateConversationIfNeeded", () => {
contextWindow: modelInfo2.contextWindow,
maxTokens: modelInfo2.maxTokens,
apiHandler: mockApiHandler,
+ systemPrompt: "System prompt",
})
- expect(result1).toEqual(result2)
+ expect(result1.messages).toEqual(result2.messages)
+ expect(result1.summary).toEqual(result2.summary)
+ expect(result1.cost).toEqual(result2.cost)
+ expect(result1.prevContextTokens).toEqual(result2.prevContextTokens)
// Test above threshold
const aboveThreshold = 70001
@@ -307,6 +329,7 @@ describe("truncateConversationIfNeeded", () => {
contextWindow: modelInfo1.contextWindow,
maxTokens: modelInfo1.maxTokens,
apiHandler: mockApiHandler,
+ systemPrompt: "System prompt",
})
const result4 = await truncateConversationIfNeeded({
@@ -315,9 +338,13 @@ describe("truncateConversationIfNeeded", () => {
contextWindow: modelInfo2.contextWindow,
maxTokens: modelInfo2.maxTokens,
apiHandler: mockApiHandler,
+ systemPrompt: "System prompt",
})
- expect(result3).toEqual(result4)
+ expect(result3.messages).toEqual(result4.messages)
+ expect(result3.summary).toEqual(result4.summary)
+ expect(result3.cost).toEqual(result4.cost)
+ expect(result3.prevContextTokens).toEqual(result4.prevContextTokens)
})
it("should consider incoming content when deciding to truncate", async () => {
@@ -328,7 +355,7 @@ describe("truncateConversationIfNeeded", () => {
// Test case 1: Small content that won't push us over the threshold
const smallContent = [{ type: "text" as const, text: "Small content" }]
const smallContentTokens = await estimateTokenCount(smallContent, mockApiHandler)
- const messagesWithSmallContent: Anthropic.Messages.MessageParam[] = [
+ const messagesWithSmallContent: ApiMessage[] = [
...messages.slice(0, -1),
{ role: messages[messages.length - 1].role, content: smallContent },
]
@@ -342,8 +369,14 @@ describe("truncateConversationIfNeeded", () => {
contextWindow: modelInfo.contextWindow,
maxTokens,
apiHandler: mockApiHandler,
+ systemPrompt: "System prompt",
})
- expect(resultWithSmall).toEqual(messagesWithSmallContent) // No truncation
+ expect(resultWithSmall).toEqual({
+ messages: messagesWithSmallContent,
+ summary: "",
+ cost: 0,
+ prevContextTokens: baseTokensForSmall + smallContentTokens,
+ }) // No truncation
// Test case 2: Large content that will push us over the threshold
const largeContent = [
@@ -353,7 +386,7 @@ describe("truncateConversationIfNeeded", () => {
},
]
const largeContentTokens = await estimateTokenCount(largeContent, mockApiHandler)
- const messagesWithLargeContent: Anthropic.Messages.MessageParam[] = [
+ const messagesWithLargeContent: ApiMessage[] = [
...messages.slice(0, -1),
{ role: messages[messages.length - 1].role, content: largeContent },
]
@@ -366,13 +399,17 @@ describe("truncateConversationIfNeeded", () => {
contextWindow: modelInfo.contextWindow,
maxTokens,
apiHandler: mockApiHandler,
+ systemPrompt: "System prompt",
})
- expect(resultWithLarge).not.toEqual(messagesWithLargeContent) // Should truncate
+ expect(resultWithLarge.messages).not.toEqual(messagesWithLargeContent) // Should truncate
+ expect(resultWithLarge.summary).toBe("")
+ expect(resultWithLarge.cost).toBe(0)
+ expect(resultWithLarge.prevContextTokens).toBe(baseTokensForLarge + largeContentTokens)
// Test case 3: Very large content that will definitely exceed threshold
const veryLargeContent = [{ type: "text" as const, text: "X".repeat(1000) }]
const veryLargeContentTokens = await estimateTokenCount(veryLargeContent, mockApiHandler)
- const messagesWithVeryLargeContent: Anthropic.Messages.MessageParam[] = [
+ const messagesWithVeryLargeContent: ApiMessage[] = [
...messages.slice(0, -1),
{ role: messages[messages.length - 1].role, content: veryLargeContent },
]
@@ -385,8 +422,12 @@ describe("truncateConversationIfNeeded", () => {
contextWindow: modelInfo.contextWindow,
maxTokens,
apiHandler: mockApiHandler,
+ systemPrompt: "System prompt",
})
- expect(resultWithVeryLarge).not.toEqual(messagesWithVeryLargeContent) // Should truncate
+ expect(resultWithVeryLarge.messages).not.toEqual(messagesWithVeryLargeContent) // Should truncate
+ expect(resultWithVeryLarge.summary).toBe("")
+ expect(resultWithVeryLarge.cost).toBe(0)
+ expect(resultWithVeryLarge.prevContextTokens).toBe(baseTokensForVeryLarge + veryLargeContentTokens)
})
it("should truncate if tokens are within TOKEN_BUFFER_PERCENTAGE of the threshold", async () => {
@@ -407,8 +448,144 @@ describe("truncateConversationIfNeeded", () => {
contextWindow: modelInfo.contextWindow,
maxTokens: modelInfo.maxTokens,
apiHandler: mockApiHandler,
+ systemPrompt: "System prompt",
+ })
+ expect(result).toEqual({
+ messages: expectedResult,
+ summary: "",
+ cost: 0,
+ prevContextTokens: totalTokens,
})
- expect(result).toEqual(expectedResult)
+ })
+
+ it("should use summarizeConversation when autoCondenseContext is true and tokens exceed threshold", async () => {
+ // Mock the summarizeConversation function
+ const mockSummary = "This is a summary of the conversation"
+ const mockCost = 0.05
+ const mockSummarizeResponse: condenseModule.SummarizeResponse = {
+ messages: [
+ { role: "user", content: "First message" },
+ { role: "assistant", content: mockSummary, isSummary: true },
+ { role: "user", content: "Last message" },
+ ],
+ summary: mockSummary,
+ cost: mockCost,
+ newContextTokens: 100,
+ }
+
+ const summarizeSpy = jest
+ .spyOn(condenseModule, "summarizeConversation")
+ .mockResolvedValue(mockSummarizeResponse)
+
+ const modelInfo = createModelInfo(100000, 30000)
+ const totalTokens = 70001 // Above threshold
+ const messagesWithSmallContent = [...messages.slice(0, -1), { ...messages[messages.length - 1], content: "" }]
+
+ const result = await truncateConversationIfNeeded({
+ messages: messagesWithSmallContent,
+ totalTokens,
+ contextWindow: modelInfo.contextWindow,
+ maxTokens: modelInfo.maxTokens,
+ apiHandler: mockApiHandler,
+ autoCondenseContext: true,
+ systemPrompt: "System prompt",
+ })
+
+ // Verify summarizeConversation was called with the right parameters
+ expect(summarizeSpy).toHaveBeenCalledWith(messagesWithSmallContent, mockApiHandler, "System prompt")
+
+ // Verify the result contains the summary information
+ expect(result).toMatchObject({
+ messages: mockSummarizeResponse.messages,
+ summary: mockSummary,
+ cost: mockCost,
+ prevContextTokens: totalTokens,
+ })
+ // newContextTokens might be present, but we don't need to verify its exact value
+
+ // Clean up
+ summarizeSpy.mockRestore()
+ })
+
+ it("should fall back to truncateConversation when autoCondenseContext is true but summarization fails", async () => {
+ // Mock the summarizeConversation function to return empty summary
+ const mockSummarizeResponse: condenseModule.SummarizeResponse = {
+ messages: messages, // Original messages unchanged
+ summary: "", // Empty summary indicates failure
+ cost: 0.01,
+ }
+
+ const summarizeSpy = jest
+ .spyOn(condenseModule, "summarizeConversation")
+ .mockResolvedValue(mockSummarizeResponse)
+
+ const modelInfo = createModelInfo(100000, 30000)
+ const totalTokens = 70001 // Above threshold
+ const messagesWithSmallContent = [...messages.slice(0, -1), { ...messages[messages.length - 1], content: "" }]
+
+ // When truncating, always uses 0.5 fraction
+ // With 4 messages after the first, 0.5 fraction means remove 2 messages
+ const expectedMessages = [messagesWithSmallContent[0], messagesWithSmallContent[3], messagesWithSmallContent[4]]
+
+ const result = await truncateConversationIfNeeded({
+ messages: messagesWithSmallContent,
+ totalTokens,
+ contextWindow: modelInfo.contextWindow,
+ maxTokens: modelInfo.maxTokens,
+ apiHandler: mockApiHandler,
+ autoCondenseContext: true,
+ systemPrompt: "System prompt",
+ })
+
+ // Verify summarizeConversation was called
+ expect(summarizeSpy).toHaveBeenCalled()
+
+ // Verify it fell back to truncation
+ expect(result.messages).toEqual(expectedMessages)
+ expect(result.summary).toBe("")
+ expect(result.prevContextTokens).toBe(totalTokens)
+ // The cost might be different than expected, so we don't check it
+
+ // Clean up
+ summarizeSpy.mockRestore()
+ })
+
+ it("should not call summarizeConversation when autoCondenseContext is false", async () => {
+ // Reset any previous mock calls
+ jest.clearAllMocks()
+ const summarizeSpy = jest.spyOn(condenseModule, "summarizeConversation")
+
+ const modelInfo = createModelInfo(100000, 30000)
+ const totalTokens = 70001 // Above threshold
+ const messagesWithSmallContent = [...messages.slice(0, -1), { ...messages[messages.length - 1], content: "" }]
+
+ // When truncating, always uses 0.5 fraction
+ // With 4 messages after the first, 0.5 fraction means remove 2 messages
+ const expectedMessages = [messagesWithSmallContent[0], messagesWithSmallContent[3], messagesWithSmallContent[4]]
+
+ const result = await truncateConversationIfNeeded({
+ messages: messagesWithSmallContent,
+ totalTokens,
+ contextWindow: modelInfo.contextWindow,
+ maxTokens: modelInfo.maxTokens,
+ apiHandler: mockApiHandler,
+ autoCondenseContext: false,
+ systemPrompt: "System prompt",
+ })
+
+ // Verify summarizeConversation was not called
+ expect(summarizeSpy).not.toHaveBeenCalled()
+
+ // Verify it used truncation
+ expect(result).toEqual({
+ messages: expectedMessages,
+ summary: "",
+ cost: 0,
+ prevContextTokens: totalTokens,
+ })
+
+ // Clean up
+ summarizeSpy.mockRestore()
})
})
@@ -424,7 +601,7 @@ describe("getMaxTokens", () => {
})
// Reuse across tests for consistency
- const messages: Anthropic.Messages.MessageParam[] = [
+ const messages: ApiMessage[] = [
{ role: "user", content: "First message" },
{ role: "assistant", content: "Second message" },
{ role: "user", content: "Third message" },
@@ -447,8 +624,14 @@ describe("getMaxTokens", () => {
contextWindow: modelInfo.contextWindow,
maxTokens: modelInfo.maxTokens,
apiHandler: mockApiHandler,
+ systemPrompt: "System prompt",
+ })
+ expect(result1).toEqual({
+ messages: messagesWithSmallContent,
+ summary: "",
+ cost: 0,
+ prevContextTokens: 39999,
})
- expect(result1).toEqual(messagesWithSmallContent)
// Above max tokens - truncate
const result2 = await truncateConversationIfNeeded({
@@ -457,9 +640,13 @@ describe("getMaxTokens", () => {
contextWindow: modelInfo.contextWindow,
maxTokens: modelInfo.maxTokens,
apiHandler: mockApiHandler,
+ systemPrompt: "System prompt",
})
- expect(result2).not.toEqual(messagesWithSmallContent)
- expect(result2.length).toBe(3) // Truncated with 0.5 fraction
+ expect(result2.messages).not.toEqual(messagesWithSmallContent)
+ expect(result2.messages.length).toBe(3) // Truncated with 0.5 fraction
+ expect(result2.summary).toBe("")
+ expect(result2.cost).toBe(0)
+ expect(result2.prevContextTokens).toBe(50001)
})
it("should use 20% of context window as buffer when maxTokens is undefined", async () => {
@@ -477,8 +664,14 @@ describe("getMaxTokens", () => {
contextWindow: modelInfo.contextWindow,
maxTokens: modelInfo.maxTokens,
apiHandler: mockApiHandler,
+ systemPrompt: "System prompt",
+ })
+ expect(result1).toEqual({
+ messages: messagesWithSmallContent,
+ summary: "",
+ cost: 0,
+ prevContextTokens: 69999,
})
- expect(result1).toEqual(messagesWithSmallContent)
// Above max tokens - truncate
const result2 = await truncateConversationIfNeeded({
@@ -487,9 +680,13 @@ describe("getMaxTokens", () => {
contextWindow: modelInfo.contextWindow,
maxTokens: modelInfo.maxTokens,
apiHandler: mockApiHandler,
+ systemPrompt: "System prompt",
})
- expect(result2).not.toEqual(messagesWithSmallContent)
- expect(result2.length).toBe(3) // Truncated with 0.5 fraction
+ expect(result2.messages).not.toEqual(messagesWithSmallContent)
+ expect(result2.messages.length).toBe(3) // Truncated with 0.5 fraction
+ expect(result2.summary).toBe("")
+ expect(result2.cost).toBe(0)
+ expect(result2.prevContextTokens).toBe(80001)
})
it("should handle small context windows appropriately", async () => {
@@ -506,8 +703,9 @@ describe("getMaxTokens", () => {
contextWindow: modelInfo.contextWindow,
maxTokens: modelInfo.maxTokens,
apiHandler: mockApiHandler,
+ systemPrompt: "System prompt",
})
- expect(result1).toEqual(messagesWithSmallContent)
+ expect(result1.messages).toEqual(messagesWithSmallContent)
// Above max tokens - truncate
const result2 = await truncateConversationIfNeeded({
@@ -516,9 +714,10 @@ describe("getMaxTokens", () => {
contextWindow: modelInfo.contextWindow,
maxTokens: modelInfo.maxTokens,
apiHandler: mockApiHandler,
+ systemPrompt: "System prompt",
})
expect(result2).not.toEqual(messagesWithSmallContent)
- expect(result2.length).toBe(3) // Truncated with 0.5 fraction
+ expect(result2.messages.length).toBe(3) // Truncated with 0.5 fraction
})
it("should handle large context windows appropriately", async () => {
@@ -536,8 +735,9 @@ describe("getMaxTokens", () => {
contextWindow: modelInfo.contextWindow,
maxTokens: modelInfo.maxTokens,
apiHandler: mockApiHandler,
+ systemPrompt: "System prompt",
})
- expect(result1).toEqual(messagesWithSmallContent)
+ expect(result1.messages).toEqual(messagesWithSmallContent)
// Above max tokens - truncate
const result2 = await truncateConversationIfNeeded({
@@ -546,8 +746,9 @@ describe("getMaxTokens", () => {
contextWindow: modelInfo.contextWindow,
maxTokens: modelInfo.maxTokens,
apiHandler: mockApiHandler,
+ systemPrompt: "System prompt",
})
expect(result2).not.toEqual(messagesWithSmallContent)
- expect(result2.length).toBe(3) // Truncated with 0.5 fraction
+ expect(result2.messages.length).toBe(3) // Truncated with 0.5 fraction
})
})
diff --git a/src/core/sliding-window/index.ts b/src/core/sliding-window/index.ts
index 75395ecd75..4c2f17e816 100644
--- a/src/core/sliding-window/index.ts
+++ b/src/core/sliding-window/index.ts
@@ -1,5 +1,7 @@
import { Anthropic } from "@anthropic-ai/sdk"
import { ApiHandler } from "../../api"
+import { summarizeConversation, SummarizeResponse } from "../condense"
+import { ApiMessage } from "../task-persistence/apiMessages"
/**
* Default percentage of the context window to use as a buffer when deciding when to truncate
@@ -27,14 +29,11 @@ export async function estimateTokenCount(
* The first message is always retained, and a specified fraction (rounded to an even number)
* of messages from the beginning (excluding the first) is removed.
*
- * @param {Anthropic.Messages.MessageParam[]} messages - The conversation messages.
+ * @param {ApiMessage[]} messages - The conversation messages.
* @param {number} fracToRemove - The fraction (between 0 and 1) of messages (excluding the first) to remove.
- * @returns {Anthropic.Messages.MessageParam[]} The truncated conversation messages.
+ * @returns {ApiMessage[]} The truncated conversation messages.
*/
-export function truncateConversation(
- messages: Anthropic.Messages.MessageParam[],
- fracToRemove: number,
-): Anthropic.Messages.MessageParam[] {
+export function truncateConversation(messages: ApiMessage[], fracToRemove: number): ApiMessage[] {
const truncatedMessages = [messages[0]]
const rawMessagesToRemove = Math.floor((messages.length - 1) * fracToRemove)
const messagesToRemove = rawMessagesToRemove - (rawMessagesToRemove % 2)
@@ -48,28 +47,34 @@ export function truncateConversation(
* Conditionally truncates the conversation messages if the total token count
* exceeds the model's limit, considering the size of incoming content.
*
- * @param {Anthropic.Messages.MessageParam[]} messages - The conversation messages.
+ * @param {ApiMessage[]} messages - The conversation messages.
* @param {number} totalTokens - The total number of tokens in the conversation (excluding the last user message).
* @param {number} contextWindow - The context window size.
* @param {number} maxTokens - The maximum number of tokens allowed.
* @param {ApiHandler} apiHandler - The API handler to use for token counting.
- * @returns {Anthropic.Messages.MessageParam[]} The original or truncated conversation messages.
+ * @param {boolean} autoCondenseContext - Whether to use LLM summarization or sliding window implementation
+ * @param {string} systemPrompt - The system prompt, used for estimating the new context size after summarizing.
+ * @returns {ApiMessage[]} The original or truncated conversation messages.
*/
type TruncateOptions = {
- messages: Anthropic.Messages.MessageParam[]
+ messages: ApiMessage[]
totalTokens: number
contextWindow: number
maxTokens?: number | null
apiHandler: ApiHandler
+ autoCondenseContext?: boolean
+ systemPrompt: string
}
+type TruncateResponse = SummarizeResponse & { prevContextTokens: number }
+
/**
* Conditionally truncates the conversation messages if the total token count
* exceeds the model's limit, considering the size of incoming content.
*
* @param {TruncateOptions} options - The options for truncation
- * @returns {Promise} The original or truncated conversation messages.
+ * @returns {Promise} The original or truncated conversation messages.
*/
export async function truncateConversationIfNeeded({
messages,
@@ -77,7 +82,9 @@ export async function truncateConversationIfNeeded({
contextWindow,
maxTokens,
apiHandler,
-}: TruncateOptions): Promise {
+ autoCondenseContext,
+ systemPrompt,
+}: TruncateOptions): Promise {
// Calculate the maximum tokens reserved for response
const reservedTokens = maxTokens || contextWindow * 0.2
@@ -96,5 +103,14 @@ export async function truncateConversationIfNeeded({
const allowedTokens = contextWindow * (1 - TOKEN_BUFFER_PERCENTAGE) - reservedTokens
// Determine if truncation is needed and apply if necessary
- return effectiveTokens > allowedTokens ? truncateConversation(messages, 0.5) : messages
+ if (effectiveTokens <= allowedTokens) {
+ return { messages, summary: "", cost: 0, prevContextTokens: effectiveTokens }
+ } else if (autoCondenseContext) {
+ const result = await summarizeConversation(messages, apiHandler, systemPrompt)
+ if (result.summary) {
+ return { ...result, prevContextTokens: effectiveTokens }
+ }
+ }
+ const truncatedMessages = truncateConversation(messages, 0.5)
+ return { messages: truncatedMessages, prevContextTokens: effectiveTokens, summary: "", cost: 0 }
}
diff --git a/src/core/task-persistence/apiMessages.ts b/src/core/task-persistence/apiMessages.ts
index b361016345..0ba9628a5d 100644
--- a/src/core/task-persistence/apiMessages.ts
+++ b/src/core/task-persistence/apiMessages.ts
@@ -6,9 +6,9 @@ import { Anthropic } from "@anthropic-ai/sdk"
import { fileExistsAtPath } from "../../utils/fs"
import { GlobalFileNames } from "../../shared/globalFileNames"
-import { getTaskDirectoryPath } from "../../shared/storagePathManager"
+import { getTaskDirectoryPath } from "../../utils/storage"
-export type ApiMessage = Anthropic.MessageParam & { ts?: number }
+export type ApiMessage = Anthropic.MessageParam & { ts?: number; isSummary?: boolean }
export async function readApiMessages({
taskId,
diff --git a/src/core/task-persistence/taskMessages.ts b/src/core/task-persistence/taskMessages.ts
index 96129e6285..54d33b1a51 100644
--- a/src/core/task-persistence/taskMessages.ts
+++ b/src/core/task-persistence/taskMessages.ts
@@ -5,7 +5,7 @@ import { fileExistsAtPath } from "../../utils/fs"
import { GlobalFileNames } from "../../shared/globalFileNames"
import { ClineMessage } from "../../shared/ExtensionMessage"
-import { getTaskDirectoryPath } from "../../shared/storagePathManager"
+import { getTaskDirectoryPath } from "../../utils/storage"
export type ReadTaskMessagesOptions = {
taskId: string
diff --git a/src/core/task-persistence/taskMetadata.ts b/src/core/task-persistence/taskMetadata.ts
index 9784e62295..0a028e5ba8 100644
--- a/src/core/task-persistence/taskMetadata.ts
+++ b/src/core/task-persistence/taskMetadata.ts
@@ -7,7 +7,7 @@ import { combineCommandSequences } from "../../shared/combineCommandSequences"
import { getApiMetrics } from "../../shared/getApiMetrics"
import { findLastIndex } from "../../shared/array"
import { HistoryItem } from "../../shared/HistoryItem"
-import { getTaskDirectoryPath } from "../../shared/storagePathManager"
+import { getTaskDirectoryPath } from "../../utils/storage"
const taskSizeCache = new NodeCache({ stdTTL: 30, checkperiod: 5 * 60 })
diff --git a/src/core/task/Task.ts b/src/core/task/Task.ts
new file mode 100644
index 0000000000..407a323d85
--- /dev/null
+++ b/src/core/task/Task.ts
@@ -0,0 +1,1699 @@
+import * as path from "path"
+import os from "os"
+import crypto from "crypto"
+import EventEmitter from "events"
+
+import { Anthropic } from "@anthropic-ai/sdk"
+import delay from "delay"
+import pWaitFor from "p-wait-for"
+import { serializeError } from "serialize-error"
+
+// schemas
+import { TokenUsage, ToolUsage, ToolName, ContextCondense } from "../../schemas"
+
+// api
+import { ApiHandler, buildApiHandler } from "../../api"
+import { ApiStream } from "../../api/transform/stream"
+
+// shared
+import { ProviderSettings } from "../../shared/api"
+import { findLastIndex } from "../../shared/array"
+import { combineApiRequests } from "../../shared/combineApiRequests"
+import { combineCommandSequences } from "../../shared/combineCommandSequences"
+import {
+ ClineApiReqCancelReason,
+ ClineApiReqInfo,
+ ClineAsk,
+ ClineMessage,
+ ClineSay,
+ ToolProgressStatus,
+} from "../../shared/ExtensionMessage"
+import { getApiMetrics } from "../../shared/getApiMetrics"
+import { HistoryItem } from "../../shared/HistoryItem"
+import { ClineAskResponse } from "../../shared/WebviewMessage"
+import { defaultModeSlug } from "../../shared/modes"
+import { DiffStrategy } from "../../shared/tools"
+
+// services
+import { UrlContentFetcher } from "../../services/browser/UrlContentFetcher"
+import { BrowserSession } from "../../services/browser/BrowserSession"
+import { McpHub } from "../../services/mcp/McpHub"
+import { McpServerManager } from "../../services/mcp/McpServerManager"
+import { telemetryService } from "../../services/telemetry/TelemetryService"
+import { RepoPerTaskCheckpointService } from "../../services/checkpoints"
+
+// integrations
+import { DiffViewProvider } from "../../integrations/editor/DiffViewProvider"
+import { findToolName, formatContentBlockToMarkdown } from "../../integrations/misc/export-markdown"
+import { RooTerminalProcess } from "../../integrations/terminal/types"
+import { TerminalRegistry } from "../../integrations/terminal/TerminalRegistry"
+
+// utils
+import { calculateApiCostAnthropic } from "../../utils/cost"
+import { getWorkspacePath } from "../../utils/path"
+
+// prompts
+import { formatResponse } from "../prompts/responses"
+import { SYSTEM_PROMPT } from "../prompts/system"
+
+// core modules
+import { ToolRepetitionDetector } from "../tools/ToolRepetitionDetector"
+import { FileContextTracker } from "../context-tracking/FileContextTracker"
+import { RooIgnoreController } from "../ignore/RooIgnoreController"
+import { type AssistantMessageContent, parseAssistantMessage, presentAssistantMessage } from "../assistant-message"
+import { truncateConversationIfNeeded } from "../sliding-window"
+import { ClineProvider } from "../webview/ClineProvider"
+import { MultiSearchReplaceDiffStrategy } from "../diff/strategies/multi-search-replace"
+import { readApiMessages, saveApiMessages, readTaskMessages, saveTaskMessages, taskMetadata } from "../task-persistence"
+import { getEnvironmentDetails } from "../environment/getEnvironmentDetails"
+import {
+ type CheckpointDiffOptions,
+ type CheckpointRestoreOptions,
+ getCheckpointService,
+ checkpointSave,
+ checkpointRestore,
+ checkpointDiff,
+} from "../checkpoints"
+import { processUserContentMentions } from "../mentions/processUserContentMentions"
+import { ApiMessage } from "../task-persistence/apiMessages"
+import { getMessagesSinceLastSummary, summarizeConversation } from "../condense"
+import { maybeRemoveImageBlocks } from "../../api/transform/image-cleaning"
+
+export type ClineEvents = {
+ message: [{ action: "created" | "updated"; message: ClineMessage }]
+ taskStarted: []
+ taskModeSwitched: [taskId: string, mode: string]
+ taskPaused: []
+ taskUnpaused: []
+ taskAskResponded: []
+ taskAborted: []
+ taskSpawned: [taskId: string]
+ taskCompleted: [taskId: string, tokenUsage: TokenUsage, toolUsage: ToolUsage]
+ taskTokenUsageUpdated: [taskId: string, tokenUsage: TokenUsage]
+ taskToolFailed: [taskId: string, tool: ToolName, error: string]
+}
+
+export type TaskOptions = {
+ provider: ClineProvider
+ apiConfiguration: ProviderSettings
+ enableDiff?: boolean
+ enableCheckpoints?: boolean
+ fuzzyMatchThreshold?: number
+ consecutiveMistakeLimit?: number
+ task?: string
+ images?: string[]
+ historyItem?: HistoryItem
+ experiments?: Record