diff --git a/.changeset/v3.54.0.md b/.changeset/v3.54.0.md new file mode 100644 index 00000000000..f2817ede2ac --- /dev/null +++ b/.changeset/v3.54.0.md @@ -0,0 +1,15 @@ +--- +"roo-cline": minor +--- + +- Remove: Roo Code Cloud and eval infrastructure from the extension, CLI, workflows, and package surfaces so the release is focused on the standalone extension (PR #12328 by @mrubens) +- Remove: All telemetry collection and analytics plumbing across the extension, website, shared types, provider flows, and related tests (PR #12324 by @mrubens) +- Remove: MDM and organization membership enforcement, including host wiring, webview state, user-facing messages, and locale strings (PR #12323 by @mrubens) +- Remove: The MCP marketplace, marketplace services, webview marketplace UI, package contributions, and related localized copy (PR #12326 by @mrubens) +- Update: Extension-facing support, diagnostics, and announcement content for the final Roo Code release, including GitHub help paths and links to Roomote, ZooCode, and Cline (PR #12341 by @brunobergher) +- Add: A cleaned docs app with GitHub Pages deployment support (PR #12344 by @brunobergher) +- Fix: Configure the docs GitHub Pages base URL so deployed assets and canonical paths load correctly under the repository Pages path (PR #12370 by @mrubens) +- Update: Point docs links in the root README, localized READMEs, and web app copy to the current GitHub Pages docs URL (PR #12371 by @mrubens) +- Remove: Stale `roocode.github.io` docs references, including the old CNAME and outdated docs README and robots.txt URLs (PR #12372 by @mrubens) +- Update: The website to focus almost entirely on the Roo Code extension and remove cloud, team, enterprise, provider, pricing, Slack, and Linear product pages (PR #12180 by @brunobergher) +- Remove: Contributor, community, social channel, and tutorial references from README files, docs, website copy, issue templates, and workflows (PR #12347 by @brunobergher) diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml index 7e140ec08cc..489533de694 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yml +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -64,7 +64,7 @@ body: attributes: value: | --- - Optional (for contributors): You can stop here if you're just proposing the improvement. + Optional: You can stop here if you're just proposing the improvement. - type: textarea id: acceptance-criteria diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md deleted file mode 100644 index e83e44cd66d..00000000000 --- a/.github/pull_request_template.md +++ /dev/null @@ -1,75 +0,0 @@ - - -### Related GitHub Issue - - - -Closes: # - -### Roo Code Task Context (Optional) - - - -### Description - - - -### Test Procedure - - - -### Pre-Submission Checklist - - - -- [ ] **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. -- [ ] **Testing**: New and/or updated tests have been added to cover my changes (if applicable). -- [ ] **Documentation Impact**: I have considered if my changes require documentation updates (see "Documentation Updates" section below). -- [ ] **Contribution Guidelines**: I have read and agree to the [Contributor Guidelines](/CONTRIBUTING.md). - -### Screenshots / Videos - - - -### Documentation Updates - - - -### Additional Notes - - - -### Get in Touch - - diff --git a/.github/workflows/docs-pages.yml b/.github/workflows/docs-pages.yml new file mode 100644 index 00000000000..089ed8a68ce --- /dev/null +++ b/.github/workflows/docs-pages.yml @@ -0,0 +1,55 @@ +name: Deploy docs to GitHub Pages + +on: + push: + branches: + - main + paths: + - "apps/docs/**" + - ".github/workflows/docs-pages.yml" + - ".github/actions/setup-node-pnpm/**" + - "package.json" + - "pnpm-lock.yaml" + - "pnpm-workspace.yaml" + workflow_dispatch: + +concurrency: + group: docs-pages + cancel-in-progress: true + +permissions: + contents: read + pages: write + id-token: write + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + - name: Setup Node.js and pnpm + uses: ./.github/actions/setup-node-pnpm + with: + install-args: "--frozen-lockfile" + - name: Run type check + run: pnpm --filter @roo-code/docs check-types + - name: Run lint + run: pnpm --filter @roo-code/docs lint + - name: Build docs + run: pnpm --filter @roo-code/docs build + - name: Upload Pages artifact + uses: actions/upload-pages-artifact@v3 + with: + path: apps/docs/build + + deploy: + runs-on: ubuntu-latest + needs: build + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/.github/workflows/evals.yml b/.github/workflows/evals.yml deleted file mode 100644 index b99fd7659ef..00000000000 --- a/.github/workflows/evals.yml +++ /dev/null @@ -1,74 +0,0 @@ -name: Evals - -on: - pull_request: - types: [labeled] - workflow_dispatch: - -env: - DOCKER_BUILDKIT: 1 - COMPOSE_DOCKER_CLI_BUILD: 1 - -jobs: - evals: - # Run if triggered manually or if PR has 'evals' label. - if: github.event_name == 'workflow_dispatch' || contains(github.event.label.name, 'evals') - runs-on: blacksmith-16vcpu-ubuntu-2404 - timeout-minutes: 45 - - defaults: - run: - working-directory: packages/evals - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - - name: Create environment - run: | - cat > .env.local << EOF - OPENROUTER_API_KEY=${{ secrets.OPENROUTER_API_KEY || 'test-key-for-build' }} - EOF - - cat > .env.development << EOF - NODE_ENV=development - DATABASE_URL=postgresql://postgres:password@db:5432/evals_development - REDIS_URL=redis://redis:6379 - HOST_EXECUTION_METHOD=docker - EOF - - - name: Build image - uses: docker/build-push-action@v6 - with: - context: . - file: packages/evals/Dockerfile.runner - tags: evals-runner:latest - cache-from: type=gha - cache-to: type=gha,mode=max - push: false - load: true - - - name: Tag image - run: docker tag evals-runner:latest evals-runner - - - name: Start containers - run: | - docker compose up -d db redis - timeout 60 bash -c 'until docker compose exec -T db pg_isready -U postgres; do sleep 2; done' - timeout 60 bash -c 'until docker compose exec -T redis redis-cli ping | grep -q PONG; do sleep 2; done' - docker compose run --rm runner sh -c 'nc -z db 5432 && echo "✓ Runner -> Database connection successful"' - docker compose run --rm runner sh -c 'nc -z redis 6379 && echo "✓ Runner -> Redis connection successful"' - docker compose run --rm runner docker ps - - - name: Run database migrations - run: docker compose run --rm runner pnpm --filter @roo-code/evals db:migrate - - - name: Run evals - run: docker compose run --rm runner pnpm --filter @roo-code/evals cli --ci - - - name: Cleanup - if: always() - run: docker compose down -v --remove-orphans diff --git a/.github/workflows/update-contributors.yml b/.github/workflows/update-contributors.yml deleted file mode 100644 index 5709bdc10a0..00000000000 --- a/.github/workflows/update-contributors.yml +++ /dev/null @@ -1,67 +0,0 @@ -name: Update Contributors # Refresh contrib.rocks image cache - -on: - workflow_dispatch: - -permissions: - contents: write - pull-requests: write - -jobs: - refresh-contrib-cache: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Bump cacheBust in all README files - run: | - set -euo pipefail - TS="$(date +%s)" - # Target only the root README.md and localized READMEs under locales/*/README.md - mapfile -t FILES < <(git ls-files README.md 'locales/*/README.md' || true) - - if [ "${#FILES[@]}" -eq 0 ]; then - echo "No target README files found." >&2 - exit 1 - fi - - UPDATED=0 - for f in "${FILES[@]}"; do - if grep -q 'cacheBust=' "$f"; then - # Use portable sed in GNU environment of ubuntu-latest - sed -i -E "s/cacheBust=[0-9]+/cacheBust=${TS}/g" "$f" - echo "Updated cacheBust in $f" - UPDATED=1 - else - echo "Warning: cacheBust parameter not found in $f" >&2 - fi - done - - if [ "$UPDATED" -eq 0 ]; then - echo "No files were updated. Ensure READMEs embed contrib.rocks with cacheBust param." >&2 - exit 1 - fi - - - name: Detect changes - id: changes - run: | - if git diff --quiet; then - echo "changed=false" >> $GITHUB_OUTPUT - else - echo "changed=true" >> $GITHUB_OUTPUT - fi - - - name: Create Pull Request - if: steps.changes.outputs.changed == 'true' - uses: peter-evans/create-pull-request@v7 - with: - token: ${{ secrets.GITHUB_TOKEN }} - commit-message: "docs: update contributors list [skip ci]" - committer: "github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>" - branch: refresh-contrib-cache - delete-branch: true - title: "Refresh contrib.rocks image cache (all READMEs)" - body: | - Automated refresh of the contrib.rocks image cache by bumping the cacheBust parameter in README.md and locales/*/README.md. - base: main diff --git a/.github/workflows/website-deploy.yml b/.github/workflows/website-deploy.yml deleted file mode 100644 index da2d4228f51..00000000000 --- a/.github/workflows/website-deploy.yml +++ /dev/null @@ -1,59 +0,0 @@ -name: Deploy roocode.com - -on: - push: - branches: - - main - paths: - - 'apps/web-roo-code/**' - workflow_dispatch: - -concurrency: - group: deploy-roocode-com - cancel-in-progress: true - -env: - VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }} - VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }} - -jobs: - check-secrets: - runs-on: ubuntu-latest - outputs: - has-vercel-token: ${{ steps.check.outputs.has-vercel-token }} - steps: - - name: Check if VERCEL_TOKEN exists - id: check - run: | - if [ -n "${{ secrets.VERCEL_TOKEN }}" ]; then - echo "has-vercel-token=true" >> $GITHUB_OUTPUT - else - echo "has-vercel-token=false" >> $GITHUB_OUTPUT - fi - - deploy: - runs-on: ubuntu-latest - needs: check-secrets - if: ${{ needs.check-secrets.outputs.has-vercel-token == 'true' }} - steps: - - name: Checkout code - uses: actions/checkout@v4 - - name: Setup Node.js and pnpm - uses: ./.github/actions/setup-node-pnpm - - name: Run lint - run: pnpm lint - working-directory: apps/web-roo-code - - name: Run type check - run: pnpm check-types - working-directory: apps/web-roo-code - - name: Run build - run: pnpm build - working-directory: apps/web-roo-code - - name: Install Vercel CLI - run: npm install --global vercel@latest - - name: Pull Vercel Environment Information - run: npx vercel pull --yes --environment=production --token=${{ secrets.VERCEL_TOKEN }} - - name: Build Project Artifacts - run: npx vercel build --prod --token=${{ secrets.VERCEL_TOKEN }} - - name: Deploy Project Artifacts to Vercel - run: npx vercel deploy --prebuilt --prod --token=${{ secrets.VERCEL_TOKEN }} diff --git a/.github/workflows/website-preview.yml b/.github/workflows/website-preview.yml deleted file mode 100644 index 9446bc77531..00000000000 --- a/.github/workflows/website-preview.yml +++ /dev/null @@ -1,102 +0,0 @@ -name: Preview roocode.com - -on: - push: - branches-ignore: - - main - paths: - - "apps/web-roo-code/**" - pull_request: - paths: - - "apps/web-roo-code/**" - workflow_dispatch: - -concurrency: - group: preview-roocode-com-${{ github.ref }} - cancel-in-progress: true - -env: - VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }} - VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }} - -jobs: - check-secrets: - runs-on: ubuntu-latest - outputs: - has-vercel-token: ${{ steps.check.outputs.has-vercel-token }} - steps: - - name: Check if VERCEL_TOKEN exists - id: check - run: | - if [ -n "${{ secrets.VERCEL_TOKEN }}" ]; then - echo "has-vercel-token=true" >> $GITHUB_OUTPUT - else - echo "has-vercel-token=false" >> $GITHUB_OUTPUT - fi - - preview: - runs-on: ubuntu-latest - needs: check-secrets - if: ${{ needs.check-secrets.outputs.has-vercel-token == 'true' }} - steps: - - name: Checkout code - uses: actions/checkout@v4 - - name: Setup Node.js and pnpm - uses: ./.github/actions/setup-node-pnpm - - name: Run lint - run: pnpm lint - working-directory: apps/web-roo-code - - name: Run type check - run: pnpm check-types - working-directory: apps/web-roo-code - - name: Run build - run: pnpm build - working-directory: apps/web-roo-code - - name: Install Vercel CLI - run: npm install --global vercel@latest - - name: Pull Vercel Environment Information - run: npx vercel pull --yes --environment=preview --token=${{ secrets.VERCEL_TOKEN }} - - name: Build Project Artifacts - run: npx vercel build --token=${{ secrets.VERCEL_TOKEN }} - - name: Deploy Project Artifacts to Vercel - id: deploy - run: | - DEPLOYMENT_URL=$(npx vercel deploy --prebuilt --token=${{ secrets.VERCEL_TOKEN }}) - echo "deployment_url=$DEPLOYMENT_URL" >> $GITHUB_OUTPUT - echo "Preview deployed to: $DEPLOYMENT_URL" - - - name: Comment PR with preview link - if: github.event_name == 'pull_request' - uses: actions/github-script@v7 - with: - script: | - const deploymentUrl = '${{ steps.deploy.outputs.deployment_url }}'; - const commentIdentifier = ''; - - const { data: comments } = await github.rest.issues.listComments({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: context.issue.number, - }); - - const existingComment = comments.find(comment => - comment.body.includes(commentIdentifier) - ); - - const comment = commentIdentifier + '\n🚀 **Preview deployed!**\n\nYour changes have been deployed to Vercel:\n\n**Preview URL:** ' + deploymentUrl + '\n\nThis preview will be updated automatically when you push new commits to this PR.'; - - if (existingComment) { - await github.rest.issues.updateComment({ - owner: context.repo.owner, - repo: context.repo.repo, - comment_id: existingComment.id, - body: comment - }); - } else { - await github.rest.issues.createComment({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: context.issue.number, - body: comment - }); - } diff --git a/CHANGELOG.md b/CHANGELOG.md index 3b25d32598a..06432ae25d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1274,7 +1274,6 @@ - Reposition Add Image button inside ChatTextArea (thanks @roomote!) - Bring back a way to temporarily and globally pause auto-approve without losing your toggle state (thanks @brunobergher!) - Makes text area buttons appear only when there's text (thanks @brunobergher!) -- CONTRIBUTING.md tweaks and issue template rewrite (thanks @hannesrudolph!) - Bump axios from 1.9.0 to 1.12.0 (thanks @dependabot!) ## [3.28.2] - 2025-09-14 @@ -1734,7 +1733,6 @@ - Fix Claude model detection by name for API protocol selection (thanks @daniel-lxs!) - Move marketplace icon from overflow menu to top navigation - Optional setting to prevent completion with open todos -- Added YouTube to website footer (thanks @thill2323!) ## [3.23.14] - 2025-07-17 @@ -2128,7 +2126,6 @@ - Fix bug with context condensing in Amazon Bedrock - Fix UTF-8 encoding in ExecaTerminalProcess (thanks @mr-ryan-james!) - Set sidebar name bugfix (thanks @chrarnoldus!) -- Fix link to CONTRIBUTING.md in feature request template (thanks @cannuri!) - Add task metadata to Unbound and improve caching logic (thanks @pugazhendhi-m!) ## [3.19.0] - 2025-05-29 @@ -3096,7 +3093,6 @@ - Ask and Architect modes can now edit markdown files - Custom modes can now be restricted to specific file patterns (for example, a technical writer who can only edit markdown files 👋) - Support for configuring the Bedrock provider with AWS Profiles -- New Roo Code community Discord at https://roocode.com/discord! ## [3.2.8] @@ -3136,8 +3132,6 @@ - Create specialized assistants for any workflow - Just type "Create a new mode for " or visit the Prompts tab in the top menu to get started -Join us at https://www.reddit.com/r/RooCode to share your custom modes and be part of our next chapter! - ## [3.1.7] - DeepSeek-R1 support (thanks @philipnext!) @@ -3185,12 +3179,8 @@ Join us at https://www.reddit.com/r/RooCode to share your custom modes and be pa ## [3.0.1] -- Fix the reddit link and a small visual glitch in the chat input - ## [3.0.0] -- This release adds chat modes! Now you can ask Roo Code questions about system architecture or the codebase without immediately jumping into writing code. You can even assign different API configuration profiles to each mode if you prefer to use different models for thinking vs coding. Would love feedback in the new Roo Code Reddit! https://www.reddit.com/r/RooCode - ## [2.2.46] - Only parse @-mentions in user input (not in files) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md deleted file mode 100644 index 328bb5c1b2e..00000000000 --- a/CODE_OF_CONDUCT.md +++ /dev/null @@ -1,90 +0,0 @@ -
- - -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) • [Bahasa Indonesia](locales/id/CODE_OF_CONDUCT.md) • [Italiano](locales/it/CODE_OF_CONDUCT.md) • [日本語](locales/ja/CODE_OF_CONDUCT.md) - - - - -[한국어](locales/ko/CODE_OF_CONDUCT.md) • [Nederlands](locales/nl/CODE_OF_CONDUCT.md) • [Polski](locales/pl/CODE_OF_CONDUCT.md) • [Português (BR)](locales/pt-BR/CODE_OF_CONDUCT.md) • [Русский](locales/ru/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 - -In the interest of fostering an open and welcoming environment, we as -contributors and maintainers pledge to make participation in our project and -our community a harassment-free experience for everyone, regardless of age, body -size, disability, ethnicity, sex characteristics, gender identity and expression, -level of experience, education, socio-economic status, nationality, personal -appearance, race, religion, or sexual identity and orientation. - -## Our Standards - -Examples of behavior that contributes to creating a positive environment -include: - -- Using welcoming and inclusive language -- Being respectful of differing viewpoints and experiences -- Gracefully accepting constructive criticism -- Focusing on what is best for the community -- Showing empathy towards other community members - -Examples of unacceptable behavior by participants include: - -- The use of sexualized language or imagery and unwelcome sexual attention or - advances -- Trolling, insulting/derogatory comments, and personal or political attacks -- Public or private harassment -- Publishing others' private information, such as a physical or electronic - address, without explicit permission -- Other conduct which could reasonably be considered inappropriate in a - professional setting - -## Our Responsibilities - -Project maintainers are responsible for clarifying the standards of acceptable -behavior and are expected to take appropriate and fair corrective action in -response to any instances of unacceptable behavior. - -Project maintainers have the right and responsibility to remove, edit, or -reject comments, commits, code, wiki edits, issues, and other contributions -that are not aligned to this Code of Conduct, or to ban temporarily or -permanently any contributor for other behaviors that they deem inappropriate, -threatening, offensive, or harmful. - -## Scope - -This Code of Conduct applies both within project spaces and in public spaces -when an individual is representing the project or its community. Examples of -representing a project or community include using an official project e-mail -address, posting via an official social media account, or acting as an appointed -representative at an online or offline event. Representation of a project may be -further defined and clarified by project maintainers. - -## Enforcement - -Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported by contacting the project team at support@roocode.com. All complaints -will be reviewed and investigated and will result in a response that -is deemed necessary and appropriate to the circumstances. The project team is -obligated to maintain confidentiality with regard to the reporter of an incident. -Further details of specific enforcement policies may be posted separately. - -Project maintainers who do not follow or enforce the Code of Conduct in good -faith may face temporary or permanent repercussions as determined by other -members of the project's leadership. - -## Attribution - -This Code of Conduct is adapted from [Cline's version][cline_coc] of the [Contributor Covenant][homepage], version 1.4, -available at 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 - -For answers to common questions about this code of conduct, see -https://www.contributor-covenant.org/faq diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index 869b59a16da..00000000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,141 +0,0 @@ -
- - -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) • [Bahasa Indonesia](locales/id/CONTRIBUTING.md) • [Italiano](locales/it/CONTRIBUTING.md) • [日本語](locales/ja/CONTRIBUTING.md) - - - - -[한국어](locales/ko/CONTRIBUTING.md) • [Nederlands](locales/nl/CONTRIBUTING.md) • [Polski](locales/pl/CONTRIBUTING.md) • [Português (BR)](locales/pt-BR/CONTRIBUTING.md) • [Русский](locales/ru/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) - - -
- -# Contributing to Roo Code - -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. - -## Table of Contents - -- [Before You Contribute](#before-you-contribute) -- [Finding & Planning Your Contribution](#finding--planning-your-contribution) -- [Development & Submission Process](#development--submission-process) -- [Legal](#legal) - -## Before You Contribute - -### 1. Code of Conduct - -All contributors must adhere to our [Code of Conduct](./CODE_OF_CONDUCT.md). - -### 2. Project Roadmap - -Our roadmap guides the project's direction. Align your contributions with these key goals: - -### Reliability First - -- 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. - -### Enhanced User Experience - -- Streamline the UI/UX for clarity and intuitiveness. -- Continuously improve the workflow to meet the high expectations developers have for daily-use tools. - -### Leading on Agent Performance - -- 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. - -Mention alignment with these areas in your PRs. - -### 3. Join the Roo Code Community - -- **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). - -## Finding & Planning Your Contribution - -### Types of Contributions - -- **Bug Fixes:** Addressing code issues. -- **New Features:** Adding functionality. -- **Documentation:** Improving guides and clarity. - -### Issue-First Approach - -All contributions start with a GitHub Issue using our skinny templates. - -- **Check existing issues**: Search [GitHub Issues](https://github.com/RooCodeInc/Roo-Code/issues). -- **Create an issue** using: - - **Enhancements:** "Enhancement Request" template (plain language focused on user benefit). - - **Bugs:** "Bug Report" template (minimal repro + expected vs actual + version). -- **Want to work on it?** Comment "Claiming" on the issue and DM **Hannes Rudolph (`hrudolph`)** on [Discord](https://discord.gg/roocode) to get assigned. Assignment will be confirmed in the thread. -- **PRs must link to the issue.** Unlinked PRs may be closed. - -### Deciding What to Work On - -- Check the [GitHub Project](https://github.com/orgs/RooCodeInc/projects/1) for "Issue [Unassigned]" issues. -- For docs, visit [Roo Code Docs](https://github.com/RooCodeInc/Roo-Code-Docs). - -### Reporting Bugs - -- Check for existing reports first. -- Create a new bug using the ["Bug Report" template](https://github.com/RooCodeInc/Roo-Code/issues/new/choose) with: - - Clear, numbered reproduction steps - - Expected vs actual result - - Roo Code version (required); API provider/model if relevant -- **Security issues**: Report privately via [security advisories](https://github.com/RooCodeInc/Roo-Code/security/advisories/new). - -## Development & Submission Process - -### Development Setup - -1. **Fork & Clone:** - -``` -git clone https://github.com/YOUR_USERNAME/Roo-Code.git -``` - -2. **Install Dependencies:** - -``` -pnpm install -``` - -3. **Debugging:** Open with VS Code (`F5`). - -### Writing Code Guidelines - -- 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. - -### Submitting a Pull Request - -- Begin as a **Draft PR** if seeking early feedback. -- Clearly describe your changes following the Pull Request Template. -- Link the issue in the PR description/title (e.g., "Fixes #123"). -- Provide screenshots/videos for UI changes. -- Indicate if documentation updates are necessary. - -### Pull Request Policy - -- Must reference an assigned GitHub Issue. To get assigned: comment "Claiming" on the issue and DM **Hannes Rudolph (`hrudolph`)** on [Discord](https://discord.gg/roocode). Assignment will be confirmed in the thread. -- Unlinked PRs may be closed. -- PRs should pass CI tests, align with the roadmap, and have clear documentation. - -### Review Process - -- **Daily Triage:** Quick checks by maintainers. -- **Weekly In-depth Review:** Comprehensive assessment. -- **Iterate promptly** based on feedback. - -## Legal - -By contributing, you agree your contributions will be licensed under the Apache 2.0 License, consistent with Roo Code's licensing. diff --git a/PRIVACY.md b/PRIVACY.md index 02e8e151034..353b8ae5606 100644 --- a/PRIVACY.md +++ b/PRIVACY.md @@ -6,12 +6,11 @@ Roo Code respects your privacy and is committed to transparency about how we han ### **Where Your Data Goes (And Where It Doesn’t)** -- **Code & Files**: Roo Code accesses files on your local machine when needed for AI-assisted features. When you send commands to Roo Code, relevant files may be transmitted to your chosen AI model provider (e.g., OpenAI, Anthropic, OpenRouter) to generate responses. If you select Roo Code Cloud as the model provider (proxy mode), your code may transit Roo Code servers only to forward it to the upstream provider. We do not store your code; it is deleted immediately after forwarding. Otherwise, your code is sent directly to the provider. AI providers may store data per their privacy policies. +- **Code & Files**: Roo Code accesses files on your local machine when needed for AI-assisted features. When you send commands to Roo Code, relevant files may be transmitted to your chosen AI model provider (e.g., OpenAI, Anthropic, OpenRouter) to generate responses. AI providers may store data per their privacy policies. - **Commands**: Any commands executed through Roo Code happen on your local environment. However, when you use AI-powered features, the relevant code and context from your commands may be transmitted to your chosen AI model provider (e.g., OpenAI, Anthropic, OpenRouter) to generate responses. We do not have access to or store this data, but AI providers may process it per their privacy policies. -- **Prompts & AI Requests**: When you use AI-powered features, your prompts and relevant project context are sent to your chosen AI model provider (e.g., OpenAI, Anthropic, OpenRouter) to generate responses. We do not store or process this data. These AI providers have their own privacy policies and may store data per their terms of service. If you choose Roo Code Cloud as the provider (proxy mode), prompts may transit Roo Code servers only to forward them to the upstream model and are not stored. +- **Prompts & AI Requests**: When you use AI-powered features, your prompts and relevant project context are sent to your chosen AI model provider (e.g., OpenAI, Anthropic, OpenRouter) to generate responses. We do not store or process this data. These AI providers have their own privacy policies and may store data per their terms of service. - **API Keys & Credentials**: If you enter an API key (e.g., to connect an AI model), it is stored locally on your device and never sent to us or any third party, except the provider you have chosen. - **Telemetry (Usage Data)**: We collect anonymous feature usage and error data to help us improve Roo Code. This telemetry is powered by PostHog and includes your VS Code machine ID, feature usage patterns, and exception reports. This telemetry does **not** collect personally identifiable information, your code, or AI prompts. You can opt out of this telemetry at any time through the settings. -- **Marketplace Requests**: When you browse or search the Marketplace for Model Configuration Profiles (MCPs) or Custom Modes, Roo Code makes a secure API call to Roo Code's backend servers to retrieve listing information. These requests send only the query parameters (e.g., extension version, search term) necessary to fulfill the request and do not include your code, prompts, or personally identifiable information. ### **How We Use Your Data (If Collected)** diff --git a/README.md b/README.md index c915556babc..6ca1976bf93 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,5 @@

- VS Code Marketplace - X - YouTube - Join Discord - Join r/RooCode -

-

- Get help fast → Join Discord • Prefer async? → Join r/RooCode + VS Code Marketplace

# Roo Code @@ -17,20 +10,6 @@ > maintained at [simurg79/Roo-Code](https://github.com/simurg79/Roo-Code). It tracks upstream and may > include experimental branches (e.g. `feature/per-mode-allowed-tools-mcp`) that are not yet upstreamed. -## What's New in v3.53.0 - -> ### The Roo Code plugin is not going away. -> -> You may have seen the [recent announcement](https://x.com/mattrubens/status/2046636598859559114) that Roo Code hit 3 million installs and the original team is going all-in on Roomote. We know that news was hard for a lot of you. This plugin means a lot to us and to you, and we hear you. -> -> The good news: **a community team has stepped up to carry Roo Code forward**, and we're working with them on an official handoff so the plugin you rely on keeps getting maintained and improved. - -**What's new in this release:** - -- Add GPT-5.5 support via the OpenAI Codex provider. -- Add Claude Opus 4.7 support on Vertex AI. -- Add previous checkpoint navigation controls in chat so you can step back through prior checkpoints more easily. -
🌐 Available languages @@ -77,117 +56,26 @@ Roo Code adapts to how you work: - Debug Mode: trace issues, add logs, isolate root causes - Custom Modes: build specialized modes for your team or workflow -Learn more: [Using Modes](https://docs.roocode.com/basic-usage/using-modes) • [Custom Modes](https://docs.roocode.com/advanced-usage/custom-modes) - -## Tutorial & Feature Videos - -
- -| | | | -| :-----------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------: | -|
Installing Roo Code |
Configuring Profiles |
Codebase Indexing | -|
Custom Modes |
Checkpoints |
Context Management | - -
-

-More quick tutorial and feature videos... -

+Learn more: [Using Modes](https://roocodeinc.github.io/Roo-Code/basic-usage/using-modes) • [Custom Modes](https://roocodeinc.github.io/Roo-Code/advanced-usage/custom-modes) ## Resources -- **[Documentation](https://docs.roocode.com):** The official guide to installing, configuring, and mastering Roo Code. -- **[YouTube Channel](https://youtube.com/@roocodeyt?feature=shared):** Watch tutorials and see features in action. -- **[Discord Server](https://discord.gg/roocode):** Join the community for real-time help and discussion. -- **[Reddit Community](https://www.reddit.com/r/RooCode):** Share your experiences and see what others are building. +- **[Documentation](https://roocodeinc.github.io/Roo-Code/):** The official guide to installing, configuring, and mastering Roo Code. - **[GitHub Issues](https://github.com/RooCodeInc/Roo-Code/issues):** Report bugs and track development. -- **[Feature Requests](https://github.com/RooCodeInc/Roo-Code/discussions/categories/feature-requests?discussions_q=is%3Aopen+category%3A%22Feature+Requests%22+sort%3Atop):** Have an idea? Share it with the developers. - ---- - -## Local Setup & Development - -1. **Clone** the repo: - -```sh -git clone https://github.com/RooCodeInc/Roo-Code.git -``` - -2. **Install dependencies**: - -```sh -pnpm install -``` - -3. **Run the extension**: - -There are several ways to run the Roo Code extension: - -### Development Mode (F5) - -For active development, use VSCode's built-in debugging: - -Press `F5` (or go to **Run** → **Start Debugging**) in VSCode. This will open a new VSCode window with the Roo Code extension running. - -- Changes to the webview will appear immediately. -- Changes to the core extension will also hot reload automatically. - -### Automated VSIX Installation - -To build and install the extension as a VSIX package directly into VSCode: - -```sh -pnpm install:vsix [-y] [--editor=] -``` - -This command will: - -- Ask which editor command to use (code/cursor/code-insiders) - defaults to 'code' -- Uninstall any existing version of the extension. -- Build the latest VSIX package. -- Install the newly built VSIX. -- Prompt you to restart VS Code for changes to take effect. - -Options: - -- `-y`: Skip all confirmation prompts and use defaults -- `--editor=`: Specify the editor command (e.g., `--editor=cursor` or `--editor=code-insiders`) - -### Manual VSIX Installation - -If you prefer to install the VSIX package manually: - -1. First, build the VSIX package: - ```sh - pnpm vsix - ``` -2. A `.vsix` file will be generated in the `bin/` directory (e.g., `bin/roo-cline-.vsix`). -3. Install it manually using the VSCode CLI: - ```sh - code --install-extension bin/roo-cline-.vsix - ``` - ---- - -We use [changesets](https://github.com/changesets/changesets) for versioning and publishing. Check our `CHANGELOG.md` for release notes. --- ## Disclaimer -**Please note** that Roo Code, Inc does **not** make any representations or warranties regarding any code, models, or other tools provided or made available in connection with Roo Code, any associated third-party tools, or any resulting outputs. You assume **all risks** associated with the use of any such tools or outputs; such tools are provided on an **"AS IS"** and **"AS AVAILABLE"** basis. Such risks may include, without limitation, intellectual property infringement, cyber vulnerabilities or attacks, bias, inaccuracies, errors, defects, viruses, downtime, property loss or damage, and/or personal injury. You are solely responsible for your use of any such tools or outputs (including, without limitation, the legality, appropriateness, and results thereof). +The Roo Code Extension was shut down on May 15th. ---- +- If you're looking for an alternative, check out [ZooCode](https://github.com/Zoo-Code-Org/Zoo-Code/) (a fork started by the Roo Code community) and [Cline](https://cline.bot/) (from where Roo Code originated). +- If you were a paying user and have billing questions, please write [billing@roocode.com](mailto:billing@roocode.com). -## Contributing - -We love community contributions! Get started by reading our [CONTRIBUTING.md](CONTRIBUTING.md). +**Please note** that Roo Code, Inc does **not** make any representations or warranties regarding any code, models, or other tools provided or made available in connection with Roo Code, any associated third-party tools, or any resulting outputs. You assume **all risks** associated with the use of any such tools or outputs; such tools are provided on an **"AS IS"** and **"AS AVAILABLE"** basis. Such risks may include, without limitation, intellectual property infringement, cyber vulnerabilities or attacks, bias, inaccuracies, errors, defects, viruses, downtime, property loss or damage, and/or personal injury. You are solely responsible for your use of any such tools or outputs (including, without limitation, the legality, appropriateness, and results thereof). --- ## License -[Apache 2.0 © 2025 Roo Code, Inc.](./LICENSE) - ---- - -**Enjoy Roo Code!** Whether you keep it on a short leash or let it roam autonomously, we can’t wait to see what you build. If you have questions or feature ideas, drop by our [Reddit community](https://www.reddit.com/r/RooCode/) or [Discord](https://discord.gg/roocode). Happy coding! +[Apache 2.0 © 2026 Roo Code, Inc.](./LICENSE) diff --git a/apps/cli/README.md b/apps/cli/README.md index 8dec1f3a1c6..6c9b73fa697 100644 --- a/apps/cli/README.md +++ b/apps/cli/README.md @@ -53,21 +53,6 @@ roo upgrade rm -rf ~/.roo/cli ~/.local/bin/roo ``` -### Development Installation - -For contributing or development: - -```bash -# From the monorepo root. -pnpm install - -# Build the main extension first. -pnpm --filter roo-cline bundle - -# Build the CLI. -pnpm --filter @roo-code/cli build -``` - ## Usage ### Interactive Mode (Default) @@ -130,81 +115,29 @@ printf '{"command":"start","requestId":"1","prompt":"1+1=?"}\n' | roo --print -- printf '{"command":"start","requestId":"1","taskId":"018f7fc8-7c96-7f7c-98aa-2ec4ff7f6d87","prompt":"1+1=?"}\n' | roo --print --stdin-prompt-stream --output-format stream-json ``` -### Roo Code Cloud Authentication - -To use Roo Code Cloud features (like the provider proxy), you need to authenticate: - -```bash -# Log in to Roo Code Cloud (opens browser) -roo auth login - -# Check authentication status -roo auth status - -# Log out -roo auth logout -``` - -The `auth login` command: - -1. Opens your browser to authenticate with Roo Code Cloud -2. Receives a secure token via localhost callback -3. Stores the token in `~/.config/roo/credentials.json` - -Tokens are valid for 90 days. The CLI will prompt you to re-authenticate when your token expires. - -**Authentication Flow:** - -``` -┌──────┐ ┌─────────┐ ┌───────────────┐ -│ CLI │ │ Browser │ │ Roo Code Cloud│ -└──┬───┘ └────┬────┘ └───────┬───────┘ - │ │ │ - │ Open auth URL │ │ - │─────────────────>│ │ - │ │ │ - │ │ Authenticate │ - │ │─────────────────────>│ - │ │ │ - │ │<─────────────────────│ - │ │ Token via callback │ - │<─────────────────│ │ - │ │ │ - │ Store token │ │ - │ │ │ -``` - ## Options -| Option | Description | Default | -| --------------------------------------- | --------------------------------------------------------------------------------------- | ---------------------------------------- | -| `[prompt]` | Your prompt (positional argument, optional) | None | -| `--prompt-file ` | Read prompt from a file instead of command line argument | None | -| `--create-with-session-id ` | Create a new task using the provided session ID (UUID) | None | -| `-w, --workspace ` | Workspace path to operate in | Current directory | -| `-p, --print` | Print response and exit (non-interactive mode) | `false` | -| `--stdin-prompt-stream` | Read NDJSON control commands from stdin (requires `--print`) | `false` | -| `-e, --extension ` | Path to the extension bundle directory | Auto-detected | -| `-d, --debug` | Enable debug output (includes detailed debug information, prompts, paths, etc) | `false` | -| `-a, --require-approval` | Require manual approval before actions execute | `false` | -| `-k, --api-key ` | API key for the LLM provider | From env var | -| `--provider ` | API provider (roo, anthropic, openai, openrouter, etc.) | `openrouter` (or `roo` if authenticated) | -| `-m, --model ` | Model to use | `anthropic/claude-opus-4.6` | -| `--mode ` | Mode to start in (code, architect, ask, debug, etc.) | `code` | -| `--terminal-shell ` | Absolute shell path for inline terminal command execution | Auto-detected shell | -| `-r, --reasoning-effort ` | Reasoning effort level (unspecified, disabled, none, minimal, low, medium, high, xhigh) | `medium` | -| `--consecutive-mistake-limit ` | Consecutive error/repetition limit before guidance prompt (`0` disables the limit) | `10` | -| `--ephemeral` | Run without persisting state (uses temporary storage) | `false` | -| `--oneshot` | Exit upon task completion | `false` | -| `--output-format ` | Output format with `--print`: `text`, `json`, or `stream-json` | `text` | - -## Auth Commands - -| Command | Description | -| ----------------- | ---------------------------------- | -| `roo auth login` | Authenticate with Roo Code Cloud | -| `roo auth logout` | Clear stored authentication token | -| `roo auth status` | Show current authentication status | +| Option | Description | Default | +| --------------------------------------- | --------------------------------------------------------------------------------------- | --------------------------- | +| `[prompt]` | Your prompt (positional argument, optional) | None | +| `--prompt-file ` | Read prompt from a file instead of command line argument | None | +| `--create-with-session-id ` | Create a new task using the provided session ID (UUID) | None | +| `-w, --workspace ` | Workspace path to operate in | Current directory | +| `-p, --print` | Print response and exit (non-interactive mode) | `false` | +| `--stdin-prompt-stream` | Read NDJSON control commands from stdin (requires `--print`) | `false` | +| `-e, --extension ` | Path to the extension bundle directory | Auto-detected | +| `-d, --debug` | Enable debug output (includes detailed debug information, prompts, paths, etc) | `false` | +| `-a, --require-approval` | Require manual approval before actions execute | `false` | +| `-k, --api-key ` | API key for the LLM provider | From env var | +| `--provider ` | API provider (anthropic, openai, openrouter, etc.) | `openrouter` | +| `-m, --model ` | Model to use | `anthropic/claude-opus-4.6` | +| `--mode ` | Mode to start in (code, architect, ask, debug, etc.) | `code` | +| `--terminal-shell ` | Absolute shell path for inline terminal command execution | Auto-detected shell | +| `-r, --reasoning-effort ` | Reasoning effort level (unspecified, disabled, none, minimal, low, medium, high, xhigh) | `medium` | +| `--consecutive-mistake-limit ` | Consecutive error/repetition limit before guidance prompt (`0` disables the limit) | `10` | +| `--ephemeral` | Run without persisting state (uses temporary storage) | `false` | +| `--oneshot` | Exit upon task completion | `false` | +| `--output-format ` | Output format with `--print`: `text`, `json`, or `stream-json` | `text` | ## Environment Variables @@ -212,19 +145,12 @@ The CLI will look for API keys in environment variables if not provided via `--a | Provider | Environment Variable | | ----------------- | --------------------------- | -| roo | `ROO_API_KEY` | | anthropic | `ANTHROPIC_API_KEY` | | openai-native | `OPENAI_API_KEY` | | openrouter | `OPENROUTER_API_KEY` | | gemini | `GOOGLE_API_KEY` | | vercel-ai-gateway | `VERCEL_AI_GATEWAY_API_KEY` | -**Authentication Environment Variables:** - -| Variable | Description | -| ----------------- | -------------------------------------------------------------------- | -| `ROO_WEB_APP_URL` | Override the Roo Code Cloud URL (default: `https://app.roocode.com`) | - ## Architecture ``` @@ -268,7 +194,7 @@ The CLI will look for API keys in environment variables if not provided via `--a ```bash # Run directly from source (no build required) -pnpm dev --provider roo --api-key $ROO_API_KEY --print "Hello" +pnpm dev --provider openrouter --api-key $OPENROUTER_API_KEY --print "Hello" # Run tests pnpm test @@ -280,12 +206,6 @@ pnpm check-types pnpm lint ``` -By default the `start` script points `ROO_CODE_PROVIDER_URL` at `http://localhost:8080/proxy` for local development. To point at the production API instead, override the environment variable: - -```bash -ROO_CODE_PROVIDER_URL=https://api.roocode.com/proxy pnpm dev --provider roo --api-key $ROO_API_KEY --print "Hello" -``` - ## Releasing Official releases are created via the GitHub Actions workflow at `.github/workflows/cli-release.yml`. diff --git a/apps/cli/package.json b/apps/cli/package.json index 9276f170534..3920c6b75f4 100644 --- a/apps/cli/package.json +++ b/apps/cli/package.json @@ -16,8 +16,8 @@ "test:integration": "tsx scripts/integration/run.ts", "build": "tsup", "build:extension": "pnpm --filter roo-cline bundle", - "dev": "ROO_AUTH_BASE_URL=https://app.roocode.com ROO_SDK_BASE_URL=https://cloud-api.roocode.com ROO_CODE_PROVIDER_URL=https://api.roocode.com/proxy tsx src/index.ts", - "dev:local": "ROO_AUTH_BASE_URL=http://localhost:3000 ROO_SDK_BASE_URL=http://localhost:3001 ROO_CODE_PROVIDER_URL=http://localhost:8080/proxy tsx src/index.ts", + "dev": "tsx src/index.ts", + "dev:local": "tsx src/index.ts", "clean": "rimraf dist .turbo" }, "dependencies": { diff --git a/apps/cli/scripts/integration/cases/create-with-session-id-resume-loads-correct-session.ts b/apps/cli/scripts/integration/cases/create-with-session-id-resume-loads-correct-session.ts index cbefd265258..ded1656e1ee 100644 --- a/apps/cli/scripts/integration/cases/create-with-session-id-resume-loads-correct-session.ts +++ b/apps/cli/scripts/integration/cases/create-with-session-id-resume-loads-correct-session.ts @@ -88,7 +88,7 @@ async function createSessionWithCustomId( "dev", "--print", "--provider", - "roo", + "openrouter", "--output-format", "stream-json", "--workspace", @@ -148,7 +148,7 @@ async function resumeSessionAndSendMarker( "--print", "--stdin-prompt-stream", "--provider", - "roo", + "openrouter", "--output-format", "stream-json", "--workspace", diff --git a/apps/cli/scripts/integration/lib/stream-harness.ts b/apps/cli/scripts/integration/lib/stream-harness.ts index 73b756c7c3a..2693103309d 100644 --- a/apps/cli/scripts/integration/lib/stream-harness.ts +++ b/apps/cli/scripts/integration/lib/stream-harness.ts @@ -69,7 +69,7 @@ export async function runStreamCase(options: RunStreamCaseOptions): Promise) this.sendToExtension({ type: "updateSettings", updatedSettings: this.initialSettings }) diff --git a/apps/cli/src/commands/auth/index.ts b/apps/cli/src/commands/auth/index.ts deleted file mode 100644 index 52ae7673a7e..00000000000 --- a/apps/cli/src/commands/auth/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from "./login.js" -export * from "./logout.js" -export * from "./status.js" diff --git a/apps/cli/src/commands/auth/login.ts b/apps/cli/src/commands/auth/login.ts deleted file mode 100644 index ab85385b0f9..00000000000 --- a/apps/cli/src/commands/auth/login.ts +++ /dev/null @@ -1,177 +0,0 @@ -import http from "http" -import { randomBytes } from "crypto" -import net from "net" -import { exec } from "child_process" - -import { AUTH_BASE_URL } from "@/types/index.js" -import { saveToken } from "@/lib/storage/index.js" - -export interface LoginOptions { - timeout?: number - verbose?: boolean -} - -export type LoginResult = - | { - success: true - token: string - } - | { - success: false - error: string - } - -const LOCALHOST = "127.0.0.1" - -export async function login({ timeout = 5 * 60 * 1000, verbose = false }: LoginOptions = {}): Promise { - const state = randomBytes(16).toString("hex") - const port = await getAvailablePort() - const host = `http://${LOCALHOST}:${port}` - - if (verbose) { - console.log(`[Auth] Starting local callback server on port ${port}`) - } - - // Create promise that will be resolved when we receive the callback. - const tokenPromise = new Promise<{ token: string; state: string }>((resolve, reject) => { - const server = http.createServer((req, res) => { - const url = new URL(req.url!, host) - - if (url.pathname === "/callback") { - const receivedState = url.searchParams.get("state") - const token = url.searchParams.get("token") - const error = url.searchParams.get("error") - - if (error) { - const errorUrl = new URL(`${AUTH_BASE_URL}/cli/sign-in?error=error-in-callback`) - errorUrl.searchParams.set("message", error) - res.writeHead(302, { Location: errorUrl.toString() }) - res.end(() => { - server.close() - reject(new Error(error)) - }) - } else if (!token) { - const errorUrl = new URL(`${AUTH_BASE_URL}/cli/sign-in?error=missing-token`) - errorUrl.searchParams.set("message", "Missing token in callback") - res.writeHead(302, { Location: errorUrl.toString() }) - res.end(() => { - server.close() - reject(new Error("Missing token in callback")) - }) - } else if (receivedState !== state) { - const errorUrl = new URL(`${AUTH_BASE_URL}/cli/sign-in?error=invalid-state-parameter`) - errorUrl.searchParams.set("message", "Invalid state parameter") - res.writeHead(302, { Location: errorUrl.toString() }) - res.end(() => { - server.close() - reject(new Error("Invalid state parameter")) - }) - } else { - res.writeHead(302, { Location: `${AUTH_BASE_URL}/cli/sign-in?success=true` }) - res.end(() => { - server.close() - resolve({ token, state: receivedState }) - }) - } - } else { - res.writeHead(404, { "Content-Type": "text/plain" }) - res.end("Not found") - } - }) - - server.listen(port, LOCALHOST) - - const timeoutId = setTimeout(() => { - server.close() - reject(new Error("Authentication timed out")) - }, timeout) - - server.on("close", () => { - clearTimeout(timeoutId) - }) - }) - - const authUrl = new URL(`${AUTH_BASE_URL}/cli/sign-in`) - authUrl.searchParams.set("state", state) - authUrl.searchParams.set("callback", `${host}/callback`) - - console.log("Opening browser for authentication...") - console.log(`If the browser doesn't open, visit: ${authUrl.toString()}`) - - try { - await openBrowser(authUrl.toString()) - } catch (error) { - if (verbose) { - console.warn("[Auth] Failed to open browser automatically:", error) - } - - console.log("Please open the URL above in your browser manually.") - } - - try { - const { token } = await tokenPromise - await saveToken(token) - console.log("✓ Successfully authenticated!") - return { success: true, token } - } catch (error) { - const message = error instanceof Error ? error.message : String(error) - console.error(`✗ Authentication failed: ${message}`) - return { success: false, error: message } - } -} - -async function getAvailablePort(startPort = 49152, endPort = 65535): Promise { - return new Promise((resolve, reject) => { - const server = net.createServer() - let port = startPort - - const tryPort = () => { - server.once("error", (err: NodeJS.ErrnoException) => { - if (err.code === "EADDRINUSE" && port < endPort) { - port++ - tryPort() - } else { - reject(err) - } - }) - - server.once("listening", () => { - server.close(() => { - resolve(port) - }) - }) - - server.listen(port, LOCALHOST) - } - - tryPort() - }) -} - -function openBrowser(url: string): Promise { - return new Promise((resolve, reject) => { - const platform = process.platform - let command: string - - switch (platform) { - case "darwin": - command = `open "${url}"` - break - case "win32": - command = `start "" "${url}"` - break - default: - // Linux and other Unix-like systems. - command = `xdg-open "${url}"` - break - } - - exec(command, (error) => { - if (error) { - reject(error) - } else { - resolve() - } - }) - }) -} diff --git a/apps/cli/src/commands/auth/logout.ts b/apps/cli/src/commands/auth/logout.ts deleted file mode 100644 index 61c3cb37a49..00000000000 --- a/apps/cli/src/commands/auth/logout.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { clearToken, hasToken, getCredentialsPath } from "@/lib/storage/index.js" - -export interface LogoutOptions { - verbose?: boolean -} - -export interface LogoutResult { - success: boolean - wasLoggedIn: boolean -} - -export async function logout({ verbose = false }: LogoutOptions = {}): Promise { - const wasLoggedIn = await hasToken() - - if (!wasLoggedIn) { - console.log("You are not currently logged in.") - return { success: true, wasLoggedIn: false } - } - - if (verbose) { - console.log(`[Auth] Removing credentials from ${getCredentialsPath()}`) - } - - await clearToken() - console.log("✓ Successfully logged out") - return { success: true, wasLoggedIn: true } -} diff --git a/apps/cli/src/commands/auth/status.ts b/apps/cli/src/commands/auth/status.ts deleted file mode 100644 index 9e81adfda8a..00000000000 --- a/apps/cli/src/commands/auth/status.ts +++ /dev/null @@ -1,97 +0,0 @@ -import { loadToken, loadCredentials, getCredentialsPath } from "@/lib/storage/index.js" -import { isTokenExpired, isTokenValid, getTokenExpirationDate } from "@/lib/auth/index.js" - -export interface StatusOptions { - verbose?: boolean -} - -export interface StatusResult { - authenticated: boolean - expired?: boolean - expiringSoon?: boolean - userId?: string - orgId?: string | null - expiresAt?: Date - createdAt?: Date -} - -export async function status(options: StatusOptions = {}): Promise { - const { verbose = false } = options - - const token = await loadToken() - - if (!token) { - console.log("✗ Not authenticated") - console.log("") - console.log("Run: roo auth login") - return { authenticated: false } - } - - const expiresAt = getTokenExpirationDate(token) - const expired = !isTokenValid(token) - const expiringSoon = isTokenExpired(token, 24 * 60 * 60) && !expired - - const credentials = await loadCredentials() - const createdAt = credentials?.createdAt ? new Date(credentials.createdAt) : undefined - - if (expired) { - console.log("✗ Authentication token expired") - console.log("") - console.log("Run: roo auth login") - - return { - authenticated: false, - expired: true, - expiresAt: expiresAt ?? undefined, - } - } - - if (expiringSoon) { - console.log("⚠ Expires soon; refresh with `roo auth login`") - } else { - console.log("✓ Authenticated") - } - - if (expiresAt) { - const remaining = getTimeRemaining(expiresAt) - console.log(` Expires: ${formatDate(expiresAt)} (${remaining})`) - } - - if (createdAt && verbose) { - console.log(` Created: ${formatDate(createdAt)}`) - } - - if (verbose) { - console.log(` Credentials: ${getCredentialsPath()}`) - } - - return { - authenticated: true, - expired: false, - expiringSoon, - expiresAt: expiresAt ?? undefined, - createdAt, - } -} - -function formatDate(date: Date): string { - return date.toLocaleDateString("en-US", { year: "numeric", month: "long", day: "numeric" }) -} - -function getTimeRemaining(date: Date): string { - const now = new Date() - const diff = date.getTime() - now.getTime() - - if (diff <= 0) { - return "expired" - } - - const days = Math.floor(diff / (1000 * 60 * 60 * 24)) - const hours = Math.floor((diff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)) - - if (days > 0) { - return `${days} day${days === 1 ? "" : "s"}` - } - - return `${hours} hour${hours === 1 ? "" : "s"}` -} diff --git a/apps/cli/src/commands/cli/list.ts b/apps/cli/src/commands/cli/list.ts index 31898c59cda..1489a5feb94 100644 --- a/apps/cli/src/commands/cli/list.ts +++ b/apps/cli/src/commands/cli/list.ts @@ -6,11 +6,10 @@ import pWaitFor from "p-wait-for" import type { TaskSessionEntry } from "@roo-code/core/cli" import type { Command, ModelRecord, WebviewMessage } from "@roo-code/types" -import { getProviderDefaultModelId } from "@roo-code/types" +import { openRouterDefaultModelId } from "@roo-code/types" import { ExtensionHost, type ExtensionHostOptions } from "@/agent/index.js" import { readWorkspaceTaskSessions } from "@/lib/task-history/index.js" -import { loadToken } from "@/lib/storage/index.js" import { getDefaultExtensionPath } from "@/lib/utils/extension.js" import { getApiKeyFromEnv } from "@/lib/utils/provider.js" import { isRecord } from "@/lib/utils/guards.js" @@ -106,14 +105,14 @@ function outputSessionsText(sessions: SessionLike[]): void { async function createListHost(options: BaseListOptions, hostOptions: ListHostOptions): Promise { const workspacePath = resolveWorkspacePath(options.workspace) const extensionPath = resolveExtensionPath(options.extension) - const apiKey = options.apiKey || (await loadToken()) || getApiKeyFromEnv("roo") + const apiKey = options.apiKey || getApiKeyFromEnv("openrouter") const extensionHostOptions: ExtensionHostOptions = { mode: "code", reasoningEffort: undefined, user: null, - provider: "roo", - model: getProviderDefaultModelId("roo"), + provider: "openrouter", + model: openRouterDefaultModelId, apiKey, workspacePath, extensionPath, @@ -215,26 +214,15 @@ function requestModes(host: ExtensionHost): Promise { }) } -function requestRooModels(host: ExtensionHost): Promise { - return requestFromExtension(host, "requestRooModels", (message) => { - if (message.type !== "singleRouterModelFetchResponse") { +function requestOpenRouterModels(host: ExtensionHost): Promise { + return requestFromExtension(host, "requestRouterModels", (message) => { + if (message.type !== "routerModels") { return undefined } - const values = isRecord(message.values) ? message.values : undefined - if (values?.provider !== "roo") { - return undefined - } - - if (message.success === false) { - const errorMessage = - typeof message.error === "string" && message.error.length > 0 - ? message.error - : "Failed to fetch Roo models" - throw new Error(errorMessage) - } - - return isRecord(values.models) ? (values.models as ModelRecord) : {} + const routerModels = isRecord(message.routerModels) ? message.routerModels : {} + const openRouterModels = routerModels.openrouter + return isRecord(openRouterModels) ? (openRouterModels as ModelRecord) : {} }) } @@ -299,7 +287,7 @@ export async function listModels(options: BaseListOptions): Promise { const format = parseFormat(options.format) await withHostAndSignalHandlers(options, { ephemeral: true }, async (host) => { - const models = await requestRooModels(host) + const models = await requestOpenRouterModels(host) if (format === "json") { outputJson({ models }) diff --git a/apps/cli/src/commands/cli/run.ts b/apps/cli/src/commands/cli/run.ts index 62760919e7e..f16d41cab18 100644 --- a/apps/cli/src/commands/cli/run.ts +++ b/apps/cli/src/commands/cli/run.ts @@ -10,22 +10,17 @@ import { setLogger } from "@roo-code/vscode-shim" import { FlagOptions, isSupportedProvider, - OnboardingProviderChoice, supportedProviders, DEFAULT_FLAGS, REASONING_EFFORTS, - SDK_BASE_URL, OutputFormat, } from "@/types/index.js" import { isValidOutputFormat } from "@/types/json-events.js" import { JsonEventEmitter } from "@/agent/json-event-emitter.js" -import { createClient } from "@/lib/sdk/index.js" -import { loadToken, loadSettings } from "@/lib/storage/index.js" +import { loadSettings } from "@/lib/storage/index.js" import { readWorkspaceTaskSessions, resolveWorkspaceResumeSessionId } from "@/lib/task-history/index.js" -import { isRecord } from "@/lib/utils/guards.js" import { getEnvVarName, getApiKeyFromEnv } from "@/lib/utils/provider.js" -import { runOnboarding } from "@/lib/utils/onboarding.js" import { validateTerminalShellPath } from "@/lib/utils/shell.js" import { getDefaultExtensionPath } from "@/lib/utils/extension.js" import { isValidSessionId } from "@/lib/utils/session-id.js" @@ -36,7 +31,6 @@ import { isExpectedControlFlowError } from "./cancellation.js" import { runStdinStreamMode } from "./stdin-stream.js" const __dirname = path.dirname(fileURLToPath(import.meta.url)) -const ROO_MODEL_WARMUP_TIMEOUT_MS = 10_000 const SIGNAL_ONLY_EXIT_KEEPALIVE_MS = 60_000 const STREAM_RESUME_WAIT_TIMEOUT_MS = 2_000 @@ -54,59 +48,6 @@ function normalizeError(error: unknown): Error { return error instanceof Error ? error : new Error(String(error)) } -async function warmRooModels(host: ExtensionHost): Promise { - await new Promise((resolve, reject) => { - let settled = false - - const cleanup = () => { - clearTimeout(timeoutId) - host.off("extensionWebviewMessage", onMessage) - } - - const finish = (fn: () => void) => { - if (settled) return - settled = true - cleanup() - fn() - } - - const onMessage = (message: unknown) => { - if (!isRecord(message)) { - return - } - - if (message.type !== "singleRouterModelFetchResponse") { - return - } - - const values = isRecord(message.values) ? message.values : undefined - - if (values?.provider !== "roo") { - return - } - - if (message.success === false) { - const errorMessage = - typeof message.error === "string" && message.error.length > 0 - ? message.error - : "failed to refresh Roo models" - - finish(() => reject(new Error(errorMessage))) - return - } - - finish(() => resolve()) - } - - const timeoutId = setTimeout(() => { - finish(() => reject(new Error(`timed out waiting for Roo models after ${ROO_MODEL_WARMUP_TIMEOUT_MS}ms`))) - }, ROO_MODEL_WARMUP_TIMEOUT_MS) - - host.on("extensionWebviewMessage", onMessage) - host.sendToExtension({ type: "requestRooModels" }) - }) -} - export async function run(promptArg: string | undefined, flagOptions: FlagOptions) { setLogger({ info: () => {}, @@ -169,19 +110,17 @@ export async function run(promptArg: string | undefined, flagOptions: FlagOption // Options - let rooToken = await loadToken() const settings = await loadSettings() const isTuiSupported = process.stdin.isTTY && process.stdout.isTTY const isTuiEnabled = !flagOptions.print && isTuiSupported - const isOnboardingEnabled = isTuiEnabled && !rooToken && !flagOptions.provider && !settings.provider // Determine effective values: CLI flags > settings file > DEFAULT_FLAGS. const effectiveMode = flagOptions.mode || settings.mode || DEFAULT_FLAGS.mode const effectiveModel = flagOptions.model || settings.model || DEFAULT_FLAGS.model const effectiveReasoningEffort = flagOptions.reasoningEffort || settings.reasoningEffort || DEFAULT_FLAGS.reasoningEffort - const effectiveProvider = flagOptions.provider ?? settings.provider ?? (rooToken ? "roo" : "openrouter") + const effectiveProvider = flagOptions.provider ?? settings.provider ?? "openrouter" const effectiveWorkspacePath = flagOptions.workspace ? path.resolve(flagOptions.workspace) : process.cwd() const legacyRequireApprovalFromSettings = settings.requireApproval ?? @@ -229,49 +168,6 @@ export async function run(promptArg: string | undefined, flagOptions: FlagOption terminalShell, } - // Roo Code Cloud Authentication - - if (isOnboardingEnabled) { - let { onboardingProviderChoice } = settings - - if (!onboardingProviderChoice) { - const { choice, token } = await runOnboarding() - onboardingProviderChoice = choice - rooToken = token ?? null - } - - if (onboardingProviderChoice === OnboardingProviderChoice.Roo) { - extensionHostOptions.provider = "roo" - } - } - - if (extensionHostOptions.provider === "roo") { - if (rooToken) { - try { - const client = createClient({ url: SDK_BASE_URL, authToken: rooToken }) - const me = await client.auth.me.query() - - if (me?.type !== "user") { - throw new Error("Invalid token") - } - - extensionHostOptions.apiKey = rooToken - extensionHostOptions.user = me.user - } catch { - // If an explicit API key was provided via flag or env var, fall through - // to the general API key resolution below instead of exiting. - if (!flagOptions.apiKey && !getApiKeyFromEnv(extensionHostOptions.provider)) { - console.error("[CLI] Your Roo Code Router token is not valid.") - console.error("[CLI] Please run: roo auth login") - console.error("[CLI] Or use --api-key or set ROO_API_KEY to provide your own API key.") - process.exit(1) - } - } - } - // If no rooToken, fall through to the general API key resolution below - // which will check flagOptions.apiKey and ROO_API_KEY env var. - } - // Validations // TODO: Validate the API key for the chosen provider. // TODO: Validate the model for the chosen provider. @@ -287,18 +183,8 @@ export async function run(promptArg: string | undefined, flagOptions: FlagOption extensionHostOptions.apiKey || flagOptions.apiKey || getApiKeyFromEnv(extensionHostOptions.provider) if (!extensionHostOptions.apiKey) { - if (extensionHostOptions.provider === "roo") { - console.error("[CLI] Error: Authentication with Roo Code Cloud failed or was cancelled.") - console.error("[CLI] Please run: roo auth login") - console.error("[CLI] Or use --api-key to provide your own API key.") - } else { - console.error( - `[CLI] Error: No API key provided. Use --api-key or set the appropriate environment variable.`, - ) - console.error( - `[CLI] For ${extensionHostOptions.provider}, set ${getEnvVarName(extensionHostOptions.provider)}`, - ) - } + console.error(`[CLI] Error: No API key provided. Use --api-key or set the appropriate environment variable.`) + console.error(`[CLI] For ${extensionHostOptions.provider}, set ${getEnvVarName(extensionHostOptions.provider)}`) process.exit(1) } @@ -606,16 +492,6 @@ export async function run(promptArg: string | undefined, flagOptions: FlagOption try { await host.activate() - if (extensionHostOptions.provider === "roo") { - try { - await warmRooModels(host) - } catch (warmupError) { - if (flagOptions.debug) { - const message = warmupError instanceof Error ? warmupError.message : String(warmupError) - console.error(`[CLI] Warning: Roo model warmup failed: ${message}`) - } - } - } if (jsonEmitter) { jsonEmitter.attachToClient(host.client) diff --git a/apps/cli/src/commands/index.ts b/apps/cli/src/commands/index.ts index 717a7040ef6..54fb955464d 100644 --- a/apps/cli/src/commands/index.ts +++ b/apps/cli/src/commands/index.ts @@ -1,2 +1 @@ -export * from "./auth/index.js" export * from "./cli/index.js" diff --git a/apps/cli/src/index.ts b/apps/cli/src/index.ts index 2805e6c9099..79a4a724751 100644 --- a/apps/cli/src/index.ts +++ b/apps/cli/src/index.ts @@ -2,17 +2,7 @@ import { Command } from "commander" import { DEFAULT_FLAGS } from "@/types/constants.js" import { VERSION } from "@/lib/utils/version.js" -import { - run, - login, - logout, - status, - listCommands, - listModes, - listModels, - listSessions, - upgrade, -} from "@/commands/index.js" +import { run, listCommands, listModes, listModels, listSessions, upgrade } from "@/commands/index.js" const program = new Command() @@ -45,7 +35,7 @@ program .option("-d, --debug", "Enable debug output (includes detailed debug information)", false) .option("-a, --require-approval", "Require manual approval for actions", false) .option("-k, --api-key ", "API key for the LLM provider") - .option("--provider ", "API provider (roo, anthropic, openai, openrouter, etc.)") + .option("--provider ", "API provider (anthropic, openai, openrouter, etc.)") .option("-m, --model ", "Model to use", DEFAULT_FLAGS.model) .option("--mode ", "Mode to start in (code, architect, ask, debug, etc.)", DEFAULT_FLAGS.mode) .option("--terminal-shell ", "Absolute path to shell executable for inline terminal commands") @@ -79,7 +69,7 @@ const applyListOptions = (command: Command) => command .option("-w, --workspace ", "Workspace directory path (defaults to current working directory)") .option("-e, --extension ", "Path to the extension bundle directory") - .option("-k, --api-key ", "Roo API key (falls back to saved login/session token)") + .option("-k, --api-key ", "API key for the LLM provider") .option("--format ", 'Output format: "json" (default) or "text"', "json") .option("-d, --debug", "Enable debug output", false) @@ -117,7 +107,7 @@ applyListOptions(listCommand.command("modes").description("List available modes" }, ) -applyListOptions(listCommand.command("models").description("List available Roo models")).action( +applyListOptions(listCommand.command("models").description("List available models")).action( async (options: Parameters[0]) => { await runListAction(() => listModels(options)) }, @@ -136,33 +126,4 @@ program await runUpgradeAction(() => upgrade()) }) -const authCommand = program.command("auth").description("Manage authentication for Roo Code Cloud") - -authCommand - .command("login") - .description("Authenticate with Roo Code Cloud") - .option("-v, --verbose", "Enable verbose output", false) - .action(async (options: { verbose: boolean }) => { - const result = await login({ verbose: options.verbose }) - process.exit(result.success ? 0 : 1) - }) - -authCommand - .command("logout") - .description("Log out from Roo Code Cloud") - .option("-v, --verbose", "Enable verbose output", false) - .action(async (options: { verbose: boolean }) => { - const result = await logout({ verbose: options.verbose }) - process.exit(result.success ? 0 : 1) - }) - -authCommand - .command("status") - .description("Show authentication status") - .option("-v, --verbose", "Enable verbose output", false) - .action(async (options: { verbose: boolean }) => { - const result = await status({ verbose: options.verbose }) - process.exit(result.authenticated ? 0 : 1) - }) - program.parse() diff --git a/apps/cli/src/lib/auth/index.ts b/apps/cli/src/lib/auth/index.ts deleted file mode 100644 index ec1a4598356..00000000000 --- a/apps/cli/src/lib/auth/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./token.js" diff --git a/apps/cli/src/lib/auth/token.ts b/apps/cli/src/lib/auth/token.ts deleted file mode 100644 index 34365223a47..00000000000 --- a/apps/cli/src/lib/auth/token.ts +++ /dev/null @@ -1,61 +0,0 @@ -export interface DecodedToken { - iss: string - sub: string - exp: number - iat: number - nbf: number - v: number - r?: { - u?: string - o?: string - t: string - } -} - -function decodeToken(token: string): DecodedToken | null { - try { - const parts = token.split(".") - - if (parts.length !== 3) { - return null - } - - const payload = parts[1] - - if (!payload) { - return null - } - - const padded = payload + "=".repeat((4 - (payload.length % 4)) % 4) - const decoded = Buffer.from(padded, "base64url").toString("utf-8") - return JSON.parse(decoded) as DecodedToken - } catch { - return null - } -} - -export function isTokenExpired(token: string, bufferSeconds = 24 * 60 * 60): boolean { - const decoded = decodeToken(token) - - if (!decoded?.exp) { - return true - } - - const expiresAt = decoded.exp - const bufferTime = Math.floor(Date.now() / 1000) + bufferSeconds - return expiresAt < bufferTime -} - -export function isTokenValid(token: string): boolean { - return !isTokenExpired(token, 0) -} - -export function getTokenExpirationDate(token: string): Date | null { - const decoded = decodeToken(token) - - if (!decoded?.exp) { - return null - } - - return new Date(decoded.exp * 1000) -} diff --git a/apps/cli/src/lib/storage/__tests__/credentials.test.ts b/apps/cli/src/lib/storage/__tests__/credentials.test.ts deleted file mode 100644 index 574b1b6bf40..00000000000 --- a/apps/cli/src/lib/storage/__tests__/credentials.test.ts +++ /dev/null @@ -1,152 +0,0 @@ -import fs from "fs/promises" -import path from "path" - -// Use vi.hoisted to make the test directory available to the mock -// This must return the path synchronously since CREDENTIALS_FILE is computed at import time -const { getTestConfigDir } = vi.hoisted(() => { - // eslint-disable-next-line @typescript-eslint/no-require-imports - const os = require("os") - // eslint-disable-next-line @typescript-eslint/no-require-imports - const path = require("path") - const testRunId = Date.now().toString() - const testConfigDir = path.join(os.tmpdir(), `roo-cli-test-${testRunId}`) - return { getTestConfigDir: () => testConfigDir } -}) - -vi.mock("../config-dir.js", () => ({ - getConfigDir: getTestConfigDir, -})) - -// Import after mocking -import { saveToken, loadToken, loadCredentials, clearToken, hasToken, getCredentialsPath } from "../credentials.js" - -// Re-derive the test config dir for use in tests (must match the hoisted one) -const actualTestConfigDir = getTestConfigDir() - -describe("Token Storage", () => { - const expectedCredentialsFile = path.join(actualTestConfigDir, "cli-credentials.json") - - beforeEach(async () => { - // Clear test directory before each test - await fs.rm(actualTestConfigDir, { recursive: true, force: true }) - }) - - afterAll(async () => { - // Clean up test directory - await fs.rm(actualTestConfigDir, { recursive: true, force: true }) - }) - - describe("getCredentialsPath", () => { - it("should return the correct credentials file path", () => { - expect(getCredentialsPath()).toBe(expectedCredentialsFile) - }) - }) - - describe("saveToken", () => { - it("should save token to disk", async () => { - const token = "test-token-123" - await saveToken(token) - - const savedData = await fs.readFile(expectedCredentialsFile, "utf-8") - const credentials = JSON.parse(savedData) - - expect(credentials.token).toBe(token) - expect(credentials.createdAt).toBeDefined() - }) - - it("should save token with user info", async () => { - const token = "test-token-456" - await saveToken(token, { userId: "user_123", orgId: "org_456" }) - - const savedData = await fs.readFile(expectedCredentialsFile, "utf-8") - const credentials = JSON.parse(savedData) - - expect(credentials.token).toBe(token) - expect(credentials.userId).toBe("user_123") - expect(credentials.orgId).toBe("org_456") - }) - - it("should create config directory if it doesn't exist", async () => { - const token = "test-token-789" - await saveToken(token) - - const dirStats = await fs.stat(actualTestConfigDir) - expect(dirStats.isDirectory()).toBe(true) - }) - - // Unix file permissions don't apply on Windows - skip this test - it.skipIf(process.platform === "win32")("should set restrictive file permissions", async () => { - const token = "test-token-perms" - await saveToken(token) - - const stats = await fs.stat(expectedCredentialsFile) - // Check that only owner has read/write (mode 0o600) - const mode = stats.mode & 0o777 - expect(mode).toBe(0o600) - }) - }) - - describe("loadToken", () => { - it("should load saved token", async () => { - const token = "test-token-abc" - await saveToken(token) - - const loaded = await loadToken() - expect(loaded).toBe(token) - }) - - it("should return null if no token exists", async () => { - const loaded = await loadToken() - expect(loaded).toBeNull() - }) - }) - - describe("loadCredentials", () => { - it("should load full credentials", async () => { - const token = "test-token-def" - await saveToken(token, { userId: "user_789" }) - - const credentials = await loadCredentials() - - expect(credentials).not.toBeNull() - expect(credentials?.token).toBe(token) - expect(credentials?.userId).toBe("user_789") - expect(credentials?.createdAt).toBeDefined() - }) - - it("should return null if no credentials exist", async () => { - const credentials = await loadCredentials() - expect(credentials).toBeNull() - }) - }) - - describe("clearToken", () => { - it("should remove saved token", async () => { - const token = "test-token-ghi" - await saveToken(token) - - await clearToken() - - const loaded = await loadToken() - expect(loaded).toBeNull() - }) - - it("should not throw if no token exists", async () => { - await expect(clearToken()).resolves.not.toThrow() - }) - }) - - describe("hasToken", () => { - it("should return true if token exists", async () => { - await saveToken("test-token-jkl") - - const exists = await hasToken() - expect(exists).toBe(true) - }) - - it("should return false if no token exists", async () => { - const exists = await hasToken() - expect(exists).toBe(false) - }) - }) -}) diff --git a/apps/cli/src/lib/storage/__tests__/settings.test.ts b/apps/cli/src/lib/storage/__tests__/settings.test.ts index f19b5c3a253..3427a8a89db 100644 --- a/apps/cli/src/lib/storage/__tests__/settings.test.ts +++ b/apps/cli/src/lib/storage/__tests__/settings.test.ts @@ -51,7 +51,7 @@ describe("Settings Storage", () => { it("should load saved settings", async () => { const settingsData = { - onboardingProviderChoice: OnboardingProviderChoice.Roo, + onboardingProviderChoice: OnboardingProviderChoice.Byok, mode: "architect", provider: "anthropic" as const, model: "claude-sonnet-4-20250514", @@ -138,7 +138,7 @@ describe("Settings Storage", () => { describe("resetOnboarding", () => { it("should reset onboarding provider choice", async () => { - await saveSettings({ onboardingProviderChoice: OnboardingProviderChoice.Roo }) + await saveSettings({ onboardingProviderChoice: OnboardingProviderChoice.Byok }) await resetOnboarding() diff --git a/apps/cli/src/lib/storage/credentials.ts b/apps/cli/src/lib/storage/credentials.ts deleted file mode 100644 index b687111c16f..00000000000 --- a/apps/cli/src/lib/storage/credentials.ts +++ /dev/null @@ -1,72 +0,0 @@ -import fs from "fs/promises" -import path from "path" - -import { getConfigDir } from "./index.js" - -const CREDENTIALS_FILE = path.join(getConfigDir(), "cli-credentials.json") - -export interface Credentials { - token: string - createdAt: string - userId?: string - orgId?: string -} - -export async function saveToken(token: string, options?: { userId?: string; orgId?: string }): Promise { - await fs.mkdir(getConfigDir(), { recursive: true }) - - const credentials: Credentials = { - token, - createdAt: new Date().toISOString(), - userId: options?.userId, - orgId: options?.orgId, - } - - await fs.writeFile(CREDENTIALS_FILE, JSON.stringify(credentials, null, 2), { - mode: 0o600, // Read/write for owner only - }) -} - -export async function loadToken(): Promise { - try { - const data = await fs.readFile(CREDENTIALS_FILE, "utf-8") - const credentials: Credentials = JSON.parse(data) - return credentials.token - } catch (error) { - if ((error as NodeJS.ErrnoException).code === "ENOENT") { - return null - } - throw error - } -} - -export async function loadCredentials(): Promise { - try { - const data = await fs.readFile(CREDENTIALS_FILE, "utf-8") - return JSON.parse(data) as Credentials - } catch (error) { - if ((error as NodeJS.ErrnoException).code === "ENOENT") { - return null - } - throw error - } -} - -export async function clearToken(): Promise { - try { - await fs.unlink(CREDENTIALS_FILE) - } catch (error) { - if ((error as NodeJS.ErrnoException).code !== "ENOENT") { - throw error - } - } -} - -export async function hasToken(): Promise { - const token = await loadToken() - return token !== null -} - -export function getCredentialsPath(): string { - return CREDENTIALS_FILE -} diff --git a/apps/cli/src/lib/storage/index.ts b/apps/cli/src/lib/storage/index.ts index 53424472c2a..89246376432 100644 --- a/apps/cli/src/lib/storage/index.ts +++ b/apps/cli/src/lib/storage/index.ts @@ -1,4 +1,3 @@ export * from "./config-dir.js" export * from "./settings.js" -export * from "./credentials.js" export * from "./ephemeral.js" diff --git a/apps/cli/src/lib/utils/onboarding.ts b/apps/cli/src/lib/utils/onboarding.ts deleted file mode 100644 index 15da68f540c..00000000000 --- a/apps/cli/src/lib/utils/onboarding.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { createElement } from "react" - -import { type OnboardingResult, OnboardingProviderChoice } from "@/types/index.js" -import { login } from "@/commands/index.js" -import { saveSettings } from "@/lib/storage/index.js" - -export async function runOnboarding(): Promise { - const { render } = await import("ink") - const { OnboardingScreen } = await import("../../ui/components/onboarding/index.js") - - return new Promise((resolve) => { - const onSelect = async (choice: OnboardingProviderChoice) => { - await saveSettings({ onboardingProviderChoice: choice }) - - app.unmount() - - console.log("") - - if (choice === OnboardingProviderChoice.Roo) { - const result = await login() - await saveSettings({ onboardingProviderChoice: choice }) - - resolve({ - choice: OnboardingProviderChoice.Roo, - token: result.success ? result.token : undefined, - skipped: false, - }) - } else { - console.log("Using your own API key.") - console.log("Set your API key via --api-key or environment variable.") - console.log("") - resolve({ choice: OnboardingProviderChoice.Byok, skipped: false }) - } - } - - const app = render(createElement(OnboardingScreen, { onSelect })) - }) -} diff --git a/apps/cli/src/lib/utils/provider.ts b/apps/cli/src/lib/utils/provider.ts index 64aec430c1b..26beaf90c45 100644 --- a/apps/cli/src/lib/utils/provider.ts +++ b/apps/cli/src/lib/utils/provider.ts @@ -8,7 +8,6 @@ const envVarMap: Record = { gemini: "GOOGLE_API_KEY", openrouter: "OPENROUTER_API_KEY", "vercel-ai-gateway": "VERCEL_AI_GATEWAY_API_KEY", - roo: "ROO_API_KEY", } export function getEnvVarName(provider: SupportedProvider): string { @@ -48,10 +47,6 @@ export function getProviderSettings( if (apiKey) config.vercelAiGatewayApiKey = apiKey if (model) config.vercelAiGatewayModelId = model break - case "roo": - if (apiKey) config.rooApiKey = apiKey - if (model) config.apiModelId = model - break default: if (apiKey) config.apiKey = apiKey if (model) config.apiModelId = model diff --git a/apps/cli/src/types/constants.ts b/apps/cli/src/types/constants.ts index b291b5f90e1..007cfa0783f 100644 --- a/apps/cli/src/types/constants.ts +++ b/apps/cli/src/types/constants.ts @@ -21,7 +21,3 @@ export const ASCII_ROO = ` _,' ___ \\,\\ / \\\\ // \\\\ ,/' \`\\_,` - -export const AUTH_BASE_URL = process.env.ROO_AUTH_BASE_URL ?? "https://app.roocode.com" - -export const SDK_BASE_URL = process.env.ROO_SDK_BASE_URL ?? "https://cloud-api.roocode.com" diff --git a/apps/cli/src/types/types.ts b/apps/cli/src/types/types.ts index ecd3922aa1c..0a9f3d22597 100644 --- a/apps/cli/src/types/types.ts +++ b/apps/cli/src/types/types.ts @@ -7,7 +7,6 @@ export const supportedProviders = [ "gemini", "openrouter", "vercel-ai-gateway", - "roo", ] as const satisfies ProviderName[] export type SupportedProvider = (typeof supportedProviders)[number] @@ -44,7 +43,6 @@ export type FlagOptions = { } export enum OnboardingProviderChoice { - Roo = "roo", Byok = "byok", } diff --git a/apps/cli/src/ui/components/onboarding/OnboardingScreen.tsx b/apps/cli/src/ui/components/onboarding/OnboardingScreen.tsx deleted file mode 100644 index 86c15f5b274..00000000000 --- a/apps/cli/src/ui/components/onboarding/OnboardingScreen.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import { Box, Text } from "ink" -import { Select } from "@inkjs/ui" - -import { OnboardingProviderChoice, ASCII_ROO } from "@/types/index.js" - -export interface OnboardingScreenProps { - onSelect: (choice: OnboardingProviderChoice) => void -} - -export function OnboardingScreen({ onSelect }: OnboardingScreenProps) { - return ( - - - {ASCII_ROO} - - Welcome! How would you like to connect to an LLM provider? - +
+ {searchBarShortcut && + searchBarShortcutHint && + (inputValue !== "" ? ( + + ) : ( + isBrowser && ( +
+ {isMac ? "⌘" : "ctrl"} + K +
+ ) + ))} +
+ ) +} diff --git a/apps/docs/src/theme/SearchPage/index.tsx b/apps/docs/src/theme/SearchPage/index.tsx new file mode 100644 index 00000000000..eed518ff075 --- /dev/null +++ b/apps/docs/src/theme/SearchPage/index.tsx @@ -0,0 +1,268 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +/* Custom SearchPage override that always pushes Release Notes results to the bottom */ +import React, { useCallback, useEffect, useMemo, useState } from "react" +import useDocusaurusContext from "@docusaurus/useDocusaurusContext" +import Layout from "@theme/Layout" +import Head from "@docusaurus/Head" +import Link from "@docusaurus/Link" +import { translate } from "@docusaurus/Translate" +import { usePluralForm } from "@docusaurus/theme-common" +import clsx from "clsx" + +// Plugin internals (mirrors original SearchPage) +import useSearchQuery from "@easyops-cn/docusaurus-search-local/dist/client/client/theme/hooks/useSearchQuery" +import { + fetchIndexesByWorker, + searchByWorker, +} from "@easyops-cn/docusaurus-search-local/dist/client/client/theme/searchByWorker" +import { SearchDocumentType } from "@easyops-cn/docusaurus-search-local/dist/client/shared/interfaces" +import { highlight } from "@easyops-cn/docusaurus-search-local/dist/client/client/utils/highlight" +import { highlightStemmed } from "@easyops-cn/docusaurus-search-local/dist/client/client/utils/highlightStemmed" +import { getStemmedPositions } from "@easyops-cn/docusaurus-search-local/dist/client/client/utils/getStemmedPositions" +import LoadingRing from "@easyops-cn/docusaurus-search-local/dist/client/client/theme/LoadingRing/LoadingRing" +import { concatDocumentPath } from "@easyops-cn/docusaurus-search-local/dist/client/client/utils/concatDocumentPath" +import { + Mark, + searchContextByPaths, + useAllContextsWithNoSearchContext, +} from "@easyops-cn/docusaurus-search-local/dist/client/client/utils/proxiedGenerated" +import styles from "@easyops-cn/docusaurus-search-local/dist/client/client/theme/SearchPage/SearchPage.module.css" +import { normalizeContextByPath } from "@easyops-cn/docusaurus-search-local/dist/client/client/utils/normalizeContextByPath" + +// Ensure release notes always sink to bottom +function deprioritizeReleaseNotes(results: any[]) { + if (!Array.isArray(results)) return results + const isRN = (u: string) => typeof u === "string" && u.includes("/update-notes") + const nonRN: any[] = [] + const rn: any[] = [] + for (const r of results) { + const url = r?.document?.u ?? "" + ;(isRN(url) ? rn : nonRN).push(r) + } + return [...nonRN, ...rn] +} + +export default function SearchPage() { + return ( + + + + ) +} + +function SearchPageContent() { + const { + siteConfig: { baseUrl }, + i18n: { currentLocale }, + } = useDocusaurusContext() + const { selectMessage } = usePluralForm() + const { searchValue, searchContext, searchVersion, updateSearchPath, updateSearchContext } = useSearchQuery() as any + + const [searchQuery, setSearchQuery] = useState(searchValue) + const [searchResults, setSearchResults] = useState() + const versionUrl = `${baseUrl}${searchVersion}` + const pageTitle = useMemo( + () => + searchQuery + ? translate( + { + id: "theme.SearchPage.existingResultsTitle", + message: 'Search results for "{query}"', + description: "The search page title for non-empty query", + }, + { + query: searchQuery, + }, + ) + : translate({ + id: "theme.SearchPage.emptyResultsTitle", + message: "Search the documentation", + description: "The search page title for empty query", + }), + [searchQuery], + ) + + useEffect(() => { + updateSearchPath(searchQuery) + if (searchQuery) { + ;(async () => { + const results = await searchByWorker(versionUrl, searchContext, searchQuery, 100) + setSearchResults(deprioritizeReleaseNotes(results)) + })() + } else { + setSearchResults(undefined) + } + // eslint-disable-next-line react-hooks/exhaustive-deps -- updateSearchPath intentionally omitted (matches plugin behavior) + }, [searchQuery, versionUrl, searchContext]) + + const handleSearchInputChange = useCallback((e: React.ChangeEvent) => { + setSearchQuery(e.target.value) + }, []) + + useEffect(() => { + if (searchValue && searchValue !== searchQuery) { + setSearchQuery(searchValue) + } + // eslint-disable-next-line react-hooks/exhaustive-deps -- searchQuery intentionally omitted to prevent loops + }, [searchValue]) + + const [searchWorkerReady, setSearchWorkerReady] = useState(false) + useEffect(() => { + async function doFetchIndexes() { + if (!Array.isArray(searchContextByPaths) || searchContext || useAllContextsWithNoSearchContext) { + await fetchIndexesByWorker(versionUrl, searchContext) + } + setSearchWorkerReady(true) + } + doFetchIndexes() + }, [searchContext, versionUrl]) + + return ( + + + {/* Do not index search pages */} + + {pageTitle} + + +
+

{pageTitle}

+ +
+
+ +
+ {Array.isArray(searchContextByPaths) ? ( +
+ +
+ ) : null} +
+ + {!searchWorkerReady && searchQuery && ( +
+ +
+ )} + + {searchResults && + (searchResults.length > 0 ? ( +

+ {selectMessage( + searchResults.length, + translate( + { + id: "theme.SearchPage.documentsFound.plurals", + message: "1 document found|{count} documents found", + description: + 'Pluralized label for "{count} documents found". See https://www.unicode.org/cldr/cldr-aux/charts/34/supplemental/language_plural_rules.html', + }, + { count: searchResults.length }, + ), + )} +

+ ) : process.env.NODE_ENV === "production" ? ( +

+ {translate({ + id: "theme.SearchPage.noResultsText", + message: "No documents were found", + description: "The paragraph for empty search result", + })} +

+ ) : ( +

⚠️ The search index is only available when you run docusaurus build!

+ ))} + +
+ {searchResults?.map((item) => )} +
+
+
+ ) +} + +function SearchResultItem({ searchResult: { document, type, page, tokens, metadata } }: { searchResult: any }) { + const isTitle = type === SearchDocumentType.Title + const isKeywords = type === SearchDocumentType.Keywords + const isDescription = type === SearchDocumentType.Description + const isDescriptionOrKeywords = isDescription || isKeywords + const isTitleRelated = isTitle || isDescriptionOrKeywords + const isContent = type === SearchDocumentType.Content + + const pathItems = (isTitle ? document.b : page.b).slice() + const articleTitle = isContent || isDescriptionOrKeywords ? document.s : document.t + if (!isTitleRelated) { + pathItems.push(page.t) + } + + let search = "" + if (Mark && tokens.length > 0) { + const params = new window.URLSearchParams() + for (const token of tokens) { + params.append("_highlight", token) + } + search = `?${params.toString()}` + } + + return ( +
+

+ +

+ + {pathItems.length > 0 &&

{concatDocumentPath(pathItems)}

} + + {(isContent || isDescription) && ( +

+ )} +

+ ) +} diff --git a/apps/docs/src/types/react-inert.d.ts b/apps/docs/src/types/react-inert.d.ts new file mode 100644 index 00000000000..1054211da38 --- /dev/null +++ b/apps/docs/src/types/react-inert.d.ts @@ -0,0 +1,17 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ + +import "react" + +declare module "react" { + interface HTMLAttributes { + inert?: boolean + } +} + +declare global { + namespace React { + interface HTMLAttributes { + inert?: boolean + } + } +} diff --git a/apps/docs/static/downloads/boomerang-tasks/roomodes.json b/apps/docs/static/downloads/boomerang-tasks/roomodes.json new file mode 100644 index 00000000000..c4aa8f39b28 --- /dev/null +++ b/apps/docs/static/downloads/boomerang-tasks/roomodes.json @@ -0,0 +1,12 @@ +{ + "customModes": [ + { + "slug": "boomerang-mode", + "name": "Boomerang Mode", + "roleDefinition": "You are Roo, a strategic workflow orchestrator who coordinates complex tasks by delegating them to appropriate specialized modes. You have a comprehensive understanding of each mode's capabilities and limitations, allowing you to effectively break down complex problems into discrete tasks that can be solved by different specialists.", + "customInstructions": "Your role is to coordinate complex workflows by delegating tasks to specialized modes. As an orchestrator, you should:\n\n1. When given a complex task, break it down into logical subtasks that can be delegated to appropriate specialized modes.\n\n2. For each subtask, use the `new_task` tool to delegate. Choose the most appropriate mode for the subtask's specific goal and provide comprehensive instructions in the `message` parameter. These instructions must include:\n * All necessary context from the parent task or previous subtasks required to complete the work.\n * A clearly defined scope, specifying exactly what the subtask should accomplish.\n * An explicit statement that the subtask should *only* perform the work outlined in these instructions and not deviate.\n * An instruction for the subtask to signal completion by using the `attempt_completion` tool, providing a concise yet thorough summary of the outcome in the `result` parameter, keeping in mind that this summary will be the source of truth used to keep track of what was completed on this project. \n * A statement that these specific instructions supersede any conflicting general instructions the subtask's mode might have.\n\n3. Track and manage the progress of all subtasks. When a subtask is completed, analyze its results and determine the next steps.\n\n4. Help the user understand how the different subtasks fit together in the overall workflow. Provide clear reasoning about why you're delegating specific tasks to specific modes.\n\n5. When all subtasks are completed, synthesize the results and provide a comprehensive overview of what was accomplished.\n\n6. Ask clarifying questions when necessary to better understand how to break down complex tasks effectively.\n\n7. Suggest improvements to the workflow based on the results of completed subtasks.\n\nUse subtasks to maintain clarity. If a request significantly shifts focus or requires a different expertise (mode), consider creating a subtask rather than overloading the current one.", + "groups": [], + "source": "global" + } + ] +} diff --git a/apps/docs/static/img/api-configuration-profiles/api-configuration-profiles-1.png b/apps/docs/static/img/api-configuration-profiles/api-configuration-profiles-1.png new file mode 100644 index 00000000000..4896bbf8909 Binary files /dev/null and b/apps/docs/static/img/api-configuration-profiles/api-configuration-profiles-1.png differ diff --git a/apps/docs/static/img/api-configuration-profiles/api-configuration-profiles-10.png b/apps/docs/static/img/api-configuration-profiles/api-configuration-profiles-10.png new file mode 100644 index 00000000000..57d8f98d16a Binary files /dev/null and b/apps/docs/static/img/api-configuration-profiles/api-configuration-profiles-10.png differ diff --git a/apps/docs/static/img/api-configuration-profiles/api-configuration-profiles-11.png b/apps/docs/static/img/api-configuration-profiles/api-configuration-profiles-11.png new file mode 100644 index 00000000000..246e2dc1902 Binary files /dev/null and b/apps/docs/static/img/api-configuration-profiles/api-configuration-profiles-11.png differ diff --git a/apps/docs/static/img/api-configuration-profiles/api-configuration-profiles-12.png b/apps/docs/static/img/api-configuration-profiles/api-configuration-profiles-12.png new file mode 100644 index 00000000000..c5ef1e97607 Binary files /dev/null and b/apps/docs/static/img/api-configuration-profiles/api-configuration-profiles-12.png differ diff --git a/apps/docs/static/img/api-configuration-profiles/api-configuration-profiles-2.png b/apps/docs/static/img/api-configuration-profiles/api-configuration-profiles-2.png new file mode 100644 index 00000000000..b3183e7bed8 Binary files /dev/null and b/apps/docs/static/img/api-configuration-profiles/api-configuration-profiles-2.png differ diff --git a/apps/docs/static/img/api-configuration-profiles/api-configuration-profiles-3.png b/apps/docs/static/img/api-configuration-profiles/api-configuration-profiles-3.png new file mode 100644 index 00000000000..50acf92cdc3 Binary files /dev/null and b/apps/docs/static/img/api-configuration-profiles/api-configuration-profiles-3.png differ diff --git a/apps/docs/static/img/api-configuration-profiles/api-configuration-profiles-4.png b/apps/docs/static/img/api-configuration-profiles/api-configuration-profiles-4.png new file mode 100644 index 00000000000..a699ad914b7 Binary files /dev/null and b/apps/docs/static/img/api-configuration-profiles/api-configuration-profiles-4.png differ diff --git a/apps/docs/static/img/api-configuration-profiles/api-configuration-profiles-5.png b/apps/docs/static/img/api-configuration-profiles/api-configuration-profiles-5.png new file mode 100644 index 00000000000..c55abe2094f Binary files /dev/null and b/apps/docs/static/img/api-configuration-profiles/api-configuration-profiles-5.png differ diff --git a/apps/docs/static/img/api-configuration-profiles/api-configuration-profiles-6.png b/apps/docs/static/img/api-configuration-profiles/api-configuration-profiles-6.png new file mode 100644 index 00000000000..188947a5ea1 Binary files /dev/null and b/apps/docs/static/img/api-configuration-profiles/api-configuration-profiles-6.png differ diff --git a/apps/docs/static/img/api-configuration-profiles/api-configuration-profiles-7.png b/apps/docs/static/img/api-configuration-profiles/api-configuration-profiles-7.png new file mode 100644 index 00000000000..0ad8b083c4e Binary files /dev/null and b/apps/docs/static/img/api-configuration-profiles/api-configuration-profiles-7.png differ diff --git a/apps/docs/static/img/api-configuration-profiles/api-configuration-profiles-8.png b/apps/docs/static/img/api-configuration-profiles/api-configuration-profiles-8.png new file mode 100644 index 00000000000..a93f7db6ec9 Binary files /dev/null and b/apps/docs/static/img/api-configuration-profiles/api-configuration-profiles-8.png differ diff --git a/apps/docs/static/img/api-configuration-profiles/api-configuration-profiles-9.png b/apps/docs/static/img/api-configuration-profiles/api-configuration-profiles-9.png new file mode 100644 index 00000000000..110e26978e2 Binary files /dev/null and b/apps/docs/static/img/api-configuration-profiles/api-configuration-profiles-9.png differ diff --git a/apps/docs/static/img/api-configuration-profiles/api-configuration-profiles.png b/apps/docs/static/img/api-configuration-profiles/api-configuration-profiles.png new file mode 100644 index 00000000000..5afdef80256 Binary files /dev/null and b/apps/docs/static/img/api-configuration-profiles/api-configuration-profiles.png differ diff --git a/apps/docs/static/img/auto-approving-actions/auto-approving-actions-1.png b/apps/docs/static/img/auto-approving-actions/auto-approving-actions-1.png new file mode 100644 index 00000000000..a10c0fce509 Binary files /dev/null and b/apps/docs/static/img/auto-approving-actions/auto-approving-actions-1.png differ diff --git a/apps/docs/static/img/auto-approving-actions/auto-approving-actions-10.png b/apps/docs/static/img/auto-approving-actions/auto-approving-actions-10.png new file mode 100644 index 00000000000..75d8b2834e1 Binary files /dev/null and b/apps/docs/static/img/auto-approving-actions/auto-approving-actions-10.png differ diff --git a/apps/docs/static/img/auto-approving-actions/auto-approving-actions-11.png b/apps/docs/static/img/auto-approving-actions/auto-approving-actions-11.png new file mode 100644 index 00000000000..0b4e3ad7315 Binary files /dev/null and b/apps/docs/static/img/auto-approving-actions/auto-approving-actions-11.png differ diff --git a/apps/docs/static/img/auto-approving-actions/auto-approving-actions-12.png b/apps/docs/static/img/auto-approving-actions/auto-approving-actions-12.png new file mode 100644 index 00000000000..b00c8b442cb Binary files /dev/null and b/apps/docs/static/img/auto-approving-actions/auto-approving-actions-12.png differ diff --git a/apps/docs/static/img/auto-approving-actions/auto-approving-actions-13.png b/apps/docs/static/img/auto-approving-actions/auto-approving-actions-13.png new file mode 100644 index 00000000000..d7afb227ad2 Binary files /dev/null and b/apps/docs/static/img/auto-approving-actions/auto-approving-actions-13.png differ diff --git a/apps/docs/static/img/auto-approving-actions/auto-approving-actions-14.png b/apps/docs/static/img/auto-approving-actions/auto-approving-actions-14.png new file mode 100644 index 00000000000..06598699385 Binary files /dev/null and b/apps/docs/static/img/auto-approving-actions/auto-approving-actions-14.png differ diff --git a/apps/docs/static/img/auto-approving-actions/auto-approving-actions-15.png b/apps/docs/static/img/auto-approving-actions/auto-approving-actions-15.png new file mode 100644 index 00000000000..1daca635bb7 Binary files /dev/null and b/apps/docs/static/img/auto-approving-actions/auto-approving-actions-15.png differ diff --git a/apps/docs/static/img/auto-approving-actions/auto-approving-actions-16.png b/apps/docs/static/img/auto-approving-actions/auto-approving-actions-16.png new file mode 100644 index 00000000000..021888ca6ad Binary files /dev/null and b/apps/docs/static/img/auto-approving-actions/auto-approving-actions-16.png differ diff --git a/apps/docs/static/img/auto-approving-actions/auto-approving-actions-18.png b/apps/docs/static/img/auto-approving-actions/auto-approving-actions-18.png new file mode 100644 index 00000000000..5166138cda1 Binary files /dev/null and b/apps/docs/static/img/auto-approving-actions/auto-approving-actions-18.png differ diff --git a/apps/docs/static/img/auto-approving-actions/auto-approving-actions-19.png b/apps/docs/static/img/auto-approving-actions/auto-approving-actions-19.png new file mode 100644 index 00000000000..096f37a7d50 Binary files /dev/null and b/apps/docs/static/img/auto-approving-actions/auto-approving-actions-19.png differ diff --git a/apps/docs/static/img/auto-approving-actions/auto-approving-actions-2.png b/apps/docs/static/img/auto-approving-actions/auto-approving-actions-2.png new file mode 100644 index 00000000000..7b0661762cb Binary files /dev/null and b/apps/docs/static/img/auto-approving-actions/auto-approving-actions-2.png differ diff --git a/apps/docs/static/img/auto-approving-actions/auto-approving-actions-20.png b/apps/docs/static/img/auto-approving-actions/auto-approving-actions-20.png new file mode 100644 index 00000000000..59d3b311f8f Binary files /dev/null and b/apps/docs/static/img/auto-approving-actions/auto-approving-actions-20.png differ diff --git a/apps/docs/static/img/auto-approving-actions/auto-approving-actions-21.png b/apps/docs/static/img/auto-approving-actions/auto-approving-actions-21.png new file mode 100644 index 00000000000..c2c02e65e3f Binary files /dev/null and b/apps/docs/static/img/auto-approving-actions/auto-approving-actions-21.png differ diff --git a/apps/docs/static/img/auto-approving-actions/auto-approving-actions-3.png b/apps/docs/static/img/auto-approving-actions/auto-approving-actions-3.png new file mode 100644 index 00000000000..00a9b9d8f3c Binary files /dev/null and b/apps/docs/static/img/auto-approving-actions/auto-approving-actions-3.png differ diff --git a/apps/docs/static/img/auto-approving-actions/auto-approving-actions-4.png b/apps/docs/static/img/auto-approving-actions/auto-approving-actions-4.png new file mode 100644 index 00000000000..795e6d4ec85 Binary files /dev/null and b/apps/docs/static/img/auto-approving-actions/auto-approving-actions-4.png differ diff --git a/apps/docs/static/img/auto-approving-actions/auto-approving-actions-5.png b/apps/docs/static/img/auto-approving-actions/auto-approving-actions-5.png new file mode 100644 index 00000000000..67a78e503ed Binary files /dev/null and b/apps/docs/static/img/auto-approving-actions/auto-approving-actions-5.png differ diff --git a/apps/docs/static/img/auto-approving-actions/auto-approving-actions-6.png b/apps/docs/static/img/auto-approving-actions/auto-approving-actions-6.png new file mode 100644 index 00000000000..428b3963671 Binary files /dev/null and b/apps/docs/static/img/auto-approving-actions/auto-approving-actions-6.png differ diff --git a/apps/docs/static/img/auto-approving-actions/auto-approving-actions-7.png b/apps/docs/static/img/auto-approving-actions/auto-approving-actions-7.png new file mode 100644 index 00000000000..b99d3a12181 Binary files /dev/null and b/apps/docs/static/img/auto-approving-actions/auto-approving-actions-7.png differ diff --git a/apps/docs/static/img/auto-approving-actions/auto-approving-actions-8.png b/apps/docs/static/img/auto-approving-actions/auto-approving-actions-8.png new file mode 100644 index 00000000000..d495e474ce9 Binary files /dev/null and b/apps/docs/static/img/auto-approving-actions/auto-approving-actions-8.png differ diff --git a/apps/docs/static/img/auto-approving-actions/auto-approving-actions-9.png b/apps/docs/static/img/auto-approving-actions/auto-approving-actions-9.png new file mode 100644 index 00000000000..f5650cff7a2 Binary files /dev/null and b/apps/docs/static/img/auto-approving-actions/auto-approving-actions-9.png differ diff --git a/apps/docs/static/img/auto-approving-actions/auto-approving-actions.png b/apps/docs/static/img/auto-approving-actions/auto-approving-actions.png new file mode 100644 index 00000000000..ea4a7a426a0 Binary files /dev/null and b/apps/docs/static/img/auto-approving-actions/auto-approving-actions.png differ diff --git a/apps/docs/static/img/available-tools/available-tools.png b/apps/docs/static/img/available-tools/available-tools.png new file mode 100644 index 00000000000..ef90e554a8a Binary files /dev/null and b/apps/docs/static/img/available-tools/available-tools.png differ diff --git a/apps/docs/static/img/background-editing/background-editing.png b/apps/docs/static/img/background-editing/background-editing.png new file mode 100644 index 00000000000..47cd38d7236 Binary files /dev/null and b/apps/docs/static/img/background-editing/background-editing.png differ diff --git a/apps/docs/static/img/browser-use/browser-use-1.png b/apps/docs/static/img/browser-use/browser-use-1.png new file mode 100644 index 00000000000..0bd708fa12e Binary files /dev/null and b/apps/docs/static/img/browser-use/browser-use-1.png differ diff --git a/apps/docs/static/img/browser-use/browser-use-2.png b/apps/docs/static/img/browser-use/browser-use-2.png new file mode 100644 index 00000000000..9c3971bf6ca Binary files /dev/null and b/apps/docs/static/img/browser-use/browser-use-2.png differ diff --git a/apps/docs/static/img/browser-use/browser-use-3.png b/apps/docs/static/img/browser-use/browser-use-3.png new file mode 100644 index 00000000000..fca7d812d5d Binary files /dev/null and b/apps/docs/static/img/browser-use/browser-use-3.png differ diff --git a/apps/docs/static/img/browser-use/browser-use-4.png b/apps/docs/static/img/browser-use/browser-use-4.png new file mode 100644 index 00000000000..4fd485168c0 Binary files /dev/null and b/apps/docs/static/img/browser-use/browser-use-4.png differ diff --git a/apps/docs/static/img/browser-use/browser-use-5.png b/apps/docs/static/img/browser-use/browser-use-5.png new file mode 100644 index 00000000000..2a412168034 Binary files /dev/null and b/apps/docs/static/img/browser-use/browser-use-5.png differ diff --git a/apps/docs/static/img/browser-use/browser-use.png b/apps/docs/static/img/browser-use/browser-use.png new file mode 100644 index 00000000000..2861a97a043 Binary files /dev/null and b/apps/docs/static/img/browser-use/browser-use.png differ diff --git a/apps/docs/static/img/checkpoints/checkpoints-1.png b/apps/docs/static/img/checkpoints/checkpoints-1.png new file mode 100644 index 00000000000..4acd43178e5 Binary files /dev/null and b/apps/docs/static/img/checkpoints/checkpoints-1.png differ diff --git a/apps/docs/static/img/checkpoints/checkpoints-2.png b/apps/docs/static/img/checkpoints/checkpoints-2.png new file mode 100644 index 00000000000..63719a10f6c Binary files /dev/null and b/apps/docs/static/img/checkpoints/checkpoints-2.png differ diff --git a/apps/docs/static/img/checkpoints/checkpoints-3.png b/apps/docs/static/img/checkpoints/checkpoints-3.png new file mode 100644 index 00000000000..e212b6190ab Binary files /dev/null and b/apps/docs/static/img/checkpoints/checkpoints-3.png differ diff --git a/apps/docs/static/img/checkpoints/checkpoints-4.png b/apps/docs/static/img/checkpoints/checkpoints-4.png new file mode 100644 index 00000000000..7c4fc3e78b8 Binary files /dev/null and b/apps/docs/static/img/checkpoints/checkpoints-4.png differ diff --git a/apps/docs/static/img/checkpoints/checkpoints-6.png b/apps/docs/static/img/checkpoints/checkpoints-6.png new file mode 100644 index 00000000000..3dd7ccb9c7f Binary files /dev/null and b/apps/docs/static/img/checkpoints/checkpoints-6.png differ diff --git a/apps/docs/static/img/checkpoints/checkpoints-7.png b/apps/docs/static/img/checkpoints/checkpoints-7.png new file mode 100644 index 00000000000..e9dc5ac45a0 Binary files /dev/null and b/apps/docs/static/img/checkpoints/checkpoints-7.png differ diff --git a/apps/docs/static/img/checkpoints/checkpoints-9.png b/apps/docs/static/img/checkpoints/checkpoints-9.png new file mode 100644 index 00000000000..b53afd33751 Binary files /dev/null and b/apps/docs/static/img/checkpoints/checkpoints-9.png differ diff --git a/apps/docs/static/img/checkpoints/checkpoints.png b/apps/docs/static/img/checkpoints/checkpoints.png new file mode 100644 index 00000000000..c9b7d65e43b Binary files /dev/null and b/apps/docs/static/img/checkpoints/checkpoints.png differ diff --git a/apps/docs/static/img/codebase-indexing/codebase-indexing-1.png b/apps/docs/static/img/codebase-indexing/codebase-indexing-1.png new file mode 100644 index 00000000000..1b2fa6478ed Binary files /dev/null and b/apps/docs/static/img/codebase-indexing/codebase-indexing-1.png differ diff --git a/apps/docs/static/img/codebase-indexing/codebase-indexing-2.png b/apps/docs/static/img/codebase-indexing/codebase-indexing-2.png new file mode 100644 index 00000000000..ba40a69ef04 Binary files /dev/null and b/apps/docs/static/img/codebase-indexing/codebase-indexing-2.png differ diff --git a/apps/docs/static/img/codebase-indexing/codebase-indexing-3.png b/apps/docs/static/img/codebase-indexing/codebase-indexing-3.png new file mode 100644 index 00000000000..85393915e66 Binary files /dev/null and b/apps/docs/static/img/codebase-indexing/codebase-indexing-3.png differ diff --git a/apps/docs/static/img/codebase-indexing/codebase-indexing.png b/apps/docs/static/img/codebase-indexing/codebase-indexing.png new file mode 100644 index 00000000000..9b18c982d89 Binary files /dev/null and b/apps/docs/static/img/codebase-indexing/codebase-indexing.png differ diff --git a/apps/docs/static/img/concurrent-file-edits/concurrent-file-edits-1.png b/apps/docs/static/img/concurrent-file-edits/concurrent-file-edits-1.png new file mode 100644 index 00000000000..46402323d20 Binary files /dev/null and b/apps/docs/static/img/concurrent-file-edits/concurrent-file-edits-1.png differ diff --git a/apps/docs/static/img/concurrent-file-edits/concurrent-file-edits.png b/apps/docs/static/img/concurrent-file-edits/concurrent-file-edits.png new file mode 100644 index 00000000000..59c7a4dffba Binary files /dev/null and b/apps/docs/static/img/concurrent-file-edits/concurrent-file-edits.png differ diff --git a/apps/docs/static/img/concurrent-file-reads/concurrent-file-reads-1.png b/apps/docs/static/img/concurrent-file-reads/concurrent-file-reads-1.png new file mode 100644 index 00000000000..4d0fbdfff46 Binary files /dev/null and b/apps/docs/static/img/concurrent-file-reads/concurrent-file-reads-1.png differ diff --git a/apps/docs/static/img/concurrent-file-reads/concurrent-file-reads-2.png b/apps/docs/static/img/concurrent-file-reads/concurrent-file-reads-2.png new file mode 100644 index 00000000000..3fcb07530e2 Binary files /dev/null and b/apps/docs/static/img/concurrent-file-reads/concurrent-file-reads-2.png differ diff --git a/apps/docs/static/img/concurrent-file-reads/concurrent-file-reads.png b/apps/docs/static/img/concurrent-file-reads/concurrent-file-reads.png new file mode 100644 index 00000000000..19b08dd514d Binary files /dev/null and b/apps/docs/static/img/concurrent-file-reads/concurrent-file-reads.png differ diff --git a/apps/docs/static/img/connecting-api-provider/connecting-api-provider-4.png b/apps/docs/static/img/connecting-api-provider/connecting-api-provider-4.png new file mode 100644 index 00000000000..a60622d16de Binary files /dev/null and b/apps/docs/static/img/connecting-api-provider/connecting-api-provider-4.png differ diff --git a/apps/docs/static/img/connecting-api-provider/connecting-api-provider-5.png b/apps/docs/static/img/connecting-api-provider/connecting-api-provider-5.png new file mode 100644 index 00000000000..09f170a8626 Binary files /dev/null and b/apps/docs/static/img/connecting-api-provider/connecting-api-provider-5.png differ diff --git a/apps/docs/static/img/connecting-api-provider/connecting-api-provider-6.png b/apps/docs/static/img/connecting-api-provider/connecting-api-provider-6.png new file mode 100644 index 00000000000..8ac7e530323 Binary files /dev/null and b/apps/docs/static/img/connecting-api-provider/connecting-api-provider-6.png differ diff --git a/apps/docs/static/img/connecting-api-provider/connecting-api-provider-7.png b/apps/docs/static/img/connecting-api-provider/connecting-api-provider-7.png new file mode 100644 index 00000000000..62fdfbb8498 Binary files /dev/null and b/apps/docs/static/img/connecting-api-provider/connecting-api-provider-7.png differ diff --git a/apps/docs/static/img/context-mentions/context-mentions-1.png b/apps/docs/static/img/context-mentions/context-mentions-1.png new file mode 100644 index 00000000000..9f8bdc4beb3 Binary files /dev/null and b/apps/docs/static/img/context-mentions/context-mentions-1.png differ diff --git a/apps/docs/static/img/context-mentions/context-mentions-2.png b/apps/docs/static/img/context-mentions/context-mentions-2.png new file mode 100644 index 00000000000..90ecbda70b0 Binary files /dev/null and b/apps/docs/static/img/context-mentions/context-mentions-2.png differ diff --git a/apps/docs/static/img/context-mentions/context-mentions-3.png b/apps/docs/static/img/context-mentions/context-mentions-3.png new file mode 100644 index 00000000000..20b2bfea461 Binary files /dev/null and b/apps/docs/static/img/context-mentions/context-mentions-3.png differ diff --git a/apps/docs/static/img/context-mentions/context-mentions-4.png b/apps/docs/static/img/context-mentions/context-mentions-4.png new file mode 100644 index 00000000000..321c750105f Binary files /dev/null and b/apps/docs/static/img/context-mentions/context-mentions-4.png differ diff --git a/apps/docs/static/img/context-mentions/context-mentions-5.png b/apps/docs/static/img/context-mentions/context-mentions-5.png new file mode 100644 index 00000000000..bd0367d149f Binary files /dev/null and b/apps/docs/static/img/context-mentions/context-mentions-5.png differ diff --git a/apps/docs/static/img/context-mentions/context-mentions-6.png b/apps/docs/static/img/context-mentions/context-mentions-6.png new file mode 100644 index 00000000000..e933613b000 Binary files /dev/null and b/apps/docs/static/img/context-mentions/context-mentions-6.png differ diff --git a/apps/docs/static/img/context-mentions/context-mentions.png b/apps/docs/static/img/context-mentions/context-mentions.png new file mode 100644 index 00000000000..30afb6ccb83 Binary files /dev/null and b/apps/docs/static/img/context-mentions/context-mentions.png differ diff --git a/apps/docs/static/img/custom-instructions/custom-instructions-2.png b/apps/docs/static/img/custom-instructions/custom-instructions-2.png new file mode 100644 index 00000000000..6c92f3dedd1 Binary files /dev/null and b/apps/docs/static/img/custom-instructions/custom-instructions-2.png differ diff --git a/apps/docs/static/img/custom-instructions/custom-instructions.png b/apps/docs/static/img/custom-instructions/custom-instructions.png new file mode 100644 index 00000000000..16feb1c328f Binary files /dev/null and b/apps/docs/static/img/custom-instructions/custom-instructions.png differ diff --git a/apps/docs/static/img/custom-modes/custom-modes-1.png b/apps/docs/static/img/custom-modes/custom-modes-1.png new file mode 100644 index 00000000000..c9ad7a989a4 Binary files /dev/null and b/apps/docs/static/img/custom-modes/custom-modes-1.png differ diff --git a/apps/docs/static/img/custom-modes/custom-modes-2.png b/apps/docs/static/img/custom-modes/custom-modes-2.png new file mode 100644 index 00000000000..8563536accd Binary files /dev/null and b/apps/docs/static/img/custom-modes/custom-modes-2.png differ diff --git a/apps/docs/static/img/custom-modes/custom-modes-3.png b/apps/docs/static/img/custom-modes/custom-modes-3.png new file mode 100644 index 00000000000..03f73061602 Binary files /dev/null and b/apps/docs/static/img/custom-modes/custom-modes-3.png differ diff --git a/apps/docs/static/img/custom-modes/custom-modes-4.png b/apps/docs/static/img/custom-modes/custom-modes-4.png new file mode 100644 index 00000000000..b274e59d7a2 Binary files /dev/null and b/apps/docs/static/img/custom-modes/custom-modes-4.png differ diff --git a/apps/docs/static/img/custom-modes/custom-modes-5.png b/apps/docs/static/img/custom-modes/custom-modes-5.png new file mode 100644 index 00000000000..fcdb067b4fc Binary files /dev/null and b/apps/docs/static/img/custom-modes/custom-modes-5.png differ diff --git a/apps/docs/static/img/custom-modes/custom-modes-6.png b/apps/docs/static/img/custom-modes/custom-modes-6.png new file mode 100644 index 00000000000..69b4221c21b Binary files /dev/null and b/apps/docs/static/img/custom-modes/custom-modes-6.png differ diff --git a/apps/docs/static/img/custom-modes/custom-modes.png b/apps/docs/static/img/custom-modes/custom-modes.png new file mode 100644 index 00000000000..18ba84c356c Binary files /dev/null and b/apps/docs/static/img/custom-modes/custom-modes.png differ diff --git a/apps/docs/static/img/custom-tools/custom-tools.png b/apps/docs/static/img/custom-tools/custom-tools.png new file mode 100644 index 00000000000..d696c64b1b9 Binary files /dev/null and b/apps/docs/static/img/custom-tools/custom-tools.png differ diff --git a/apps/docs/static/img/experimental-features/experimental-features.png b/apps/docs/static/img/experimental-features/experimental-features.png new file mode 100644 index 00000000000..d6bf53d74ce Binary files /dev/null and b/apps/docs/static/img/experimental-features/experimental-features.png differ diff --git a/apps/docs/static/img/fast-edits/fast-edits-1.png b/apps/docs/static/img/fast-edits/fast-edits-1.png new file mode 100644 index 00000000000..dc98d356779 Binary files /dev/null and b/apps/docs/static/img/fast-edits/fast-edits-1.png differ diff --git a/apps/docs/static/img/fast-edits/fast-edits-2.png b/apps/docs/static/img/fast-edits/fast-edits-2.png new file mode 100644 index 00000000000..ec569038d99 Binary files /dev/null and b/apps/docs/static/img/fast-edits/fast-edits-2.png differ diff --git a/apps/docs/static/img/fast-edits/fast-edits-3.png b/apps/docs/static/img/fast-edits/fast-edits-3.png new file mode 100644 index 00000000000..ae5842a0162 Binary files /dev/null and b/apps/docs/static/img/fast-edits/fast-edits-3.png differ diff --git a/apps/docs/static/img/fast-edits/fast-edits-4.png b/apps/docs/static/img/fast-edits/fast-edits-4.png new file mode 100644 index 00000000000..b6b26784f36 Binary files /dev/null and b/apps/docs/static/img/fast-edits/fast-edits-4.png differ diff --git a/apps/docs/static/img/fast-edits/fast-edits-5.png b/apps/docs/static/img/fast-edits/fast-edits-5.png new file mode 100644 index 00000000000..c465fd6dbce Binary files /dev/null and b/apps/docs/static/img/fast-edits/fast-edits-5.png differ diff --git a/apps/docs/static/img/fast-edits/fast-edits.png b/apps/docs/static/img/fast-edits/fast-edits.png new file mode 100644 index 00000000000..036beb4d3bb Binary files /dev/null and b/apps/docs/static/img/fast-edits/fast-edits.png differ diff --git a/apps/web-roo-code/public/favicon.ico b/apps/docs/static/img/favicon.ico similarity index 100% rename from apps/web-roo-code/public/favicon.ico rename to apps/docs/static/img/favicon.ico diff --git a/apps/docs/static/img/footgun-prompting/footgun-prompting-1.png b/apps/docs/static/img/footgun-prompting/footgun-prompting-1.png new file mode 100644 index 00000000000..359c9b487d6 Binary files /dev/null and b/apps/docs/static/img/footgun-prompting/footgun-prompting-1.png differ diff --git a/apps/docs/static/img/footgun-prompting/footgun-prompting.png b/apps/docs/static/img/footgun-prompting/footgun-prompting.png new file mode 100644 index 00000000000..0f19fa5c268 Binary files /dev/null and b/apps/docs/static/img/footgun-prompting/footgun-prompting.png differ diff --git a/apps/docs/static/img/how-tools-work/how-tools-work.png b/apps/docs/static/img/how-tools-work/how-tools-work.png new file mode 100644 index 00000000000..4227e61da09 Binary files /dev/null and b/apps/docs/static/img/how-tools-work/how-tools-work.png differ diff --git a/apps/docs/static/img/installing/installing-1.png b/apps/docs/static/img/installing/installing-1.png new file mode 100644 index 00000000000..d2baafe79ff Binary files /dev/null and b/apps/docs/static/img/installing/installing-1.png differ diff --git a/apps/docs/static/img/installing/installing-2.png b/apps/docs/static/img/installing/installing-2.png new file mode 100644 index 00000000000..47a2e7f1bbc Binary files /dev/null and b/apps/docs/static/img/installing/installing-2.png differ diff --git a/apps/docs/static/img/installing/installing-3.png b/apps/docs/static/img/installing/installing-3.png new file mode 100644 index 00000000000..679fe7c1379 Binary files /dev/null and b/apps/docs/static/img/installing/installing-3.png differ diff --git a/apps/docs/static/img/installing/installing-4.png b/apps/docs/static/img/installing/installing-4.png new file mode 100644 index 00000000000..f2870524db7 Binary files /dev/null and b/apps/docs/static/img/installing/installing-4.png differ diff --git a/apps/docs/static/img/installing/installing-5.png b/apps/docs/static/img/installing/installing-5.png new file mode 100644 index 00000000000..237e8392af7 Binary files /dev/null and b/apps/docs/static/img/installing/installing-5.png differ diff --git a/apps/docs/static/img/installing/installing.png b/apps/docs/static/img/installing/installing.png new file mode 100644 index 00000000000..5e604e16795 Binary files /dev/null and b/apps/docs/static/img/installing/installing.png differ diff --git a/apps/docs/static/img/intelligent-context-condensation/intelligent-context-condensation-1.png b/apps/docs/static/img/intelligent-context-condensation/intelligent-context-condensation-1.png new file mode 100644 index 00000000000..a03f4cd2d64 Binary files /dev/null and b/apps/docs/static/img/intelligent-context-condensation/intelligent-context-condensation-1.png differ diff --git a/apps/docs/static/img/intelligent-context-condensation/intelligent-context-condensation-2.png b/apps/docs/static/img/intelligent-context-condensation/intelligent-context-condensation-2.png new file mode 100644 index 00000000000..b13d7f5d7b1 Binary files /dev/null and b/apps/docs/static/img/intelligent-context-condensation/intelligent-context-condensation-2.png differ diff --git a/apps/docs/static/img/intelligent-context-condensation/intelligent-context-condensation-3.png b/apps/docs/static/img/intelligent-context-condensation/intelligent-context-condensation-3.png new file mode 100644 index 00000000000..01a88ec391a Binary files /dev/null and b/apps/docs/static/img/intelligent-context-condensation/intelligent-context-condensation-3.png differ diff --git a/apps/docs/static/img/intelligent-context-condensation/intelligent-context-condensation-4.png b/apps/docs/static/img/intelligent-context-condensation/intelligent-context-condensation-4.png new file mode 100644 index 00000000000..47df6416a6a Binary files /dev/null and b/apps/docs/static/img/intelligent-context-condensation/intelligent-context-condensation-4.png differ diff --git a/apps/docs/static/img/intelligent-context-condensation/intelligent-context-condensation.png b/apps/docs/static/img/intelligent-context-condensation/intelligent-context-condensation.png new file mode 100644 index 00000000000..f7213e8733e Binary files /dev/null and b/apps/docs/static/img/intelligent-context-condensation/intelligent-context-condensation.png differ diff --git a/apps/docs/static/img/intelligent-context-condensing/intelligent-context-condensing-1.png b/apps/docs/static/img/intelligent-context-condensing/intelligent-context-condensing-1.png new file mode 100644 index 00000000000..40a40aebcb0 Binary files /dev/null and b/apps/docs/static/img/intelligent-context-condensing/intelligent-context-condensing-1.png differ diff --git a/apps/docs/static/img/intelligent-context-condensing/intelligent-context-condensing-2.png b/apps/docs/static/img/intelligent-context-condensing/intelligent-context-condensing-2.png new file mode 100644 index 00000000000..8fee828d8cf Binary files /dev/null and b/apps/docs/static/img/intelligent-context-condensing/intelligent-context-condensing-2.png differ diff --git a/apps/docs/static/img/intelligent-context-condensing/intelligent-context-condensing.png b/apps/docs/static/img/intelligent-context-condensing/intelligent-context-condensing.png new file mode 100644 index 00000000000..294730821e7 Binary files /dev/null and b/apps/docs/static/img/intelligent-context-condensing/intelligent-context-condensing.png differ diff --git a/apps/docs/static/img/litellm/litellm.png b/apps/docs/static/img/litellm/litellm.png new file mode 100644 index 00000000000..3605db1f620 Binary files /dev/null and b/apps/docs/static/img/litellm/litellm.png differ diff --git a/apps/docs/static/img/marketplace/marketplace-1.png b/apps/docs/static/img/marketplace/marketplace-1.png new file mode 100644 index 00000000000..6c7e0095b84 Binary files /dev/null and b/apps/docs/static/img/marketplace/marketplace-1.png differ diff --git a/apps/docs/static/img/marketplace/marketplace-2.png b/apps/docs/static/img/marketplace/marketplace-2.png new file mode 100644 index 00000000000..715705534cd Binary files /dev/null and b/apps/docs/static/img/marketplace/marketplace-2.png differ diff --git a/apps/docs/static/img/marketplace/marketplace-3.png b/apps/docs/static/img/marketplace/marketplace-3.png new file mode 100644 index 00000000000..af222cc4c8b Binary files /dev/null and b/apps/docs/static/img/marketplace/marketplace-3.png differ diff --git a/apps/docs/static/img/marketplace/marketplace-4.png b/apps/docs/static/img/marketplace/marketplace-4.png new file mode 100644 index 00000000000..938f1fdd5e5 Binary files /dev/null and b/apps/docs/static/img/marketplace/marketplace-4.png differ diff --git a/apps/docs/static/img/marketplace/marketplace-5.png b/apps/docs/static/img/marketplace/marketplace-5.png new file mode 100644 index 00000000000..d1cde75943f Binary files /dev/null and b/apps/docs/static/img/marketplace/marketplace-5.png differ diff --git a/apps/docs/static/img/marketplace/marketplace-6.png b/apps/docs/static/img/marketplace/marketplace-6.png new file mode 100644 index 00000000000..e31b6e2926f Binary files /dev/null and b/apps/docs/static/img/marketplace/marketplace-6.png differ diff --git a/apps/docs/static/img/marketplace/marketplace-7.png b/apps/docs/static/img/marketplace/marketplace-7.png new file mode 100644 index 00000000000..00f1e0028f8 Binary files /dev/null and b/apps/docs/static/img/marketplace/marketplace-7.png differ diff --git a/apps/docs/static/img/marketplace/marketplace.png b/apps/docs/static/img/marketplace/marketplace.png new file mode 100644 index 00000000000..97dcb2c600d Binary files /dev/null and b/apps/docs/static/img/marketplace/marketplace.png differ diff --git a/apps/docs/static/img/message-queueing/message-queueing.png b/apps/docs/static/img/message-queueing/message-queueing.png new file mode 100644 index 00000000000..2e4087ff88b Binary files /dev/null and b/apps/docs/static/img/message-queueing/message-queueing.png differ diff --git a/apps/docs/static/img/model-temperature/model-temperature.gif b/apps/docs/static/img/model-temperature/model-temperature.gif new file mode 100644 index 00000000000..0e6b7a9b162 Binary files /dev/null and b/apps/docs/static/img/model-temperature/model-temperature.gif differ diff --git a/apps/docs/static/img/model-temperature/model-temperature.png b/apps/docs/static/img/model-temperature/model-temperature.png new file mode 100644 index 00000000000..89152261155 Binary files /dev/null and b/apps/docs/static/img/model-temperature/model-temperature.png differ diff --git a/apps/docs/static/img/modes/modes-1.png b/apps/docs/static/img/modes/modes-1.png new file mode 100644 index 00000000000..465c98b566d Binary files /dev/null and b/apps/docs/static/img/modes/modes-1.png differ diff --git a/apps/docs/static/img/modes/modes-2.png b/apps/docs/static/img/modes/modes-2.png new file mode 100644 index 00000000000..eb98c17432f Binary files /dev/null and b/apps/docs/static/img/modes/modes-2.png differ diff --git a/apps/docs/static/img/modes/modes.png b/apps/docs/static/img/modes/modes.png new file mode 100644 index 00000000000..cc0cd8358b5 Binary files /dev/null and b/apps/docs/static/img/modes/modes.png differ diff --git a/apps/docs/static/img/power-steering/power-steering.png b/apps/docs/static/img/power-steering/power-steering.png new file mode 100644 index 00000000000..f6ecaf514ed Binary files /dev/null and b/apps/docs/static/img/power-steering/power-steering.png differ diff --git a/apps/docs/static/img/recommended-mcp-servers/context7-global-setup-fixed.png b/apps/docs/static/img/recommended-mcp-servers/context7-global-setup-fixed.png new file mode 100644 index 00000000000..785d17ca356 Binary files /dev/null and b/apps/docs/static/img/recommended-mcp-servers/context7-global-setup-fixed.png differ diff --git a/apps/docs/static/img/recommended-mcp-servers/context7-project-setup-fixed.png b/apps/docs/static/img/recommended-mcp-servers/context7-project-setup-fixed.png new file mode 100644 index 00000000000..bb8b6023502 Binary files /dev/null and b/apps/docs/static/img/recommended-mcp-servers/context7-project-setup-fixed.png differ diff --git a/apps/docs/static/img/recommended-mcp-servers/context7-running-fixed.png b/apps/docs/static/img/recommended-mcp-servers/context7-running-fixed.png new file mode 100644 index 00000000000..1e80a0f082d Binary files /dev/null and b/apps/docs/static/img/recommended-mcp-servers/context7-running-fixed.png differ diff --git a/apps/docs/static/img/reporting-errors/reporting-errors-1.png b/apps/docs/static/img/reporting-errors/reporting-errors-1.png new file mode 100644 index 00000000000..472b7e17d32 Binary files /dev/null and b/apps/docs/static/img/reporting-errors/reporting-errors-1.png differ diff --git a/apps/docs/static/img/reporting-errors/reporting-errors-2.png b/apps/docs/static/img/reporting-errors/reporting-errors-2.png new file mode 100644 index 00000000000..9f3838efb09 Binary files /dev/null and b/apps/docs/static/img/reporting-errors/reporting-errors-2.png differ diff --git a/apps/docs/static/img/right-column-roo.gif b/apps/docs/static/img/right-column-roo.gif new file mode 100644 index 00000000000..eaa058efcb7 Binary files /dev/null and b/apps/docs/static/img/right-column-roo.gif differ diff --git a/apps/web-roo-code/public/Roo-Code-Logo-Horiz-blk.svg b/apps/docs/static/img/roo-code-logo-dark.svg similarity index 100% rename from apps/web-roo-code/public/Roo-Code-Logo-Horiz-blk.svg rename to apps/docs/static/img/roo-code-logo-dark.svg diff --git a/apps/web-roo-code/public/Roo-Code-Logo-Horiz-white.svg b/apps/docs/static/img/roo-code-logo-white.svg similarity index 100% rename from apps/web-roo-code/public/Roo-Code-Logo-Horiz-white.svg rename to apps/docs/static/img/roo-code-logo-white.svg diff --git a/apps/docs/static/img/settings-management/settings-management.png b/apps/docs/static/img/settings-management/settings-management.png new file mode 100644 index 00000000000..e19dfbb1e82 Binary files /dev/null and b/apps/docs/static/img/settings-management/settings-management.png differ diff --git a/apps/docs/static/img/shell-integration/shell-integration-1.png b/apps/docs/static/img/shell-integration/shell-integration-1.png new file mode 100644 index 00000000000..d089be39ed7 Binary files /dev/null and b/apps/docs/static/img/shell-integration/shell-integration-1.png differ diff --git a/apps/docs/static/img/shell-integration/shell-integration-10.png b/apps/docs/static/img/shell-integration/shell-integration-10.png new file mode 100644 index 00000000000..22347adccaf Binary files /dev/null and b/apps/docs/static/img/shell-integration/shell-integration-10.png differ diff --git a/apps/docs/static/img/shell-integration/shell-integration-11.png b/apps/docs/static/img/shell-integration/shell-integration-11.png new file mode 100644 index 00000000000..4aa836e2422 Binary files /dev/null and b/apps/docs/static/img/shell-integration/shell-integration-11.png differ diff --git a/apps/docs/static/img/shell-integration/shell-integration-12.png b/apps/docs/static/img/shell-integration/shell-integration-12.png new file mode 100644 index 00000000000..a356a606a6f Binary files /dev/null and b/apps/docs/static/img/shell-integration/shell-integration-12.png differ diff --git a/apps/docs/static/img/shell-integration/shell-integration-13.png b/apps/docs/static/img/shell-integration/shell-integration-13.png new file mode 100644 index 00000000000..f4255e8dee8 Binary files /dev/null and b/apps/docs/static/img/shell-integration/shell-integration-13.png differ diff --git a/apps/docs/static/img/shell-integration/shell-integration-14.png b/apps/docs/static/img/shell-integration/shell-integration-14.png new file mode 100644 index 00000000000..c2de52ec35b Binary files /dev/null and b/apps/docs/static/img/shell-integration/shell-integration-14.png differ diff --git a/apps/docs/static/img/shell-integration/shell-integration-15.png b/apps/docs/static/img/shell-integration/shell-integration-15.png new file mode 100644 index 00000000000..7ca4a0ade0d Binary files /dev/null and b/apps/docs/static/img/shell-integration/shell-integration-15.png differ diff --git a/apps/docs/static/img/shell-integration/shell-integration-16.png b/apps/docs/static/img/shell-integration/shell-integration-16.png new file mode 100644 index 00000000000..3a684723676 Binary files /dev/null and b/apps/docs/static/img/shell-integration/shell-integration-16.png differ diff --git a/apps/docs/static/img/shell-integration/shell-integration-17.png b/apps/docs/static/img/shell-integration/shell-integration-17.png new file mode 100644 index 00000000000..56106648d83 Binary files /dev/null and b/apps/docs/static/img/shell-integration/shell-integration-17.png differ diff --git a/apps/docs/static/img/shell-integration/shell-integration-18.png b/apps/docs/static/img/shell-integration/shell-integration-18.png new file mode 100644 index 00000000000..d13285acebd Binary files /dev/null and b/apps/docs/static/img/shell-integration/shell-integration-18.png differ diff --git a/apps/docs/static/img/shell-integration/shell-integration-19.png b/apps/docs/static/img/shell-integration/shell-integration-19.png new file mode 100644 index 00000000000..b69e726b685 Binary files /dev/null and b/apps/docs/static/img/shell-integration/shell-integration-19.png differ diff --git a/apps/docs/static/img/shell-integration/shell-integration-2.png b/apps/docs/static/img/shell-integration/shell-integration-2.png new file mode 100644 index 00000000000..e55a511675b Binary files /dev/null and b/apps/docs/static/img/shell-integration/shell-integration-2.png differ diff --git a/apps/docs/static/img/shell-integration/shell-integration-20.png b/apps/docs/static/img/shell-integration/shell-integration-20.png new file mode 100644 index 00000000000..dc5344d3cc5 Binary files /dev/null and b/apps/docs/static/img/shell-integration/shell-integration-20.png differ diff --git a/apps/docs/static/img/shell-integration/shell-integration-21.png b/apps/docs/static/img/shell-integration/shell-integration-21.png new file mode 100644 index 00000000000..3b1ef2f15d9 Binary files /dev/null and b/apps/docs/static/img/shell-integration/shell-integration-21.png differ diff --git a/apps/docs/static/img/shell-integration/shell-integration-22.png b/apps/docs/static/img/shell-integration/shell-integration-22.png new file mode 100644 index 00000000000..0e87d269329 Binary files /dev/null and b/apps/docs/static/img/shell-integration/shell-integration-22.png differ diff --git a/apps/docs/static/img/shell-integration/shell-integration-23.png b/apps/docs/static/img/shell-integration/shell-integration-23.png new file mode 100644 index 00000000000..7ae3f7d6906 Binary files /dev/null and b/apps/docs/static/img/shell-integration/shell-integration-23.png differ diff --git a/apps/docs/static/img/shell-integration/shell-integration-24.png b/apps/docs/static/img/shell-integration/shell-integration-24.png new file mode 100644 index 00000000000..e0815d15bb7 Binary files /dev/null and b/apps/docs/static/img/shell-integration/shell-integration-24.png differ diff --git a/apps/docs/static/img/shell-integration/shell-integration-3.png b/apps/docs/static/img/shell-integration/shell-integration-3.png new file mode 100644 index 00000000000..24c8532a44a Binary files /dev/null and b/apps/docs/static/img/shell-integration/shell-integration-3.png differ diff --git a/apps/docs/static/img/shell-integration/shell-integration-4.png b/apps/docs/static/img/shell-integration/shell-integration-4.png new file mode 100644 index 00000000000..6faa30e5b46 Binary files /dev/null and b/apps/docs/static/img/shell-integration/shell-integration-4.png differ diff --git a/apps/docs/static/img/shell-integration/shell-integration-5.png b/apps/docs/static/img/shell-integration/shell-integration-5.png new file mode 100644 index 00000000000..f80ba0dcfbe Binary files /dev/null and b/apps/docs/static/img/shell-integration/shell-integration-5.png differ diff --git a/apps/docs/static/img/shell-integration/shell-integration-6.png b/apps/docs/static/img/shell-integration/shell-integration-6.png new file mode 100644 index 00000000000..c9e34f0b3c9 Binary files /dev/null and b/apps/docs/static/img/shell-integration/shell-integration-6.png differ diff --git a/apps/docs/static/img/shell-integration/shell-integration-7.png b/apps/docs/static/img/shell-integration/shell-integration-7.png new file mode 100644 index 00000000000..3011004ec7c Binary files /dev/null and b/apps/docs/static/img/shell-integration/shell-integration-7.png differ diff --git a/apps/docs/static/img/shell-integration/shell-integration-8.png b/apps/docs/static/img/shell-integration/shell-integration-8.png new file mode 100644 index 00000000000..6c5b7937909 Binary files /dev/null and b/apps/docs/static/img/shell-integration/shell-integration-8.png differ diff --git a/apps/docs/static/img/shell-integration/shell-integration-9.png b/apps/docs/static/img/shell-integration/shell-integration-9.png new file mode 100644 index 00000000000..0b3a6355cfb Binary files /dev/null and b/apps/docs/static/img/shell-integration/shell-integration-9.png differ diff --git a/apps/docs/static/img/shell-integration/shell-integration.png b/apps/docs/static/img/shell-integration/shell-integration.png new file mode 100644 index 00000000000..87749d1a09a Binary files /dev/null and b/apps/docs/static/img/shell-integration/shell-integration.png differ diff --git a/apps/docs/static/img/slash-commands/slash-commands-1.png b/apps/docs/static/img/slash-commands/slash-commands-1.png new file mode 100644 index 00000000000..dd8881cec93 Binary files /dev/null and b/apps/docs/static/img/slash-commands/slash-commands-1.png differ diff --git a/apps/docs/static/img/slash-commands/slash-commands-2.png b/apps/docs/static/img/slash-commands/slash-commands-2.png new file mode 100644 index 00000000000..50bc6178852 Binary files /dev/null and b/apps/docs/static/img/slash-commands/slash-commands-2.png differ diff --git a/apps/docs/static/img/slash-commands/slash-commands-3.png b/apps/docs/static/img/slash-commands/slash-commands-3.png new file mode 100644 index 00000000000..1949e87fa69 Binary files /dev/null and b/apps/docs/static/img/slash-commands/slash-commands-3.png differ diff --git a/apps/docs/static/img/slash-commands/slash-commands-4.png b/apps/docs/static/img/slash-commands/slash-commands-4.png new file mode 100644 index 00000000000..821e3c8d530 Binary files /dev/null and b/apps/docs/static/img/slash-commands/slash-commands-4.png differ diff --git a/apps/docs/static/img/slash-commands/slash-commands-5.png b/apps/docs/static/img/slash-commands/slash-commands-5.png new file mode 100644 index 00000000000..a97a016bc35 Binary files /dev/null and b/apps/docs/static/img/slash-commands/slash-commands-5.png differ diff --git a/apps/docs/static/img/slash-commands/slash-commands-6.png b/apps/docs/static/img/slash-commands/slash-commands-6.png new file mode 100644 index 00000000000..d70837e7d8b Binary files /dev/null and b/apps/docs/static/img/slash-commands/slash-commands-6.png differ diff --git a/apps/docs/static/img/slash-commands/slash-commands.png b/apps/docs/static/img/slash-commands/slash-commands.png new file mode 100644 index 00000000000..3f8570131c9 Binary files /dev/null and b/apps/docs/static/img/slash-commands/slash-commands.png differ diff --git a/apps/docs/static/img/social-share.png b/apps/docs/static/img/social-share.png new file mode 100644 index 00000000000..9b8709330cc Binary files /dev/null and b/apps/docs/static/img/social-share.png differ diff --git a/apps/docs/static/img/suggested-responses/suggested-responses-1.png b/apps/docs/static/img/suggested-responses/suggested-responses-1.png new file mode 100644 index 00000000000..76fe29f5366 Binary files /dev/null and b/apps/docs/static/img/suggested-responses/suggested-responses-1.png differ diff --git a/apps/docs/static/img/suggested-responses/suggested-responses.png b/apps/docs/static/img/suggested-responses/suggested-responses.png new file mode 100644 index 00000000000..e9e8def917c Binary files /dev/null and b/apps/docs/static/img/suggested-responses/suggested-responses.png differ diff --git a/apps/docs/static/img/task-sharing/task-sharing-1.gif b/apps/docs/static/img/task-sharing/task-sharing-1.gif new file mode 100644 index 00000000000..762504b3917 Binary files /dev/null and b/apps/docs/static/img/task-sharing/task-sharing-1.gif differ diff --git a/apps/docs/static/img/task-sharing/task-sharing-1.png b/apps/docs/static/img/task-sharing/task-sharing-1.png new file mode 100644 index 00000000000..0caf7f50baf Binary files /dev/null and b/apps/docs/static/img/task-sharing/task-sharing-1.png differ diff --git a/apps/docs/static/img/task-sharing/task-sharing-10.png b/apps/docs/static/img/task-sharing/task-sharing-10.png new file mode 100644 index 00000000000..e86d761398d Binary files /dev/null and b/apps/docs/static/img/task-sharing/task-sharing-10.png differ diff --git a/apps/docs/static/img/task-sharing/task-sharing-11.png b/apps/docs/static/img/task-sharing/task-sharing-11.png new file mode 100644 index 00000000000..0a28e342b2a Binary files /dev/null and b/apps/docs/static/img/task-sharing/task-sharing-11.png differ diff --git a/apps/docs/static/img/task-sharing/task-sharing-12.png b/apps/docs/static/img/task-sharing/task-sharing-12.png new file mode 100644 index 00000000000..ca66a64c8be Binary files /dev/null and b/apps/docs/static/img/task-sharing/task-sharing-12.png differ diff --git a/apps/docs/static/img/task-sharing/task-sharing-2.png b/apps/docs/static/img/task-sharing/task-sharing-2.png new file mode 100644 index 00000000000..f3ade40bee6 Binary files /dev/null and b/apps/docs/static/img/task-sharing/task-sharing-2.png differ diff --git a/apps/docs/static/img/task-sharing/task-sharing-3.png b/apps/docs/static/img/task-sharing/task-sharing-3.png new file mode 100644 index 00000000000..3030a3ec30f Binary files /dev/null and b/apps/docs/static/img/task-sharing/task-sharing-3.png differ diff --git a/apps/docs/static/img/task-sharing/task-sharing-4.png b/apps/docs/static/img/task-sharing/task-sharing-4.png new file mode 100644 index 00000000000..8c339408051 Binary files /dev/null and b/apps/docs/static/img/task-sharing/task-sharing-4.png differ diff --git a/apps/docs/static/img/task-sharing/task-sharing-5.png b/apps/docs/static/img/task-sharing/task-sharing-5.png new file mode 100644 index 00000000000..5908e2c5f26 Binary files /dev/null and b/apps/docs/static/img/task-sharing/task-sharing-5.png differ diff --git a/apps/docs/static/img/task-sharing/task-sharing-6.png b/apps/docs/static/img/task-sharing/task-sharing-6.png new file mode 100644 index 00000000000..ea50f5dd5df Binary files /dev/null and b/apps/docs/static/img/task-sharing/task-sharing-6.png differ diff --git a/apps/docs/static/img/task-sharing/task-sharing-7.png b/apps/docs/static/img/task-sharing/task-sharing-7.png new file mode 100644 index 00000000000..3f31155bfd1 Binary files /dev/null and b/apps/docs/static/img/task-sharing/task-sharing-7.png differ diff --git a/apps/docs/static/img/task-sharing/task-sharing-8.png b/apps/docs/static/img/task-sharing/task-sharing-8.png new file mode 100644 index 00000000000..063f6505ff5 Binary files /dev/null and b/apps/docs/static/img/task-sharing/task-sharing-8.png differ diff --git a/apps/docs/static/img/task-sharing/task-sharing-9.png b/apps/docs/static/img/task-sharing/task-sharing-9.png new file mode 100644 index 00000000000..d979fb0ae47 Binary files /dev/null and b/apps/docs/static/img/task-sharing/task-sharing-9.png differ diff --git a/apps/docs/static/img/task-sharing/task-sharing.png b/apps/docs/static/img/task-sharing/task-sharing.png new file mode 100644 index 00000000000..8df379a875c Binary files /dev/null and b/apps/docs/static/img/task-sharing/task-sharing.png differ diff --git a/apps/docs/static/img/task-todo-list/task-todo-list-1.png b/apps/docs/static/img/task-todo-list/task-todo-list-1.png new file mode 100644 index 00000000000..b37caa1cd92 Binary files /dev/null and b/apps/docs/static/img/task-todo-list/task-todo-list-1.png differ diff --git a/apps/docs/static/img/task-todo-list/task-todo-list-2.png b/apps/docs/static/img/task-todo-list/task-todo-list-2.png new file mode 100644 index 00000000000..42ae5397626 Binary files /dev/null and b/apps/docs/static/img/task-todo-list/task-todo-list-2.png differ diff --git a/apps/docs/static/img/task-todo-list/task-todo-list-3.png b/apps/docs/static/img/task-todo-list/task-todo-list-3.png new file mode 100644 index 00000000000..79072b309af Binary files /dev/null and b/apps/docs/static/img/task-todo-list/task-todo-list-3.png differ diff --git a/apps/docs/static/img/task-todo-list/task-todo-list-4.png b/apps/docs/static/img/task-todo-list/task-todo-list-4.png new file mode 100644 index 00000000000..6378b5db8af Binary files /dev/null and b/apps/docs/static/img/task-todo-list/task-todo-list-4.png differ diff --git a/apps/docs/static/img/task-todo-list/task-todo-list-5.png b/apps/docs/static/img/task-todo-list/task-todo-list-5.png new file mode 100644 index 00000000000..14033a4e3c1 Binary files /dev/null and b/apps/docs/static/img/task-todo-list/task-todo-list-5.png differ diff --git a/apps/docs/static/img/task-todo-list/task-todo-list-6.png b/apps/docs/static/img/task-todo-list/task-todo-list-6.png new file mode 100644 index 00000000000..3f96434656f Binary files /dev/null and b/apps/docs/static/img/task-todo-list/task-todo-list-6.png differ diff --git a/apps/docs/static/img/task-todo-list/task-todo-list.png b/apps/docs/static/img/task-todo-list/task-todo-list.png new file mode 100644 index 00000000000..6d3d57667fc Binary files /dev/null and b/apps/docs/static/img/task-todo-list/task-todo-list.png differ diff --git a/apps/docs/static/img/the-chat-interface/the-chat-interface-1.png b/apps/docs/static/img/the-chat-interface/the-chat-interface-1.png new file mode 100644 index 00000000000..328d802d3bd Binary files /dev/null and b/apps/docs/static/img/the-chat-interface/the-chat-interface-1.png differ diff --git a/apps/docs/static/img/typing-your-requests/naturally.gif b/apps/docs/static/img/typing-your-requests/naturally.gif new file mode 100644 index 00000000000..24449aa4479 Binary files /dev/null and b/apps/docs/static/img/typing-your-requests/naturally.gif differ diff --git a/apps/docs/static/img/typing-your-requests/typing-your-requests.png b/apps/docs/static/img/typing-your-requests/typing-your-requests.png new file mode 100644 index 00000000000..14b8bda0310 Binary files /dev/null and b/apps/docs/static/img/typing-your-requests/typing-your-requests.png differ diff --git a/apps/docs/static/img/using-mcp-in-roo/using-mcp-in-roo-1.png b/apps/docs/static/img/using-mcp-in-roo/using-mcp-in-roo-1.png new file mode 100644 index 00000000000..e602c103dea Binary files /dev/null and b/apps/docs/static/img/using-mcp-in-roo/using-mcp-in-roo-1.png differ diff --git a/apps/docs/static/img/using-mcp-in-roo/using-mcp-in-roo-10.png b/apps/docs/static/img/using-mcp-in-roo/using-mcp-in-roo-10.png new file mode 100644 index 00000000000..6a63955801f Binary files /dev/null and b/apps/docs/static/img/using-mcp-in-roo/using-mcp-in-roo-10.png differ diff --git a/apps/docs/static/img/using-mcp-in-roo/using-mcp-in-roo-2.png b/apps/docs/static/img/using-mcp-in-roo/using-mcp-in-roo-2.png new file mode 100644 index 00000000000..59f80a2a46f Binary files /dev/null and b/apps/docs/static/img/using-mcp-in-roo/using-mcp-in-roo-2.png differ diff --git a/apps/docs/static/img/using-mcp-in-roo/using-mcp-in-roo-3.png b/apps/docs/static/img/using-mcp-in-roo/using-mcp-in-roo-3.png new file mode 100644 index 00000000000..064c185e7b0 Binary files /dev/null and b/apps/docs/static/img/using-mcp-in-roo/using-mcp-in-roo-3.png differ diff --git a/apps/docs/static/img/using-mcp-in-roo/using-mcp-in-roo-4.png b/apps/docs/static/img/using-mcp-in-roo/using-mcp-in-roo-4.png new file mode 100644 index 00000000000..dff3532ecd9 Binary files /dev/null and b/apps/docs/static/img/using-mcp-in-roo/using-mcp-in-roo-4.png differ diff --git a/apps/docs/static/img/using-mcp-in-roo/using-mcp-in-roo-5.png b/apps/docs/static/img/using-mcp-in-roo/using-mcp-in-roo-5.png new file mode 100644 index 00000000000..a995f04cd1e Binary files /dev/null and b/apps/docs/static/img/using-mcp-in-roo/using-mcp-in-roo-5.png differ diff --git a/apps/docs/static/img/using-mcp-in-roo/using-mcp-in-roo-6.png b/apps/docs/static/img/using-mcp-in-roo/using-mcp-in-roo-6.png new file mode 100644 index 00000000000..5f643b267f0 Binary files /dev/null and b/apps/docs/static/img/using-mcp-in-roo/using-mcp-in-roo-6.png differ diff --git a/apps/docs/static/img/using-mcp-in-roo/using-mcp-in-roo-7.png b/apps/docs/static/img/using-mcp-in-roo/using-mcp-in-roo-7.png new file mode 100644 index 00000000000..3b3a75a0624 Binary files /dev/null and b/apps/docs/static/img/using-mcp-in-roo/using-mcp-in-roo-7.png differ diff --git a/apps/docs/static/img/using-mcp-in-roo/using-mcp-in-roo-8.png b/apps/docs/static/img/using-mcp-in-roo/using-mcp-in-roo-8.png new file mode 100644 index 00000000000..b25949e1786 Binary files /dev/null and b/apps/docs/static/img/using-mcp-in-roo/using-mcp-in-roo-8.png differ diff --git a/apps/docs/static/img/using-mcp-in-roo/using-mcp-in-roo-9.png b/apps/docs/static/img/using-mcp-in-roo/using-mcp-in-roo-9.png new file mode 100644 index 00000000000..1e5ea319ecf Binary files /dev/null and b/apps/docs/static/img/using-mcp-in-roo/using-mcp-in-roo-9.png differ diff --git a/apps/docs/static/img/using-mcp-in-roo/using-mcp-in-roo.png b/apps/docs/static/img/using-mcp-in-roo/using-mcp-in-roo.png new file mode 100644 index 00000000000..f217b786387 Binary files /dev/null and b/apps/docs/static/img/using-mcp-in-roo/using-mcp-in-roo.png differ diff --git a/apps/docs/static/img/using-modes/using-modes-1.png b/apps/docs/static/img/using-modes/using-modes-1.png new file mode 100644 index 00000000000..d25686fb93c Binary files /dev/null and b/apps/docs/static/img/using-modes/using-modes-1.png differ diff --git a/apps/docs/static/img/using-modes/using-modes-2.png b/apps/docs/static/img/using-modes/using-modes-2.png new file mode 100644 index 00000000000..e3cb7c43bf7 Binary files /dev/null and b/apps/docs/static/img/using-modes/using-modes-2.png differ diff --git a/apps/docs/static/img/using-modes/using-modes.png b/apps/docs/static/img/using-modes/using-modes.png new file mode 100644 index 00000000000..b22f93a913c Binary files /dev/null and b/apps/docs/static/img/using-modes/using-modes.png differ diff --git a/apps/docs/static/img/v3.11.13/v3.11.13-1.png b/apps/docs/static/img/v3.11.13/v3.11.13-1.png new file mode 100644 index 00000000000..27bb8e46387 Binary files /dev/null and b/apps/docs/static/img/v3.11.13/v3.11.13-1.png differ diff --git a/apps/docs/static/img/v3.11.13/v3.11.13.png b/apps/docs/static/img/v3.11.13/v3.11.13.png new file mode 100644 index 00000000000..631a95d1124 Binary files /dev/null and b/apps/docs/static/img/v3.11.13/v3.11.13.png differ diff --git a/apps/docs/static/img/v3.11/v3.11-1.png b/apps/docs/static/img/v3.11/v3.11-1.png new file mode 100644 index 00000000000..22a7c73f815 Binary files /dev/null and b/apps/docs/static/img/v3.11/v3.11-1.png differ diff --git a/apps/docs/static/img/v3.11/v3.11-2.png b/apps/docs/static/img/v3.11/v3.11-2.png new file mode 100644 index 00000000000..b51f1b9b040 Binary files /dev/null and b/apps/docs/static/img/v3.11/v3.11-2.png differ diff --git a/apps/docs/static/img/v3.11/v3.11.png b/apps/docs/static/img/v3.11/v3.11.png new file mode 100644 index 00000000000..6966f45af7b Binary files /dev/null and b/apps/docs/static/img/v3.11/v3.11.png differ diff --git a/apps/docs/static/img/v3.14.0/v3.14.0.png b/apps/docs/static/img/v3.14.0/v3.14.0.png new file mode 100644 index 00000000000..3204855bed8 Binary files /dev/null and b/apps/docs/static/img/v3.14.0/v3.14.0.png differ diff --git a/apps/docs/static/img/v3.14.2/v3.14.2.png b/apps/docs/static/img/v3.14.2/v3.14.2.png new file mode 100644 index 00000000000..754ae9568ab Binary files /dev/null and b/apps/docs/static/img/v3.14.2/v3.14.2.png differ diff --git a/apps/docs/static/img/v3.14.3/v3.14.3-1.png b/apps/docs/static/img/v3.14.3/v3.14.3-1.png new file mode 100644 index 00000000000..d62608d67f6 Binary files /dev/null and b/apps/docs/static/img/v3.14.3/v3.14.3-1.png differ diff --git a/apps/docs/static/img/v3.14.3/v3.14.3.png b/apps/docs/static/img/v3.14.3/v3.14.3.png new file mode 100644 index 00000000000..066049465b0 Binary files /dev/null and b/apps/docs/static/img/v3.14.3/v3.14.3.png differ diff --git a/apps/docs/static/img/v3.15.0/v3.15.0-1.png b/apps/docs/static/img/v3.15.0/v3.15.0-1.png new file mode 100644 index 00000000000..8063006af54 Binary files /dev/null and b/apps/docs/static/img/v3.15.0/v3.15.0-1.png differ diff --git a/apps/docs/static/img/v3.15.0/v3.15.0-2.png b/apps/docs/static/img/v3.15.0/v3.15.0-2.png new file mode 100644 index 00000000000..80b70cc163a Binary files /dev/null and b/apps/docs/static/img/v3.15.0/v3.15.0-2.png differ diff --git a/apps/docs/static/img/v3.15.0/v3.15.0.png b/apps/docs/static/img/v3.15.0/v3.15.0.png new file mode 100644 index 00000000000..c7fa55b1b97 Binary files /dev/null and b/apps/docs/static/img/v3.15.0/v3.15.0.png differ diff --git a/apps/docs/static/img/v3.15.2/v3.15.2.png b/apps/docs/static/img/v3.15.2/v3.15.2.png new file mode 100644 index 00000000000..1f1d12955cd Binary files /dev/null and b/apps/docs/static/img/v3.15.2/v3.15.2.png differ diff --git a/apps/docs/static/img/v3.15/v3.15-1.png b/apps/docs/static/img/v3.15/v3.15-1.png new file mode 100644 index 00000000000..37eb260d804 Binary files /dev/null and b/apps/docs/static/img/v3.15/v3.15-1.png differ diff --git a/apps/docs/static/img/v3.15/v3.15.png b/apps/docs/static/img/v3.15/v3.15.png new file mode 100644 index 00000000000..5a4ffd2fd42 Binary files /dev/null and b/apps/docs/static/img/v3.15/v3.15.png differ diff --git a/apps/docs/static/img/v3.16/v3.16-1.png b/apps/docs/static/img/v3.16/v3.16-1.png new file mode 100644 index 00000000000..ab3ac2f24b3 Binary files /dev/null and b/apps/docs/static/img/v3.16/v3.16-1.png differ diff --git a/apps/docs/static/img/v3.16/v3.16.gif b/apps/docs/static/img/v3.16/v3.16.gif new file mode 100644 index 00000000000..d9f4ba88f0a Binary files /dev/null and b/apps/docs/static/img/v3.16/v3.16.gif differ diff --git a/apps/docs/static/img/v3.16/v3.16.png b/apps/docs/static/img/v3.16/v3.16.png new file mode 100644 index 00000000000..ff433788caa Binary files /dev/null and b/apps/docs/static/img/v3.16/v3.16.png differ diff --git a/apps/docs/static/img/v3.17.0/v3.17.0.png b/apps/docs/static/img/v3.17.0/v3.17.0.png new file mode 100644 index 00000000000..f08f0e040f2 Binary files /dev/null and b/apps/docs/static/img/v3.17.0/v3.17.0.png differ diff --git a/apps/docs/static/img/v3.18.0/v3.18.0-1.png b/apps/docs/static/img/v3.18.0/v3.18.0-1.png new file mode 100644 index 00000000000..2ac54d5e913 Binary files /dev/null and b/apps/docs/static/img/v3.18.0/v3.18.0-1.png differ diff --git a/apps/docs/static/img/v3.18.0/v3.18.0.png b/apps/docs/static/img/v3.18.0/v3.18.0.png new file mode 100644 index 00000000000..b37da240c4d Binary files /dev/null and b/apps/docs/static/img/v3.18.0/v3.18.0.png differ diff --git a/apps/docs/static/img/v3.22.0/v3.22.0-1.png b/apps/docs/static/img/v3.22.0/v3.22.0-1.png new file mode 100644 index 00000000000..200fa276c1f Binary files /dev/null and b/apps/docs/static/img/v3.22.0/v3.22.0-1.png differ diff --git a/apps/docs/static/img/v3.22.0/v3.22.0.png b/apps/docs/static/img/v3.22.0/v3.22.0.png new file mode 100644 index 00000000000..1b0f5f1f1b5 Binary files /dev/null and b/apps/docs/static/img/v3.22.0/v3.22.0.png differ diff --git a/apps/docs/static/img/v3.23.0/v3.23.0-1.png b/apps/docs/static/img/v3.23.0/v3.23.0-1.png new file mode 100644 index 00000000000..aa75bd5e379 Binary files /dev/null and b/apps/docs/static/img/v3.23.0/v3.23.0-1.png differ diff --git a/apps/docs/static/img/v3.23.0/v3.23.0.png b/apps/docs/static/img/v3.23.0/v3.23.0.png new file mode 100644 index 00000000000..a97cd61c746 Binary files /dev/null and b/apps/docs/static/img/v3.23.0/v3.23.0.png differ diff --git a/apps/docs/static/img/v3.23/v3.23-1.png b/apps/docs/static/img/v3.23/v3.23-1.png new file mode 100644 index 00000000000..1b2fa6478ed Binary files /dev/null and b/apps/docs/static/img/v3.23/v3.23-1.png differ diff --git a/apps/docs/static/img/v3.23/v3.23.png b/apps/docs/static/img/v3.23/v3.23.png new file mode 100644 index 00000000000..1b2fa6478ed Binary files /dev/null and b/apps/docs/static/img/v3.23/v3.23.png differ diff --git a/apps/docs/static/img/v3.25.0/v3.25.0-1.png b/apps/docs/static/img/v3.25.0/v3.25.0-1.png new file mode 100644 index 00000000000..5159d219033 Binary files /dev/null and b/apps/docs/static/img/v3.25.0/v3.25.0-1.png differ diff --git a/apps/docs/static/img/v3.25.0/v3.25.0-2.png b/apps/docs/static/img/v3.25.0/v3.25.0-2.png new file mode 100644 index 00000000000..3243e4e34da Binary files /dev/null and b/apps/docs/static/img/v3.25.0/v3.25.0-2.png differ diff --git a/apps/docs/static/img/v3.25.0/v3.25.0.png b/apps/docs/static/img/v3.25.0/v3.25.0.png new file mode 100644 index 00000000000..64985a1bebb Binary files /dev/null and b/apps/docs/static/img/v3.25.0/v3.25.0.png differ diff --git a/apps/docs/static/img/v3.25.21/v3.25.21.png b/apps/docs/static/img/v3.25.21/v3.25.21.png new file mode 100644 index 00000000000..8f4b43e8810 Binary files /dev/null and b/apps/docs/static/img/v3.25.21/v3.25.21.png differ diff --git a/apps/docs/static/img/v3.25.5/v3.25.5-1.png b/apps/docs/static/img/v3.25.5/v3.25.5-1.png new file mode 100644 index 00000000000..11e27905174 Binary files /dev/null and b/apps/docs/static/img/v3.25.5/v3.25.5-1.png differ diff --git a/apps/docs/static/img/v3.25.5/v3.25.5.png b/apps/docs/static/img/v3.25.5/v3.25.5.png new file mode 100644 index 00000000000..b05bba1e601 Binary files /dev/null and b/apps/docs/static/img/v3.25.5/v3.25.5.png differ diff --git a/apps/docs/static/img/v3.26.0/v3.26.0.png b/apps/docs/static/img/v3.26.0/v3.26.0.png new file mode 100644 index 00000000000..393eaa94ebd Binary files /dev/null and b/apps/docs/static/img/v3.26.0/v3.26.0.png differ diff --git a/apps/docs/static/img/v3.26.1/v3.26.1.png b/apps/docs/static/img/v3.26.1/v3.26.1.png new file mode 100644 index 00000000000..9eb76ec7067 Binary files /dev/null and b/apps/docs/static/img/v3.26.1/v3.26.1.png differ diff --git a/apps/docs/static/img/v3.26.2/v3.26.2.png b/apps/docs/static/img/v3.26.2/v3.26.2.png new file mode 100644 index 00000000000..9b21da456e1 Binary files /dev/null and b/apps/docs/static/img/v3.26.2/v3.26.2.png differ diff --git a/apps/docs/static/img/v3.26.3/v3.26.3.png b/apps/docs/static/img/v3.26.3/v3.26.3.png new file mode 100644 index 00000000000..3d2b5cf729f Binary files /dev/null and b/apps/docs/static/img/v3.26.3/v3.26.3.png differ diff --git a/apps/docs/static/img/v3.26.4/v3.26.4.png b/apps/docs/static/img/v3.26.4/v3.26.4.png new file mode 100644 index 00000000000..76803b9f994 Binary files /dev/null and b/apps/docs/static/img/v3.26.4/v3.26.4.png differ diff --git a/apps/docs/static/img/v3.26.5/v3.26.5.png b/apps/docs/static/img/v3.26.5/v3.26.5.png new file mode 100644 index 00000000000..e986259ff50 Binary files /dev/null and b/apps/docs/static/img/v3.26.5/v3.26.5.png differ diff --git a/apps/docs/static/img/v3.26.6/v3.26.6.png b/apps/docs/static/img/v3.26.6/v3.26.6.png new file mode 100644 index 00000000000..96be69a4e36 Binary files /dev/null and b/apps/docs/static/img/v3.26.6/v3.26.6.png differ diff --git a/apps/docs/static/img/v3.26.7/v3.26.7.png b/apps/docs/static/img/v3.26.7/v3.26.7.png new file mode 100644 index 00000000000..44f2aa07357 Binary files /dev/null and b/apps/docs/static/img/v3.26.7/v3.26.7.png differ diff --git a/apps/docs/static/img/v3.27.0/v3.27.0.png b/apps/docs/static/img/v3.27.0/v3.27.0.png new file mode 100644 index 00000000000..fce290e357c Binary files /dev/null and b/apps/docs/static/img/v3.27.0/v3.27.0.png differ diff --git a/apps/docs/static/img/v3.28.0/v3.28.0.png b/apps/docs/static/img/v3.28.0/v3.28.0.png new file mode 100644 index 00000000000..14e22059618 Binary files /dev/null and b/apps/docs/static/img/v3.28.0/v3.28.0.png differ diff --git a/apps/docs/static/img/v3.28.1/v3.28.1.png b/apps/docs/static/img/v3.28.1/v3.28.1.png new file mode 100644 index 00000000000..d4c77ebcfef Binary files /dev/null and b/apps/docs/static/img/v3.28.1/v3.28.1.png differ diff --git a/apps/docs/static/img/v3.28.10/v3.28.10-1.png b/apps/docs/static/img/v3.28.10/v3.28.10-1.png new file mode 100644 index 00000000000..285d276b2e3 Binary files /dev/null and b/apps/docs/static/img/v3.28.10/v3.28.10-1.png differ diff --git a/apps/docs/static/img/v3.28.10/v3.28.10.png b/apps/docs/static/img/v3.28.10/v3.28.10.png new file mode 100644 index 00000000000..97f6cd429f4 Binary files /dev/null and b/apps/docs/static/img/v3.28.10/v3.28.10.png differ diff --git a/apps/docs/static/img/v3.28.14/v3.28.14.png b/apps/docs/static/img/v3.28.14/v3.28.14.png new file mode 100644 index 00000000000..4ef1acc7021 Binary files /dev/null and b/apps/docs/static/img/v3.28.14/v3.28.14.png differ diff --git a/apps/docs/static/img/v3.28.15/v3.28.15.png b/apps/docs/static/img/v3.28.15/v3.28.15.png new file mode 100644 index 00000000000..fc6e235befe Binary files /dev/null and b/apps/docs/static/img/v3.28.15/v3.28.15.png differ diff --git a/apps/docs/static/img/v3.28.16/v3.28.16.png b/apps/docs/static/img/v3.28.16/v3.28.16.png new file mode 100644 index 00000000000..5c4c31ece22 Binary files /dev/null and b/apps/docs/static/img/v3.28.16/v3.28.16.png differ diff --git a/apps/docs/static/img/v3.28.2/v3.28.2.png b/apps/docs/static/img/v3.28.2/v3.28.2.png new file mode 100644 index 00000000000..917c0a63098 Binary files /dev/null and b/apps/docs/static/img/v3.28.2/v3.28.2.png differ diff --git a/apps/docs/static/img/v3.28.3/v3.28.3.png b/apps/docs/static/img/v3.28.3/v3.28.3.png new file mode 100644 index 00000000000..883ef809700 Binary files /dev/null and b/apps/docs/static/img/v3.28.3/v3.28.3.png differ diff --git a/apps/docs/static/img/v3.28.4/v3.28.4.png b/apps/docs/static/img/v3.28.4/v3.28.4.png new file mode 100644 index 00000000000..ea1e82a8dda Binary files /dev/null and b/apps/docs/static/img/v3.28.4/v3.28.4.png differ diff --git a/apps/docs/static/img/v3.28.5/v3.28.5.png b/apps/docs/static/img/v3.28.5/v3.28.5.png new file mode 100644 index 00000000000..0a22c25c40d Binary files /dev/null and b/apps/docs/static/img/v3.28.5/v3.28.5.png differ diff --git a/apps/docs/static/img/v3.28.6/v3.28.6.png b/apps/docs/static/img/v3.28.6/v3.28.6.png new file mode 100644 index 00000000000..3ce1883756f Binary files /dev/null and b/apps/docs/static/img/v3.28.6/v3.28.6.png differ diff --git a/apps/docs/static/img/v3.28.7/v3.28.7.png b/apps/docs/static/img/v3.28.7/v3.28.7.png new file mode 100644 index 00000000000..d4690f19c99 Binary files /dev/null and b/apps/docs/static/img/v3.28.7/v3.28.7.png differ diff --git a/apps/docs/static/img/v3.28.8/v3.28.8.png b/apps/docs/static/img/v3.28.8/v3.28.8.png new file mode 100644 index 00000000000..8fcfa224538 Binary files /dev/null and b/apps/docs/static/img/v3.28.8/v3.28.8.png differ diff --git a/apps/docs/static/img/v3.28.9/v3.28.9.png b/apps/docs/static/img/v3.28.9/v3.28.9.png new file mode 100644 index 00000000000..a2272430035 Binary files /dev/null and b/apps/docs/static/img/v3.28.9/v3.28.9.png differ diff --git a/apps/docs/static/img/v3.29.0/v3.29.0.png b/apps/docs/static/img/v3.29.0/v3.29.0.png new file mode 100644 index 00000000000..8f9381fbae7 Binary files /dev/null and b/apps/docs/static/img/v3.29.0/v3.29.0.png differ diff --git a/apps/docs/static/img/v3.29.1/v3.29.1.png b/apps/docs/static/img/v3.29.1/v3.29.1.png new file mode 100644 index 00000000000..71dcd474b0e Binary files /dev/null and b/apps/docs/static/img/v3.29.1/v3.29.1.png differ diff --git a/apps/docs/static/img/v3.29.4/v3.29.4.png b/apps/docs/static/img/v3.29.4/v3.29.4.png new file mode 100644 index 00000000000..aa53e7bcdb1 Binary files /dev/null and b/apps/docs/static/img/v3.29.4/v3.29.4.png differ diff --git a/apps/docs/static/img/v3.30.0/v3.30.0.png b/apps/docs/static/img/v3.30.0/v3.30.0.png new file mode 100644 index 00000000000..8050aa49ce6 Binary files /dev/null and b/apps/docs/static/img/v3.30.0/v3.30.0.png differ diff --git a/apps/docs/static/img/v3.30.2/v3.30.2.png b/apps/docs/static/img/v3.30.2/v3.30.2.png new file mode 100644 index 00000000000..134bf2290db Binary files /dev/null and b/apps/docs/static/img/v3.30.2/v3.30.2.png differ diff --git a/apps/docs/static/img/v3.30.3/v3.30.3.png b/apps/docs/static/img/v3.30.3/v3.30.3.png new file mode 100644 index 00000000000..6fb3e84d780 Binary files /dev/null and b/apps/docs/static/img/v3.30.3/v3.30.3.png differ diff --git a/apps/docs/static/img/v3.31.0/v3.31.0.png b/apps/docs/static/img/v3.31.0/v3.31.0.png new file mode 100644 index 00000000000..03177536404 Binary files /dev/null and b/apps/docs/static/img/v3.31.0/v3.31.0.png differ diff --git a/apps/docs/static/img/v3.31.1/v3.31.1.png b/apps/docs/static/img/v3.31.1/v3.31.1.png new file mode 100644 index 00000000000..15499dd936f Binary files /dev/null and b/apps/docs/static/img/v3.31.1/v3.31.1.png differ diff --git a/apps/docs/static/img/v3.31.3/v3.31.3.png b/apps/docs/static/img/v3.31.3/v3.31.3.png new file mode 100644 index 00000000000..f3b88afa91c Binary files /dev/null and b/apps/docs/static/img/v3.31.3/v3.31.3.png differ diff --git a/apps/docs/static/img/v3.32.0/v3.32.0.png b/apps/docs/static/img/v3.32.0/v3.32.0.png new file mode 100644 index 00000000000..2c7744b9eb0 Binary files /dev/null and b/apps/docs/static/img/v3.32.0/v3.32.0.png differ diff --git a/apps/docs/static/img/v3.32.1/v3.32.1.png b/apps/docs/static/img/v3.32.1/v3.32.1.png new file mode 100644 index 00000000000..b5613c2bc79 Binary files /dev/null and b/apps/docs/static/img/v3.32.1/v3.32.1.png differ diff --git a/apps/docs/static/img/v3.33.0/v3.33.0.png b/apps/docs/static/img/v3.33.0/v3.33.0.png new file mode 100644 index 00000000000..12ba3224262 Binary files /dev/null and b/apps/docs/static/img/v3.33.0/v3.33.0.png differ diff --git a/apps/docs/static/img/v3.33.1/v3.33.1.png b/apps/docs/static/img/v3.33.1/v3.33.1.png new file mode 100644 index 00000000000..fc27da29dd5 Binary files /dev/null and b/apps/docs/static/img/v3.33.1/v3.33.1.png differ diff --git a/apps/docs/static/img/v3.33.3/v3.33.3.png b/apps/docs/static/img/v3.33.3/v3.33.3.png new file mode 100644 index 00000000000..c71aab016e3 Binary files /dev/null and b/apps/docs/static/img/v3.33.3/v3.33.3.png differ diff --git a/apps/docs/static/img/v3.34.0/v3.34.0.png b/apps/docs/static/img/v3.34.0/v3.34.0.png new file mode 100644 index 00000000000..bd3696c1c83 Binary files /dev/null and b/apps/docs/static/img/v3.34.0/v3.34.0.png differ diff --git a/apps/docs/static/img/v3.34.2/v3.34.2.png b/apps/docs/static/img/v3.34.2/v3.34.2.png new file mode 100644 index 00000000000..d32526a3369 Binary files /dev/null and b/apps/docs/static/img/v3.34.2/v3.34.2.png differ diff --git a/apps/docs/static/img/v3.34.3/v3.34.3.png b/apps/docs/static/img/v3.34.3/v3.34.3.png new file mode 100644 index 00000000000..a3785a3ec60 Binary files /dev/null and b/apps/docs/static/img/v3.34.3/v3.34.3.png differ diff --git a/apps/docs/static/img/v3.34.4/v3.34.4.png b/apps/docs/static/img/v3.34.4/v3.34.4.png new file mode 100644 index 00000000000..11103bf3e46 Binary files /dev/null and b/apps/docs/static/img/v3.34.4/v3.34.4.png differ diff --git a/apps/docs/static/img/v3.34.5/v3.34.5.png b/apps/docs/static/img/v3.34.5/v3.34.5.png new file mode 100644 index 00000000000..90ba9b06b49 Binary files /dev/null and b/apps/docs/static/img/v3.34.5/v3.34.5.png differ diff --git a/apps/docs/static/img/v3.34.6/v3.34.6.png b/apps/docs/static/img/v3.34.6/v3.34.6.png new file mode 100644 index 00000000000..cfba5aa1100 Binary files /dev/null and b/apps/docs/static/img/v3.34.6/v3.34.6.png differ diff --git a/apps/docs/static/img/v3.34.7/v3.34.7.png b/apps/docs/static/img/v3.34.7/v3.34.7.png new file mode 100644 index 00000000000..b9cad77d8ed Binary files /dev/null and b/apps/docs/static/img/v3.34.7/v3.34.7.png differ diff --git a/apps/docs/static/img/v3.34.8/v3.34.8.png b/apps/docs/static/img/v3.34.8/v3.34.8.png new file mode 100644 index 00000000000..aedbe48365a Binary files /dev/null and b/apps/docs/static/img/v3.34.8/v3.34.8.png differ diff --git a/apps/docs/static/img/v3.35.0/v3.35.0.png b/apps/docs/static/img/v3.35.0/v3.35.0.png new file mode 100644 index 00000000000..71857f1d114 Binary files /dev/null and b/apps/docs/static/img/v3.35.0/v3.35.0.png differ diff --git a/apps/docs/static/img/v3.35.2/v3.35.2.png b/apps/docs/static/img/v3.35.2/v3.35.2.png new file mode 100644 index 00000000000..085712cf970 Binary files /dev/null and b/apps/docs/static/img/v3.35.2/v3.35.2.png differ diff --git a/apps/docs/static/img/v3.36.0/v3.36.0.png b/apps/docs/static/img/v3.36.0/v3.36.0.png new file mode 100644 index 00000000000..79200d592af Binary files /dev/null and b/apps/docs/static/img/v3.36.0/v3.36.0.png differ diff --git a/apps/docs/static/img/v3.36.1/v3.36.1.png b/apps/docs/static/img/v3.36.1/v3.36.1.png new file mode 100644 index 00000000000..a3e8b11d0ec Binary files /dev/null and b/apps/docs/static/img/v3.36.1/v3.36.1.png differ diff --git a/apps/docs/static/img/v3.36.10/v3.36.10.png b/apps/docs/static/img/v3.36.10/v3.36.10.png new file mode 100644 index 00000000000..76fb30b98e0 Binary files /dev/null and b/apps/docs/static/img/v3.36.10/v3.36.10.png differ diff --git a/apps/docs/static/img/v3.36.11/v3.36.11.png b/apps/docs/static/img/v3.36.11/v3.36.11.png new file mode 100644 index 00000000000..d4b0843cf80 Binary files /dev/null and b/apps/docs/static/img/v3.36.11/v3.36.11.png differ diff --git a/apps/docs/static/img/v3.36.12/v3.36.12.png b/apps/docs/static/img/v3.36.12/v3.36.12.png new file mode 100644 index 00000000000..9b48925f1d1 Binary files /dev/null and b/apps/docs/static/img/v3.36.12/v3.36.12.png differ diff --git a/apps/docs/static/img/v3.36.13/v3.36.13.png b/apps/docs/static/img/v3.36.13/v3.36.13.png new file mode 100644 index 00000000000..ab7f00a7066 Binary files /dev/null and b/apps/docs/static/img/v3.36.13/v3.36.13.png differ diff --git a/apps/docs/static/img/v3.36.14/v3.36.14.png b/apps/docs/static/img/v3.36.14/v3.36.14.png new file mode 100644 index 00000000000..15009bb2f40 Binary files /dev/null and b/apps/docs/static/img/v3.36.14/v3.36.14.png differ diff --git a/apps/docs/static/img/v3.36.15/v3.36.15.png b/apps/docs/static/img/v3.36.15/v3.36.15.png new file mode 100644 index 00000000000..549f02bd5de Binary files /dev/null and b/apps/docs/static/img/v3.36.15/v3.36.15.png differ diff --git a/apps/docs/static/img/v3.36.2/v3.36.2.png b/apps/docs/static/img/v3.36.2/v3.36.2.png new file mode 100644 index 00000000000..bd30b350de0 Binary files /dev/null and b/apps/docs/static/img/v3.36.2/v3.36.2.png differ diff --git a/apps/docs/static/img/v3.36.3/v3.36.3.png b/apps/docs/static/img/v3.36.3/v3.36.3.png new file mode 100644 index 00000000000..279a2f04190 Binary files /dev/null and b/apps/docs/static/img/v3.36.3/v3.36.3.png differ diff --git a/apps/docs/static/img/v3.36.4/v3.36.4.png b/apps/docs/static/img/v3.36.4/v3.36.4.png new file mode 100644 index 00000000000..914765bfb8d Binary files /dev/null and b/apps/docs/static/img/v3.36.4/v3.36.4.png differ diff --git a/apps/docs/static/img/v3.36.5/v3.36.5.png b/apps/docs/static/img/v3.36.5/v3.36.5.png new file mode 100644 index 00000000000..058fc031052 Binary files /dev/null and b/apps/docs/static/img/v3.36.5/v3.36.5.png differ diff --git a/apps/docs/static/img/v3.36.6/v3.36.6.png b/apps/docs/static/img/v3.36.6/v3.36.6.png new file mode 100644 index 00000000000..0edb7428b92 Binary files /dev/null and b/apps/docs/static/img/v3.36.6/v3.36.6.png differ diff --git a/apps/docs/static/img/v3.36.9/v3.36.9.png b/apps/docs/static/img/v3.36.9/v3.36.9.png new file mode 100644 index 00000000000..b63ccb5f891 Binary files /dev/null and b/apps/docs/static/img/v3.36.9/v3.36.9.png differ diff --git a/apps/docs/static/img/v3.37.0/v3.37.0.png b/apps/docs/static/img/v3.37.0/v3.37.0.png new file mode 100644 index 00000000000..2bee909c5b9 Binary files /dev/null and b/apps/docs/static/img/v3.37.0/v3.37.0.png differ diff --git a/apps/docs/static/img/v3.37.1/v3.37.1.png b/apps/docs/static/img/v3.37.1/v3.37.1.png new file mode 100644 index 00000000000..586f4821db6 Binary files /dev/null and b/apps/docs/static/img/v3.37.1/v3.37.1.png differ diff --git a/apps/docs/static/img/v3.37/v3.37-1.png b/apps/docs/static/img/v3.37/v3.37-1.png new file mode 100644 index 00000000000..e00eba37d9b Binary files /dev/null and b/apps/docs/static/img/v3.37/v3.37-1.png differ diff --git a/apps/docs/static/img/v3.37/v3.37-2.png b/apps/docs/static/img/v3.37/v3.37-2.png new file mode 100644 index 00000000000..44ef150a6c0 Binary files /dev/null and b/apps/docs/static/img/v3.37/v3.37-2.png differ diff --git a/apps/docs/static/img/v3.37/v3.37.png b/apps/docs/static/img/v3.37/v3.37.png new file mode 100644 index 00000000000..469ff66ae62 Binary files /dev/null and b/apps/docs/static/img/v3.37/v3.37.png differ diff --git a/apps/docs/static/img/v3.38.0/v3.38.0.png b/apps/docs/static/img/v3.38.0/v3.38.0.png new file mode 100644 index 00000000000..46683a26df6 Binary files /dev/null and b/apps/docs/static/img/v3.38.0/v3.38.0.png differ diff --git a/apps/docs/static/img/v3.38.1/v3.38.1.png b/apps/docs/static/img/v3.38.1/v3.38.1.png new file mode 100644 index 00000000000..d77506be2cc Binary files /dev/null and b/apps/docs/static/img/v3.38.1/v3.38.1.png differ diff --git a/apps/docs/static/img/v3.38.2/v3.38.2.png b/apps/docs/static/img/v3.38.2/v3.38.2.png new file mode 100644 index 00000000000..d1e8f06d2f9 Binary files /dev/null and b/apps/docs/static/img/v3.38.2/v3.38.2.png differ diff --git a/apps/docs/static/img/v3.39.0/v3.39.0.png b/apps/docs/static/img/v3.39.0/v3.39.0.png new file mode 100644 index 00000000000..4f71720928d Binary files /dev/null and b/apps/docs/static/img/v3.39.0/v3.39.0.png differ diff --git a/apps/docs/static/img/v3.39.3/v3.39.3.png b/apps/docs/static/img/v3.39.3/v3.39.3.png new file mode 100644 index 00000000000..f8dcd92b698 Binary files /dev/null and b/apps/docs/static/img/v3.39.3/v3.39.3.png differ diff --git a/apps/docs/static/img/v3.40.0/v3.40.0.png b/apps/docs/static/img/v3.40.0/v3.40.0.png new file mode 100644 index 00000000000..32f2e717115 Binary files /dev/null and b/apps/docs/static/img/v3.40.0/v3.40.0.png differ diff --git a/apps/docs/static/img/v3.41.0/v3.41.0.png b/apps/docs/static/img/v3.41.0/v3.41.0.png new file mode 100644 index 00000000000..069858f2ddd Binary files /dev/null and b/apps/docs/static/img/v3.41.0/v3.41.0.png differ diff --git a/apps/docs/static/img/v3.41.1/v3.41.1.png b/apps/docs/static/img/v3.41.1/v3.41.1.png new file mode 100644 index 00000000000..c07c05aa6e2 Binary files /dev/null and b/apps/docs/static/img/v3.41.1/v3.41.1.png differ diff --git a/apps/docs/static/img/v3.42.0/v3.42.0.png b/apps/docs/static/img/v3.42.0/v3.42.0.png new file mode 100644 index 00000000000..80bb7ffa359 Binary files /dev/null and b/apps/docs/static/img/v3.42.0/v3.42.0.png differ diff --git a/apps/docs/static/img/v3.43.0/v3.43.0.png b/apps/docs/static/img/v3.43.0/v3.43.0.png new file mode 100644 index 00000000000..b38ad925cc3 Binary files /dev/null and b/apps/docs/static/img/v3.43.0/v3.43.0.png differ diff --git a/apps/docs/static/img/v3.44.0/v3.44.0.png b/apps/docs/static/img/v3.44.0/v3.44.0.png new file mode 100644 index 00000000000..ca92998b3c2 Binary files /dev/null and b/apps/docs/static/img/v3.44.0/v3.44.0.png differ diff --git a/apps/docs/static/img/v3.45.0/v3.45.0.png b/apps/docs/static/img/v3.45.0/v3.45.0.png new file mode 100644 index 00000000000..53e2016420f Binary files /dev/null and b/apps/docs/static/img/v3.45.0/v3.45.0.png differ diff --git a/apps/docs/static/img/v3.46.0/v3.46.0.png b/apps/docs/static/img/v3.46.0/v3.46.0.png new file mode 100644 index 00000000000..10aa0cf20c8 Binary files /dev/null and b/apps/docs/static/img/v3.46.0/v3.46.0.png differ diff --git a/apps/docs/static/img/v3.47.0/v3.47.0.png b/apps/docs/static/img/v3.47.0/v3.47.0.png new file mode 100644 index 00000000000..bc5460933ef Binary files /dev/null and b/apps/docs/static/img/v3.47.0/v3.47.0.png differ diff --git a/apps/docs/static/img/your-first-task/your-first-task-6.png b/apps/docs/static/img/your-first-task/your-first-task-6.png new file mode 100644 index 00000000000..93b88a043c9 Binary files /dev/null and b/apps/docs/static/img/your-first-task/your-first-task-6.png differ diff --git a/apps/docs/static/img/your-first-task/your-first-task-7.png b/apps/docs/static/img/your-first-task/your-first-task-7.png new file mode 100644 index 00000000000..01291cd8dbb Binary files /dev/null and b/apps/docs/static/img/your-first-task/your-first-task-7.png differ diff --git a/apps/docs/static/img/your-first-task/your-first-task-8.png b/apps/docs/static/img/your-first-task/your-first-task-8.png new file mode 100644 index 00000000000..e7fc32506d2 Binary files /dev/null and b/apps/docs/static/img/your-first-task/your-first-task-8.png differ diff --git a/apps/docs/static/img/your-first-task/your-first-task.png b/apps/docs/static/img/your-first-task/your-first-task.png new file mode 100644 index 00000000000..5a3cd874ddc Binary files /dev/null and b/apps/docs/static/img/your-first-task/your-first-task.png differ diff --git a/apps/docs/static/robots.txt b/apps/docs/static/robots.txt new file mode 100644 index 00000000000..725d0397439 --- /dev/null +++ b/apps/docs/static/robots.txt @@ -0,0 +1,3 @@ +User-agent: * +Allow: / +Sitemap: https://roocodeinc.github.io/Roo-Code/sitemap.xml diff --git a/apps/docs/static/ui/arrow-left.svg b/apps/docs/static/ui/arrow-left.svg new file mode 100644 index 00000000000..d31609649c9 --- /dev/null +++ b/apps/docs/static/ui/arrow-left.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/docs/static/ui/arrow-right.svg b/apps/docs/static/ui/arrow-right.svg new file mode 100644 index 00000000000..8405ae26f26 --- /dev/null +++ b/apps/docs/static/ui/arrow-right.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/docs/static/ui/check.svg b/apps/docs/static/ui/check.svg new file mode 100644 index 00000000000..6e390aaca89 --- /dev/null +++ b/apps/docs/static/ui/check.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/docs/static/ui/x.svg b/apps/docs/static/ui/x.svg new file mode 100644 index 00000000000..eb194fd2e6c --- /dev/null +++ b/apps/docs/static/ui/x.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/docs/tsconfig.json b/apps/docs/tsconfig.json new file mode 100644 index 00000000000..712d200a37b --- /dev/null +++ b/apps/docs/tsconfig.json @@ -0,0 +1,8 @@ +{ + // This file is not used in compilation. It is here just for a nice editor experience. + "extends": "@docusaurus/tsconfig", + "compilerOptions": { + "baseUrl": "." + }, + "exclude": [".docusaurus", "build"] +} diff --git a/apps/docs/turbo.json b/apps/docs/turbo.json new file mode 100644 index 00000000000..1aa4810cc96 --- /dev/null +++ b/apps/docs/turbo.json @@ -0,0 +1,18 @@ +{ + "$schema": "https://turbo.build/schema.json", + "extends": ["//"], + "tasks": { + "build": { + "outputs": ["build/**"], + "inputs": [ + "docs/**", + "src/**", + "static/**", + "docusaurus.config.ts", + "sidebars.ts", + "package.json", + "tsconfig.json" + ] + } + } +} diff --git a/apps/web-evals/.env b/apps/web-evals/.env deleted file mode 100644 index 1bb6dd6dacb..00000000000 --- a/apps/web-evals/.env +++ /dev/null @@ -1 +0,0 @@ -DATABASE_URL=postgres://postgres:password@localhost:5433/evals_development diff --git a/apps/web-evals/.gitignore b/apps/web-evals/.gitignore deleted file mode 100644 index 443f3159ed3..00000000000 --- a/apps/web-evals/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -# .env -!.env - -# next.js -.next - -# typescript -tsconfig.tsbuildinfo diff --git a/apps/web-evals/CHANGELOG.md b/apps/web-evals/CHANGELOG.md deleted file mode 100644 index b3531905ac1..00000000000 --- a/apps/web-evals/CHANGELOG.md +++ /dev/null @@ -1,3 +0,0 @@ -# @roo-code/web-evals - -## 0.0.1 diff --git a/apps/web-evals/components.json b/apps/web-evals/components.json deleted file mode 100644 index 5bcedb31416..00000000000 --- a/apps/web-evals/components.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "$schema": "https://ui.shadcn.com/schema.json", - "style": "new-york", - "rsc": true, - "tsx": true, - "tailwind": { - "config": "", - "css": "src/app/globals.css", - "baseColor": "neutral", - "cssVariables": true, - "prefix": "" - }, - "aliases": { - "components": "@/components", - "utils": "@/lib/utils", - "ui": "@/components/ui", - "lib": "@/lib", - "hooks": "@/hooks" - }, - "iconLibrary": "lucide" -} diff --git a/apps/web-evals/eslint.config.mjs b/apps/web-evals/eslint.config.mjs deleted file mode 100644 index 024d6157d43..00000000000 --- a/apps/web-evals/eslint.config.mjs +++ /dev/null @@ -1,17 +0,0 @@ -import { nextJsConfig } from "@roo-code/config-eslint/next-js" - -/** @type {import("eslint").Linter.Config} */ -export default [ - ...nextJsConfig, - { - rules: { - "no-unused-vars": "off", - "@typescript-eslint/no-unused-vars": [ - "error", - { - caughtErrorsIgnorePattern: "^_", - }, - ], - }, - }, -] diff --git a/apps/web-evals/next-env.d.ts b/apps/web-evals/next-env.d.ts deleted file mode 100644 index 7506fe6afbc..00000000000 --- a/apps/web-evals/next-env.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -/// -/// -import "./.next/dev/types/routes.d.ts" - -// NOTE: This file should not be edited -// see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/apps/web-evals/next.config.ts b/apps/web-evals/next.config.ts deleted file mode 100644 index b5f54a87be8..00000000000 --- a/apps/web-evals/next.config.ts +++ /dev/null @@ -1,7 +0,0 @@ -import type { NextConfig } from "next" - -const nextConfig: NextConfig = { - turbopack: {}, -} - -export default nextConfig diff --git a/apps/web-evals/package.json b/apps/web-evals/package.json deleted file mode 100644 index 1723f575836..00000000000 --- a/apps/web-evals/package.json +++ /dev/null @@ -1,63 +0,0 @@ -{ - "name": "@roo-code/web-evals", - "version": "0.0.1", - "type": "module", - "scripts": { - "lint": "eslint src --ext=ts,tsx --max-warnings=0", - "check-types": "tsc -b", - "dev": "scripts/check-services.sh && next dev -p 3446", - "format": "prettier --write src", - "build": "next build", - "start": "next start -p 3446", - "clean": "rimraf tsconfig.tsbuildinfo .next .turbo" - }, - "dependencies": { - "@hookform/resolvers": "^5.1.1", - "@radix-ui/react-alert-dialog": "^1.1.7", - "@radix-ui/react-checkbox": "^1.1.5", - "@radix-ui/react-dialog": "^1.1.6", - "@radix-ui/react-dropdown-menu": "^2.1.7", - "@radix-ui/react-label": "^2.1.2", - "@radix-ui/react-popover": "^1.1.6", - "@radix-ui/react-scroll-area": "^1.2.3", - "@radix-ui/react-select": "^2.1.6", - "@radix-ui/react-separator": "^1.1.2", - "@radix-ui/react-slider": "^1.2.4", - "@radix-ui/react-slot": "^1.1.2", - "@radix-ui/react-tabs": "^1.1.3", - "@radix-ui/react-tooltip": "^1.2.8", - "@roo-code/evals": "workspace:^", - "@roo-code/types": "workspace:^", - "@tanstack/react-query": "^5.69.0", - "archiver": "^7.0.1", - "class-variance-authority": "^0.7.1", - "clsx": "^2.1.1", - "cmdk": "^1.1.0", - "fuzzysort": "^3.1.0", - "lucide-react": "^0.518.0", - "next": "^16.1.6", - "next-themes": "^0.4.6", - "p-map": "^7.0.3", - "react": "^18.3.1", - "react-dom": "^18.3.1", - "react-hook-form": "^7.57.0", - "react-use": "^17.6.0", - "redis": "^5.5.5", - "sonner": "^2.0.5", - "tailwind-merge": "^3.3.0", - "tailwindcss-animate": "^1.0.7", - "vaul": "^1.1.2", - "zod": "^3.25.61" - }, - "devDependencies": { - "@roo-code/config-eslint": "workspace:^", - "@roo-code/config-typescript": "workspace:^", - "@tailwindcss/postcss": "^4", - "@types/archiver": "^7.0.0", - "@types/ps-tree": "^1.1.6", - "@types/react": "^18.3.23", - "@types/react-dom": "^18.3.5", - "tailwindcss": "^4", - "vitest": "^3.2.3" - } -} diff --git a/apps/web-evals/postcss.config.mjs b/apps/web-evals/postcss.config.mjs deleted file mode 100644 index 78452aadce7..00000000000 --- a/apps/web-evals/postcss.config.mjs +++ /dev/null @@ -1,5 +0,0 @@ -const config = { - plugins: ["@tailwindcss/postcss"], -} - -export default config diff --git a/apps/web-evals/public/.gitkeep b/apps/web-evals/public/.gitkeep deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/apps/web-evals/scripts/check-services.sh b/apps/web-evals/scripts/check-services.sh deleted file mode 100755 index d72ffd54e8d..00000000000 --- a/apps/web-evals/scripts/check-services.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash - -if ! docker info &> /dev/null; then - echo "❌ Docker is not running. Please start Docker Desktop and try again." - exit 1 -fi - -if ! nc -z postgres 5433 2>/dev/null; then - echo "❌ PostgreSQL is not running on port 5432" - echo "💡 Start it with: pnpm --filter @roo-code/evals db:up" - exit 1 -fi - -if ! nc -z redis 6380 2>/dev/null; then - echo "❌ Redis is not running on port 6379" - echo "💡 Start it with: pnpm --filter @roo-code/evals redis:up" - exit 1 -fi - -echo "✅ All required services are running" diff --git a/apps/web-evals/src/actions/__tests__/killRun.spec.ts b/apps/web-evals/src/actions/__tests__/killRun.spec.ts deleted file mode 100644 index 814d70d9fca..00000000000 --- a/apps/web-evals/src/actions/__tests__/killRun.spec.ts +++ /dev/null @@ -1,207 +0,0 @@ -// npx vitest run src/actions/__tests__/killRun.spec.ts - -import { execFileSync } from "child_process" - -// Mock child_process -vi.mock("child_process", () => ({ - execFileSync: vi.fn(), - spawn: vi.fn(), -})) - -// Mock next/cache -vi.mock("next/cache", () => ({ - revalidatePath: vi.fn(), -})) - -// Mock redis client -vi.mock("@/lib/server/redis", () => ({ - redisClient: vi.fn().mockResolvedValue({ - del: vi.fn().mockResolvedValue(1), - }), -})) - -// Mock @roo-code/evals -vi.mock("@roo-code/evals", () => ({ - createRun: vi.fn(), - deleteRun: vi.fn(), - createTask: vi.fn(), - exerciseLanguages: [], - getExercisesForLanguage: vi.fn().mockResolvedValue([]), -})) - -// Mock timers to speed up tests -vi.useFakeTimers() - -// Import after mocks -import { killRun } from "../runs" - -const mockExecFileSync = execFileSync as ReturnType - -describe("killRun", () => { - beforeEach(() => { - vi.clearAllMocks() - }) - - afterEach(() => { - vi.clearAllTimers() - }) - - it("should kill controller first, wait, then kill task containers", async () => { - const runId = 123 - - // execFileSync is used for all docker commands - mockExecFileSync - .mockReturnValueOnce("") // docker kill controller - .mockReturnValueOnce("evals-task-123-456.0\nevals-task-123-789.1\n") // docker ps - .mockReturnValueOnce("") // docker kill evals-task-123-456.0 - .mockReturnValueOnce("") // docker kill evals-task-123-789.1 - - const resultPromise = killRun(runId) - - // Fast-forward past the 10 second sleep - await vi.advanceTimersByTimeAsync(10000) - - const result = await resultPromise - - expect(result.success).toBe(true) - expect(result.killedContainers).toContain("evals-controller-123") - expect(result.killedContainers).toContain("evals-task-123-456.0") - expect(result.killedContainers).toContain("evals-task-123-789.1") - expect(result.errors).toHaveLength(0) - - // Verify execFileSync was called for docker kill - expect(mockExecFileSync).toHaveBeenNthCalledWith( - 1, - "docker", - ["kill", "evals-controller-123"], - expect.any(Object), - ) - // Verify execFileSync was called for docker ps with run-specific filter - expect(mockExecFileSync).toHaveBeenNthCalledWith( - 2, - "docker", - ["ps", "--format", "{{.Names}}", "--filter", "name=evals-task-123-"], - expect.any(Object), - ) - }) - - it("should continue killing runners even if controller is not running", async () => { - const runId = 456 - - mockExecFileSync - .mockImplementationOnce(() => { - throw new Error("No such container") - }) // controller kill fails - .mockReturnValueOnce("evals-task-456-100.0\n") // docker ps - .mockReturnValueOnce("") // docker kill task - - const resultPromise = killRun(runId) - await vi.advanceTimersByTimeAsync(10000) - const result = await resultPromise - - expect(result.success).toBe(true) - expect(result.killedContainers).toContain("evals-task-456-100.0") - // Controller not in list since it failed - expect(result.killedContainers).not.toContain("evals-controller-456") - }) - - it("should clear Redis state after killing containers", async () => { - const runId = 789 - - const mockDel = vi.fn().mockResolvedValue(1) - const { redisClient } = await import("@/lib/server/redis") - vi.mocked(redisClient).mockResolvedValue({ del: mockDel } as never) - - mockExecFileSync - .mockReturnValueOnce("") // controller kill - .mockReturnValueOnce("") // docker ps (no tasks) - - const resultPromise = killRun(runId) - await vi.advanceTimersByTimeAsync(10000) - await resultPromise - - expect(mockDel).toHaveBeenCalledWith("heartbeat:789") - expect(mockDel).toHaveBeenCalledWith("runners:789") - }) - - it("should handle docker ps failure gracefully", async () => { - const runId = 111 - - mockExecFileSync - .mockReturnValueOnce("") // controller kill succeeds - .mockImplementationOnce(() => { - throw new Error("Docker error") - }) // docker ps fails - - const resultPromise = killRun(runId) - await vi.advanceTimersByTimeAsync(10000) - const result = await resultPromise - - // Should still be successful because controller was killed - expect(result.success).toBe(true) - expect(result.killedContainers).toContain("evals-controller-111") - expect(result.errors).toContain("Failed to list Docker task containers") - }) - - it("should handle individual task kill failures", async () => { - const runId = 222 - - mockExecFileSync - .mockReturnValueOnce("") // controller kill - .mockReturnValueOnce("evals-task-222-300.0\nevals-task-222-400.0\n") // docker ps - .mockImplementationOnce(() => { - throw new Error("Kill failed") - }) // first task kill fails - .mockReturnValueOnce("") // second task kill succeeds - - const resultPromise = killRun(runId) - await vi.advanceTimersByTimeAsync(10000) - const result = await resultPromise - - expect(result.success).toBe(true) - expect(result.killedContainers).toContain("evals-controller-222") - expect(result.killedContainers).toContain("evals-task-222-400.0") - expect(result.errors.length).toBe(1) - expect(result.errors[0]).toContain("evals-task-222-300.0") - }) - - it("should return success with no containers when nothing is running", async () => { - const runId = 333 - - mockExecFileSync - .mockImplementationOnce(() => { - throw new Error("No such container") - }) // controller not running - .mockReturnValueOnce("") // no task containers - - const resultPromise = killRun(runId) - await vi.advanceTimersByTimeAsync(10000) - const result = await resultPromise - - expect(result.success).toBe(true) - expect(result.killedContainers).toHaveLength(0) - expect(result.errors).toHaveLength(0) - }) - - it("should only kill containers belonging to the specific run", async () => { - const runId = 555 - - mockExecFileSync - .mockReturnValueOnce("") // controller kill - .mockReturnValueOnce("evals-task-555-100.0\n") // docker ps - .mockReturnValueOnce("") // docker kill task - - const resultPromise = killRun(runId) - await vi.advanceTimersByTimeAsync(10000) - const result = await resultPromise - - expect(result.success).toBe(true) - // Verify execFileSync was called for docker ps with run-specific filter - expect(mockExecFileSync).toHaveBeenNthCalledWith( - 2, - "docker", - ["ps", "--format", "{{.Names}}", "--filter", "name=evals-task-555-"], - expect.any(Object), - ) - }) -}) diff --git a/apps/web-evals/src/actions/exercises.ts b/apps/web-evals/src/actions/exercises.ts deleted file mode 100644 index 17eb1ff085e..00000000000 --- a/apps/web-evals/src/actions/exercises.ts +++ /dev/null @@ -1,22 +0,0 @@ -"use server" - -import * as path from "path" -import { fileURLToPath } from "url" - -import { exerciseLanguages, listDirectories } from "@roo-code/evals" - -const __dirname = path.dirname(fileURLToPath(import.meta.url)) // /apps/web-evals/src/actions - -const EVALS_REPO_PATH = path.resolve(__dirname, "../../../../../evals") - -export const getExercises = async () => { - const result = await Promise.all( - exerciseLanguages.map(async (language) => { - const languagePath = path.join(EVALS_REPO_PATH, language) - const exercises = await listDirectories(__dirname, languagePath) - return exercises.map((exercise) => `${language}/${exercise}`) - }), - ) - - return result.flat() -} diff --git a/apps/web-evals/src/actions/heartbeat.ts b/apps/web-evals/src/actions/heartbeat.ts deleted file mode 100644 index a74aa8ee64e..00000000000 --- a/apps/web-evals/src/actions/heartbeat.ts +++ /dev/null @@ -1,8 +0,0 @@ -"use server" - -import { redisClient } from "@/lib/server/redis" - -export const getHeartbeat = async (runId: number) => { - const redis = await redisClient() - return redis.get(`heartbeat:${runId}`) -} diff --git a/apps/web-evals/src/actions/runners.ts b/apps/web-evals/src/actions/runners.ts deleted file mode 100644 index 8b7e86b0f3e..00000000000 --- a/apps/web-evals/src/actions/runners.ts +++ /dev/null @@ -1,8 +0,0 @@ -"use server" - -import { redisClient } from "@/lib/server/redis" - -export const getRunners = async (runId: number) => { - const redis = await redisClient() - return redis.sMembers(`runners:${runId}`) -} diff --git a/apps/web-evals/src/actions/runs.ts b/apps/web-evals/src/actions/runs.ts deleted file mode 100644 index f0c1578aed1..00000000000 --- a/apps/web-evals/src/actions/runs.ts +++ /dev/null @@ -1,377 +0,0 @@ -"use server" - -import * as path from "path" -import fs from "fs" -import { fileURLToPath } from "url" -import { spawn, execFileSync } from "child_process" - -import { revalidatePath } from "next/cache" -import pMap from "p-map" - -import { - type ExerciseLanguage, - exerciseLanguages, - createRun as _createRun, - deleteRun as _deleteRun, - updateRun as _updateRun, - getIncompleteRuns as _getIncompleteRuns, - deleteRunsByIds as _deleteRunsByIds, - createTask, - getExercisesForLanguage, -} from "@roo-code/evals" - -import { CreateRun } from "@/lib/schemas" -import { redisClient } from "@/lib/server/redis" - -// Storage base path for eval logs -const EVALS_STORAGE_PATH = "/tmp/evals/runs" - -const EVALS_REPO_PATH = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../../../../../evals") - -export async function createRun({ - suite, - exercises = [], - timeout, - iterations = 1, - executionMethod = "vscode", - ...values -}: CreateRun) { - const run = await _createRun({ - ...values, - timeout, - executionMethod, - socketPath: "", // TODO: Get rid of this. - }) - - if (suite === "partial") { - for (const path of exercises) { - const [language, exercise] = path.split("/") - - if (!language || !exercise) { - throw new Error("Invalid exercise path: " + path) - } - - // Create multiple tasks for each iteration - for (let iteration = 1; iteration <= iterations; iteration++) { - await createTask({ - ...values, - runId: run.id, - language: language as ExerciseLanguage, - exercise, - iteration, - }) - } - } - } else { - for (const language of exerciseLanguages) { - const languageExercises = await getExercisesForLanguage(EVALS_REPO_PATH, language) - - // Create tasks for all iterations of each exercise - const tasksToCreate: Array<{ language: ExerciseLanguage; exercise: string; iteration: number }> = [] - for (const exercise of languageExercises) { - for (let iteration = 1; iteration <= iterations; iteration++) { - tasksToCreate.push({ language, exercise, iteration }) - } - } - - await pMap( - tasksToCreate, - ({ language, exercise, iteration }) => createTask({ runId: run.id, language, exercise, iteration }), - { concurrency: 10 }, - ) - } - } - - revalidatePath("/runs") - - try { - const isRunningInDocker = fs.existsSync("/.dockerenv") - - const dockerArgs = [ - `--name evals-controller-${run.id}`, - "--rm", - "--network evals_default", - "-v /var/run/docker.sock:/var/run/docker.sock", - "-v /tmp/evals:/var/log/evals", - "-e HOST_EXECUTION_METHOD=docker", - ] - - const cliCommand = `pnpm --filter @roo-code/evals cli --runId ${run.id}` - - const command = isRunningInDocker - ? `docker run ${dockerArgs.join(" ")} evals-runner sh -c "${cliCommand}"` - : cliCommand - - console.log("spawn ->", command) - - const childProcess = spawn("sh", ["-c", command], { - detached: true, - stdio: ["ignore", "pipe", "pipe"], - }) - - const logStream = fs.createWriteStream("/tmp/roo-code-evals.log", { flags: "a" }) - - if (childProcess.stdout) { - childProcess.stdout.pipe(logStream) - } - - if (childProcess.stderr) { - childProcess.stderr.pipe(logStream) - } - - childProcess.unref() - } catch (error) { - console.error(error) - } - - return run -} - -export async function deleteRun(runId: number) { - await _deleteRun(runId) - revalidatePath("/runs") -} - -export type KillRunResult = { - success: boolean - killedContainers: string[] - errors: string[] -} - -const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)) - -/** - * Kill all Docker containers associated with a run (controller and task runners). - * Kills the controller first, waits 10 seconds, then kills runners. - * Also clears Redis state for heartbeat and runners. - * - * Container naming conventions: - * - Controller: evals-controller-{runId} - * - Task runners: evals-task-{runId}-{taskId}.{attempt} - */ -export async function killRun(runId: number): Promise { - const killedContainers: string[] = [] - const errors: string[] = [] - const controllerPattern = `evals-controller-${runId}` - const taskPattern = `evals-task-${runId}-` - - try { - // Step 1: Kill the controller first - console.log(`Killing controller: ${controllerPattern}`) - try { - execFileSync("docker", ["kill", controllerPattern], { encoding: "utf-8", timeout: 10000 }) - killedContainers.push(controllerPattern) - console.log(`Killed controller container: ${controllerPattern}`) - } catch (_error) { - // Controller might not be running - that's ok, continue to kill runners - console.log(`Controller ${controllerPattern} not running or already stopped`) - } - - // Step 2: Wait 10 seconds before killing runners - console.log("Waiting 10 seconds before killing runners...") - await sleep(10000) - - // Step 3: Find and kill all task runner containers for THIS run only - let taskContainerNames: string[] = [] - - try { - const output = execFileSync("docker", ["ps", "--format", "{{.Names}}", "--filter", `name=${taskPattern}`], { - encoding: "utf-8", - timeout: 10000, - }) - taskContainerNames = output - .split("\n") - .map((name) => name.trim()) - .filter((name) => name.length > 0 && name.startsWith(taskPattern)) - } catch (error) { - console.error("Failed to list task containers:", error) - errors.push("Failed to list Docker task containers") - } - - // Kill each task runner container - for (const containerName of taskContainerNames) { - try { - execFileSync("docker", ["kill", containerName], { encoding: "utf-8", timeout: 10000 }) - killedContainers.push(containerName) - console.log(`Killed task container: ${containerName}`) - } catch (error) { - // Container might have already stopped - console.error(`Failed to kill container ${containerName}:`, error) - errors.push(`Failed to kill container: ${containerName}`) - } - } - - // Step 4: Clear Redis state - try { - const redis = await redisClient() - const heartbeatKey = `heartbeat:${runId}` - const runnersKey = `runners:${runId}` - - await redis.del(heartbeatKey) - await redis.del(runnersKey) - console.log(`Cleared Redis keys: ${heartbeatKey}, ${runnersKey}`) - } catch (error) { - console.error("Failed to clear Redis state:", error) - errors.push("Failed to clear Redis state") - } - } catch (error) { - console.error("Error in killRun:", error) - errors.push("Unexpected error while killing containers") - } - - revalidatePath(`/runs/${runId}`) - revalidatePath("/runs") - - return { - success: killedContainers.length > 0 || errors.length === 0, - killedContainers, - errors, - } -} - -export type DeleteIncompleteRunsResult = { - success: boolean - deletedCount: number - deletedRunIds: number[] - storageErrors: string[] -} - -/** - * Delete all incomplete runs (runs without a taskMetricsId/final score). - * Removes both database records and storage folders. - */ -export async function deleteIncompleteRuns(): Promise { - const storageErrors: string[] = [] - - // Get all incomplete runs - const incompleteRuns = await _getIncompleteRuns() - const runIds = incompleteRuns.map((run) => run.id) - - if (runIds.length === 0) { - return { - success: true, - deletedCount: 0, - deletedRunIds: [], - storageErrors: [], - } - } - - // Delete storage folders for each run - for (const runId of runIds) { - const storagePath = path.join(EVALS_STORAGE_PATH, String(runId)) - try { - if (fs.existsSync(storagePath)) { - fs.rmSync(storagePath, { recursive: true, force: true }) - console.log(`Deleted storage folder: ${storagePath}`) - } - } catch (error) { - console.error(`Failed to delete storage folder ${storagePath}:`, error) - storageErrors.push(`Failed to delete storage for run ${runId}`) - } - - // Also try to clear Redis state for any potentially running incomplete runs - try { - const redis = await redisClient() - await redis.del(`heartbeat:${runId}`) - await redis.del(`runners:${runId}`) - } catch (error) { - // Non-critical error, just log it - console.error(`Failed to clear Redis state for run ${runId}:`, error) - } - } - - // Delete from database - await _deleteRunsByIds(runIds) - - revalidatePath("/runs") - - return { - success: true, - deletedCount: runIds.length, - deletedRunIds: runIds, - storageErrors, - } -} - -/** - * Get count of incomplete runs (for UI display) - */ -export async function getIncompleteRunsCount(): Promise { - const incompleteRuns = await _getIncompleteRuns() - return incompleteRuns.length -} - -/** - * Delete all runs older than 30 days. - * Removes both database records and storage folders. - */ -export async function deleteOldRuns(): Promise { - const storageErrors: string[] = [] - - // Get all runs older than 30 days - const thirtyDaysAgo = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000) - const { getRuns } = await import("@roo-code/evals") - const allRuns = await getRuns() - const oldRuns = allRuns.filter((run) => run.createdAt < thirtyDaysAgo) - const runIds = oldRuns.map((run) => run.id) - - if (runIds.length === 0) { - return { - success: true, - deletedCount: 0, - deletedRunIds: [], - storageErrors: [], - } - } - - // Delete storage folders for each run - for (const runId of runIds) { - const storagePath = path.join(EVALS_STORAGE_PATH, String(runId)) - try { - if (fs.existsSync(storagePath)) { - fs.rmSync(storagePath, { recursive: true, force: true }) - console.log(`Deleted storage folder: ${storagePath}`) - } - } catch (error) { - console.error(`Failed to delete storage folder ${storagePath}:`, error) - storageErrors.push(`Failed to delete storage for run ${runId}`) - } - - // Also try to clear Redis state - try { - const redis = await redisClient() - await redis.del(`heartbeat:${runId}`) - await redis.del(`runners:${runId}`) - } catch (error) { - // Non-critical error, just log it - console.error(`Failed to clear Redis state for run ${runId}:`, error) - } - } - - // Delete from database - await _deleteRunsByIds(runIds) - - revalidatePath("/runs") - - return { - success: true, - deletedCount: runIds.length, - deletedRunIds: runIds, - storageErrors, - } -} - -/** - * Update the description of a run. - */ -export async function updateRunDescription(runId: number, description: string | null): Promise<{ success: boolean }> { - try { - await _updateRun(runId, { description }) - revalidatePath("/runs") - revalidatePath(`/runs/${runId}`) - return { success: true } - } catch (error) { - console.error("Failed to update run description:", error) - return { success: false } - } -} diff --git a/apps/web-evals/src/actions/tasks.ts b/apps/web-evals/src/actions/tasks.ts deleted file mode 100644 index 18b428b0cad..00000000000 --- a/apps/web-evals/src/actions/tasks.ts +++ /dev/null @@ -1,11 +0,0 @@ -"use server" - -import { revalidatePath } from "next/cache" - -import { getTasks as _getTasks } from "@roo-code/evals" - -export async function getTasks(runId: number) { - const tasks = await _getTasks(runId) - revalidatePath(`/runs/${runId}`) - return tasks -} diff --git a/apps/web-evals/src/app/api/runs/[id]/logs/[taskId]/route.ts b/apps/web-evals/src/app/api/runs/[id]/logs/[taskId]/route.ts deleted file mode 100644 index e5ec8751ab0..00000000000 --- a/apps/web-evals/src/app/api/runs/[id]/logs/[taskId]/route.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { NextResponse } from "next/server" -import type { NextRequest } from "next/server" -import * as fs from "node:fs/promises" -import * as path from "node:path" - -import { findTask, findRun } from "@roo-code/evals" - -export const dynamic = "force-dynamic" - -const LOG_BASE_PATH = "/tmp/evals/runs" - -// Sanitize path components to prevent path traversal attacks -function sanitizePathComponent(component: string): string { - // Remove any path separators, null bytes, and other dangerous characters - return component.replace(/[/\\:\0*?"<>|]/g, "_") -} - -export async function GET(request: NextRequest, { params }: { params: Promise<{ id: string; taskId: string }> }) { - const { id, taskId } = await params - - try { - const runId = Number(id) - const taskIdNum = Number(taskId) - - if (isNaN(runId) || isNaN(taskIdNum)) { - return NextResponse.json({ error: "Invalid run ID or task ID" }, { status: 400 }) - } - - // Verify the run exists - await findRun(runId) - - // Get the task to find its language and exercise - const task = await findTask(taskIdNum) - - // Verify the task belongs to this run - if (task.runId !== runId) { - return NextResponse.json({ error: "Task does not belong to this run" }, { status: 404 }) - } - - // Sanitize language and exercise to prevent path traversal - const safeLanguage = sanitizePathComponent(task.language) - const safeExercise = sanitizePathComponent(task.exercise) - - // Construct the log file path - const logFileName = `${safeLanguage}-${safeExercise}.log` - const logFilePath = path.join(LOG_BASE_PATH, String(runId), logFileName) - - // Verify the resolved path is within the expected directory (defense in depth) - const resolvedPath = path.resolve(logFilePath) - const expectedBase = path.resolve(LOG_BASE_PATH) - if (!resolvedPath.startsWith(expectedBase)) { - return NextResponse.json({ error: "Invalid log path" }, { status: 400 }) - } - - // Check if the log file exists and read it (async) - try { - const logContent = await fs.readFile(logFilePath, "utf-8") - return NextResponse.json({ logContent }) - } catch (err) { - if ((err as NodeJS.ErrnoException).code === "ENOENT") { - return NextResponse.json({ error: "Log file not found", logContent: null }, { status: 200 }) - } - throw err - } - } catch (error) { - console.error("Error reading task log:", error) - - if (error instanceof Error && error.name === "RecordNotFoundError") { - return NextResponse.json({ error: "Task or run not found" }, { status: 404 }) - } - - return NextResponse.json({ error: "Failed to read log file" }, { status: 500 }) - } -} diff --git a/apps/web-evals/src/app/api/runs/[id]/logs/failed/route.ts b/apps/web-evals/src/app/api/runs/[id]/logs/failed/route.ts deleted file mode 100644 index 8b2760df987..00000000000 --- a/apps/web-evals/src/app/api/runs/[id]/logs/failed/route.ts +++ /dev/null @@ -1,147 +0,0 @@ -import { NextResponse } from "next/server" -import type { NextRequest } from "next/server" -import * as fs from "node:fs" -import * as path from "node:path" -import archiver from "archiver" - -import { findRun, getTasks } from "@roo-code/evals" - -export const dynamic = "force-dynamic" - -const LOG_BASE_PATH = "/tmp/evals/runs" - -// Sanitize path components to prevent path traversal attacks -function sanitizePathComponent(component: string): string { - // Remove any path separators, null bytes, and other dangerous characters - return component.replace(/[/\\:\0*?"<>|]/g, "_") -} - -export async function GET(request: NextRequest, { params }: { params: Promise<{ id: string }> }) { - const { id } = await params - - try { - const runId = Number(id) - - if (isNaN(runId)) { - return NextResponse.json({ error: "Invalid run ID" }, { status: 400 }) - } - - // Verify the run exists - await findRun(runId) - - // Get all tasks for this run - const tasks = await getTasks(runId) - - // Filter for failed tasks only - const failedTasks = tasks.filter((task) => task.passed === false) - - if (failedTasks.length === 0) { - return NextResponse.json({ error: "No failed tasks to export" }, { status: 400 }) - } - - // Create a zip archive - const archive = archiver("zip", { zlib: { level: 9 } }) - - // Collect chunks to build the response - const chunks: Buffer[] = [] - - archive.on("data", (chunk: Buffer) => { - chunks.push(chunk) - }) - - // Track archive errors - let archiveError: Error | null = null - archive.on("error", (err: Error) => { - archiveError = err - }) - - // Set up the end promise before finalizing (proper event listener ordering) - const archiveEndPromise = new Promise((resolve, reject) => { - archive.on("end", resolve) - archive.on("error", reject) - }) - - // Add each failed task's log file and history files to the archive - const logDir = path.join(LOG_BASE_PATH, String(runId)) - let filesAdded = 0 - - for (const task of failedTasks) { - // Sanitize language and exercise to prevent path traversal - const safeLanguage = sanitizePathComponent(task.language) - const safeExercise = sanitizePathComponent(task.exercise) - const expectedBase = path.resolve(LOG_BASE_PATH) - - // Add the log file - const logFileName = `${safeLanguage}-${safeExercise}.log` - const logFilePath = path.join(logDir, logFileName) - - // Verify the resolved path is within the expected directory (defense in depth) - const resolvedLogPath = path.resolve(logFilePath) - if (resolvedLogPath.startsWith(expectedBase) && fs.existsSync(logFilePath)) { - archive.file(logFilePath, { name: logFileName }) - filesAdded++ - } - - // Add the API conversation history file - // Format: {language}-{exercise}.{iteration}_api_conversation_history.json - const apiHistoryFileName = `${safeLanguage}-${safeExercise}.${task.iteration}_api_conversation_history.json` - const apiHistoryFilePath = path.join(logDir, apiHistoryFileName) - const resolvedApiHistoryPath = path.resolve(apiHistoryFilePath) - if (resolvedApiHistoryPath.startsWith(expectedBase) && fs.existsSync(apiHistoryFilePath)) { - archive.file(apiHistoryFilePath, { name: apiHistoryFileName }) - filesAdded++ - } - - // Add the UI messages file - // Format: {language}-{exercise}.{iteration}_ui_messages.json - const uiMessagesFileName = `${safeLanguage}-${safeExercise}.${task.iteration}_ui_messages.json` - const uiMessagesFilePath = path.join(logDir, uiMessagesFileName) - const resolvedUiMessagesPath = path.resolve(uiMessagesFilePath) - if (resolvedUiMessagesPath.startsWith(expectedBase) && fs.existsSync(uiMessagesFilePath)) { - archive.file(uiMessagesFilePath, { name: uiMessagesFileName }) - filesAdded++ - } - } - - // Check if any files were actually added - if (filesAdded === 0) { - archive.abort() - return NextResponse.json( - { error: "No log files found - they may have been cleared from disk" }, - { status: 404 }, - ) - } - - // Finalize the archive - await archive.finalize() - - // Wait for all data to be collected - await archiveEndPromise - - // Check for archive errors - if (archiveError) { - throw archiveError - } - - // Combine all chunks into a single buffer - const zipBuffer = Buffer.concat(chunks) - - // Return the zip file - return new NextResponse(zipBuffer, { - status: 200, - headers: { - "Content-Type": "application/zip", - "Content-Disposition": `attachment; filename="run-${runId}-failed-logs.zip"`, - "Content-Length": String(zipBuffer.length), - }, - }) - } catch (error) { - console.error("Error exporting failed logs:", error) - - if (error instanceof Error && error.name === "RecordNotFoundError") { - return NextResponse.json({ error: "Run not found" }, { status: 404 }) - } - - return NextResponse.json({ error: "Failed to export logs" }, { status: 500 }) - } -} diff --git a/apps/web-evals/src/app/api/runs/[id]/stream/route.ts b/apps/web-evals/src/app/api/runs/[id]/stream/route.ts deleted file mode 100644 index 3168974ecd4..00000000000 --- a/apps/web-evals/src/app/api/runs/[id]/stream/route.ts +++ /dev/null @@ -1,71 +0,0 @@ -import type { NextRequest } from "next/server" - -import { taskEventSchema } from "@roo-code/types" -import { findRun } from "@roo-code/evals" - -import { SSEStream } from "@/lib/server/sse-stream" -import { redisClient } from "@/lib/server/redis" - -export const dynamic = "force-dynamic" - -export async function GET(request: NextRequest, { params }: { params: Promise<{ id: string }> }) { - const { id } = await params - const requestId = crypto.randomUUID() - const stream = new SSEStream() - const run = await findRun(Number(id)) - const redis = await redisClient() - - let isStreamClosed = false - const channelName = `evals:${run.id}` - - const onMessage = async (data: string) => { - if (isStreamClosed || stream.isClosed) { - return - } - - try { - const taskEvent = taskEventSchema.parse(JSON.parse(data)) - // console.log(`[stream#${requestId}] task event -> ${taskEvent.eventName}`) - const writeSuccess = await stream.write(JSON.stringify(taskEvent)) - - if (!writeSuccess) { - await disconnect() - } - } catch (_error) { - console.error(`[stream#${requestId}] invalid task event:`, data) - } - } - - const disconnect = async () => { - if (isStreamClosed) { - return - } - - isStreamClosed = true - - try { - await redis.unsubscribe(channelName) - console.log(`[stream#${requestId}] unsubscribed from ${channelName}`) - } catch (error) { - console.error(`[stream#${requestId}] error unsubscribing:`, error) - } - - try { - await stream.close() - } catch (error) { - console.error(`[stream#${requestId}] error closing stream:`, error) - } - } - - await redis.subscribe(channelName, onMessage) - - request.signal.addEventListener("abort", () => { - console.log(`[stream#${requestId}] abort`) - - disconnect().catch((error) => { - console.error(`[stream#${requestId}] cleanup error:`, error) - }) - }) - - return stream.getResponse() -} diff --git a/apps/web-evals/src/app/favicon.ico b/apps/web-evals/src/app/favicon.ico deleted file mode 100644 index 718d6fea483..00000000000 Binary files a/apps/web-evals/src/app/favicon.ico and /dev/null differ diff --git a/apps/web-evals/src/app/globals.css b/apps/web-evals/src/app/globals.css deleted file mode 100644 index 8c12f0d1d2c..00000000000 --- a/apps/web-evals/src/app/globals.css +++ /dev/null @@ -1,141 +0,0 @@ -@import "tailwindcss"; - -@plugin "tailwindcss-animate"; - -@custom-variant dark (&:is(.dark *)); - -:root { - --radius: 0.625rem; - --background: oklch(1 0 0); - --foreground: oklch(0.145 0 0); - --card: oklch(1 0 0); - --card-foreground: oklch(0.145 0 0); - --popover: oklch(1 0 0); - --popover-foreground: oklch(0.145 0 0); - --primary: oklch(0.205 0 0); - --primary-foreground: oklch(0.985 0 0); - --secondary: oklch(0.97 0 0); - --secondary-foreground: oklch(0.205 0 0); - --muted: oklch(0.97 0 0); - --muted-foreground: oklch(0.556 0 0); - --accent: oklch(0.97 0 0); - --accent-foreground: oklch(0.205 0 0); - --destructive: oklch(0.577 0.245 27.325); - --border: oklch(0.922 0 0); - --input: oklch(0.922 0 0); - --ring: oklch(0.708 0 0); - --chart-1: oklch(0.646 0.222 41.116); - --chart-2: oklch(0.6 0.118 184.704); - --chart-3: oklch(0.398 0.07 227.392); - --chart-4: oklch(0.828 0.189 84.429); - --chart-5: oklch(0.769 0.188 70.08); - --sidebar: oklch(0.985 0 0); - --sidebar-foreground: oklch(0.145 0 0); - --sidebar-primary: oklch(0.205 0 0); - --sidebar-primary-foreground: oklch(0.985 0 0); - --sidebar-accent: oklch(0.97 0 0); - --sidebar-accent-foreground: oklch(0.205 0 0); - --sidebar-border: oklch(0.922 0 0); - --sidebar-ring: oklch(0.708 0 0); -} - -.dark { - --background: oklch(23.66% 0.0198 271.79); - --foreground: oklch(75.15% 0.0477 278.41); - --card: oklch(0.205 0 0); - --card-foreground: oklch(0.985 0 0); - --popover: var(--primary); - --popover-foreground: oklch(0.985 0 0); - --primary: oklch(29.33% 0.0295 276.18); - --primary-foreground: var(--accent); - --secondary: var(--primary); - --secondary-foreground: var(--foreground); - --muted: oklch(28.27% 0.0207 273.06); - --muted-foreground: oklch(75.15% 0.0477 278.41 / 75%); - --accent: oklch(70.21% 0.1813 328.71); - --accent-foreground: oklch(1 0 0 / 75%); - --destructive: oklch(72.14% 0.1616 15.49); - --border: var(--primary); - --input: var(--primary); - --ring: oklch(83.63% 0.1259 176.52); - --chart-1: oklch(0.488 0.243 264.376); - --chart-2: oklch(0.696 0.17 162.48); - --chart-3: oklch(0.769 0.188 70.08); - --chart-4: oklch(0.627 0.265 303.9); - --chart-5: oklch(0.645 0.246 16.439); - --sidebar: oklch(0.205 0 0); - --sidebar-foreground: oklch(0.985 0 0); - --sidebar-primary: oklch(0.488 0.243 264.376); - --sidebar-primary-foreground: oklch(0.985 0 0); - --sidebar-accent: oklch(0.269 0 0); - --sidebar-accent-foreground: oklch(0.985 0 0); - --sidebar-border: oklch(1 0 0 / 10%); - --sidebar-ring: oklch(0.556 0 0); -} - -@theme inline { - --color-background: var(--background); - --color-foreground: var(--foreground); - --color-sidebar-ring: var(--sidebar-ring); - --color-sidebar-border: var(--sidebar-border); - --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); - --color-sidebar-accent: var(--sidebar-accent); - --color-sidebar-primary-foreground: var(--sidebar-primary-foreground); - --color-sidebar-primary: var(--sidebar-primary); - --color-sidebar-foreground: var(--sidebar-foreground); - --color-sidebar: var(--sidebar); - --color-chart-5: var(--chart-5); - --color-chart-4: var(--chart-4); - --color-chart-3: var(--chart-3); - --color-chart-2: var(--chart-2); - --color-chart-1: var(--chart-1); - --color-ring: var(--ring); - --color-input: var(--input); - --color-border: var(--border); - --color-destructive: var(--destructive); - --color-accent-foreground: var(--accent-foreground); - --color-accent: var(--accent); - --color-muted-foreground: var(--muted-foreground); - --color-muted: var(--muted); - --color-secondary-foreground: var(--secondary-foreground); - --color-secondary: var(--secondary); - --color-primary-foreground: var(--primary-foreground); - --color-primary: var(--primary); - --color-popover-foreground: var(--popover-foreground); - --color-popover: var(--popover); - --color-card-foreground: var(--card-foreground); - --color-card: var(--card); - --radius-sm: calc(var(--radius) - 4px); - --radius-md: calc(var(--radius) - 2px); - --radius-lg: var(--radius); - --radius-xl: calc(var(--radius) + 4px); - - --animate-hop: hop 0.8s ease-in-out infinite; - - @keyframes hop { - 0%, - 100% { - transform: none; - animation-timing-function: cubic-bezier(0.8, 0, 1, 1); - } - 50% { - transform: translateY(-8px); - animation-timing-function: cubic-bezier(0, 0, 0.2, 1); - } - } -} - -@layer base { - * { - @apply border-border outline-ring/50; - } - html, - body { - height: 100%; - } - body { - @apply bg-background text-foreground; - scrollbar-color: rgba(0, 0, 0, 0.2) transparent; /* Firefox */ - scrollbar-width: thin; - } -} diff --git a/apps/web-evals/src/app/layout.tsx b/apps/web-evals/src/app/layout.tsx deleted file mode 100644 index 3bb34f7dfb8..00000000000 --- a/apps/web-evals/src/app/layout.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import type { Metadata } from "next" -import { Geist, Geist_Mono } from "next/font/google" - -import { ThemeProvider, ReactQueryProvider } from "@/components/providers" -import { Toaster } from "@/components/ui" -import { Header } from "@/components/layout/header" - -import "./globals.css" - -const fontSans = Geist({ variable: "--font-sans", subsets: ["latin"] }) -const fontMono = Geist_Mono({ variable: "--font-mono", subsets: ["latin"] }) - -export const metadata: Metadata = { - title: "Roo Code Evals", -} - -export default function RootLayout({ - children, -}: Readonly<{ - children: React.ReactNode -}>) { - return ( - - - - -
- {children} - - - - - - ) -} diff --git a/apps/web-evals/src/app/page.tsx b/apps/web-evals/src/app/page.tsx deleted file mode 100644 index 3dcb26aebf2..00000000000 --- a/apps/web-evals/src/app/page.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import { getRuns } from "@roo-code/evals" - -import { Runs } from "@/components/home/runs" - -export const dynamic = "force-dynamic" - -export default async function Page() { - const runs = await getRuns() - return -} diff --git a/apps/web-evals/src/app/runs/[id]/page.tsx b/apps/web-evals/src/app/runs/[id]/page.tsx deleted file mode 100644 index 8b993eec8a0..00000000000 --- a/apps/web-evals/src/app/runs/[id]/page.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import { findRun } from "@roo-code/evals" - -import { Run } from "./run" - -export default async function Page({ params }: { params: Promise<{ id: string }> }) { - const { id } = await params - const run = await findRun(Number(id)) - - return ( -
- -
- ) -} diff --git a/apps/web-evals/src/app/runs/[id]/run-status.tsx b/apps/web-evals/src/app/runs/[id]/run-status.tsx deleted file mode 100644 index e05b1b51ebe..00000000000 --- a/apps/web-evals/src/app/runs/[id]/run-status.tsx +++ /dev/null @@ -1,79 +0,0 @@ -"use client" - -import { Link2, Link2Off, CheckCircle2 } from "lucide-react" -import type { RunStatus as _RunStatus } from "@/hooks/use-run-status" -import { cn } from "@/lib/utils" -import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui" - -function StreamIcon({ status }: { status: "connected" | "waiting" | "error" }) { - if (status === "connected") { - return - } - return -} - -export const RunStatus = ({ - runStatus: { sseStatus, heartbeat, runners = [] }, - isComplete = false, -}: { - runStatus: _RunStatus - isComplete?: boolean -}) => { - // For completed runs, show a simple "Complete" badge - if (isComplete) { - return ( - - -
- -
-
- - Run complete - -
- ) - } - - return ( - - -
- {/* Task Stream status icon */} - - - {/* Task Controller ID */} - {heartbeat ?? "-"} - - {/* Task Runners count */} - 0 ? "text-green-500" : "text-rose-500"}> - {runners.length > 0 ? `${runners.length}r` : "0r"} - -
-
- -
-
- - Task Stream: {sseStatus} -
-
- - Task Controller: {heartbeat ?? "dead"} -
-
- 0 ? "text-green-500" : "text-rose-500"}>● - Task Runners: {runners.length > 0 ? runners.length : "none"} -
- {runners.length > 0 && ( -
- {runners.map((runner) => ( -
{runner}
- ))} -
- )} -
-
-
- ) -} diff --git a/apps/web-evals/src/app/runs/[id]/run.tsx b/apps/web-evals/src/app/runs/[id]/run.tsx deleted file mode 100644 index badd77741e0..00000000000 --- a/apps/web-evals/src/app/runs/[id]/run.tsx +++ /dev/null @@ -1,1058 +0,0 @@ -"use client" - -import { useMemo, useState, useCallback, useEffect, Fragment } from "react" -import { toast } from "sonner" -import { LoaderCircle, FileText, Copy, Check, StopCircle, List, Layers } from "lucide-react" - -import type { Run, TaskMetrics as _TaskMetrics, Task } from "@roo-code/evals" -import type { ToolName } from "@roo-code/types" - -import { formatCurrency, formatDuration, formatTokens, formatToolUsageSuccessRate } from "@/lib/formatters" -import { useRunStatus } from "@/hooks/use-run-status" -import { killRun } from "@/actions/runs" -import { - Table, - TableBody, - TableCell, - TableHead, - TableHeader, - TableRow, - Tooltip, - TooltipContent, - TooltipTrigger, - Dialog, - DialogContent, - DialogHeader, - DialogTitle, - ScrollArea, - Button, - AlertDialog, - AlertDialogAction, - AlertDialogCancel, - AlertDialogContent, - AlertDialogDescription, - AlertDialogFooter, - AlertDialogHeader, - AlertDialogTitle, -} from "@/components/ui" - -import { TaskStatus } from "./task-status" -import { RunStatus } from "./run-status" - -type TaskMetrics = Pick<_TaskMetrics, "tokensIn" | "tokensOut" | "tokensContext" | "duration" | "cost"> - -// Extended Task type with taskMetrics from useRunStatus -type TaskWithMetrics = Task & { taskMetrics: _TaskMetrics | null } - -type ToolUsageEntry = { attempts: number; failures: number } -type ToolUsage = Record - -// Generate abbreviation from tool name (e.g., "read_file" -> "RF", "list_code_definition_names" -> "LCDN") -function getToolAbbreviation(toolName: string): string { - return toolName - .split("_") - .map((word) => word[0]?.toUpperCase() ?? "") - .join("") -} - -// Pattern definitions for syntax highlighting -type HighlightPattern = { - pattern: RegExp - className: string - // If true, wraps the entire match; if a number, wraps that capture group - wrapGroup?: number -} - -const HIGHLIGHT_PATTERNS: HighlightPattern[] = [ - // Log levels - styled as badges - { pattern: /\|\s*(INFO)\s*\|/g, className: "text-green-400", wrapGroup: 1 }, - { pattern: /\|\s*(WARN|WARNING)\s*\|/g, className: "text-yellow-400", wrapGroup: 1 }, - { pattern: /\|\s*(ERROR)\s*\|/g, className: "text-red-400 font-semibold", wrapGroup: 1 }, - { pattern: /\|\s*(DEBUG)\s*\|/g, className: "text-gray-400", wrapGroup: 1 }, - // Task identifiers - important events - { - pattern: /(taskCreated|taskFocused|taskStarted|taskCompleted|taskAborted|taskResumable)/g, - className: "text-purple-400 font-medium", - }, - // Tool failures - highlight in red - { pattern: /(taskToolFailed)/g, className: "text-red-400 font-bold" }, - { pattern: /(Tool execution failed|tool.*failed|failed.*tool)/gi, className: "text-red-400" }, - { pattern: /(EvalPass)/g, className: "text-green-400 font-bold" }, - { pattern: /(EvalFail)/g, className: "text-red-400 font-bold" }, - // Message arrows - { pattern: /→/g, className: "text-cyan-400" }, - // Tool names in quotes - { pattern: /"(tool)":\s*"([^"]+)"/g, className: "text-orange-400" }, - // JSON keys - { pattern: /"([^"]+)":/g, className: "text-sky-300" }, - // Boolean values - { pattern: /:\s*(true|false)/g, className: "text-amber-400", wrapGroup: 1 }, - // Numbers - { pattern: /:\s*(-?\d+\.?\d*)/g, className: "text-emerald-400", wrapGroup: 1 }, -] - -// Extract timestamp from a log line and return elapsed time from baseline -function formatElapsedTime(timestamp: string, baselineMs: number): string { - const currentMs = new Date(timestamp).getTime() - const elapsedMs = currentMs - baselineMs - const totalSeconds = Math.floor(elapsedMs / 1000) - const minutes = Math.floor(totalSeconds / 60) - const seconds = totalSeconds % 60 - return `${minutes.toString().padStart(2, "0")}:${seconds.toString().padStart(2, "0")}` -} - -// Extract the first timestamp from the log to use as baseline -function extractFirstTimestamp(log: string): number | null { - // Match timestamp at start of line: [2025-11-28T09:35:23.187Z | ... or [2025-11-28T09:35:23.187Z] - const match = log.match(/\[(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z)[\s|\]]/) - const isoString = match?.[1] - if (!isoString) return null - return new Date(isoString).getTime() -} - -// Simplify log line by removing redundant metadata -function simplifyLogLine(line: string, baselineMs: number | null): { timestamp: string; simplified: string } { - // Extract timestamp - matches [2025-11-28T09:35:23.187Z | ... format - const timestampMatch = line.match(/\[(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z)[\s|\]]/) - const isoTimestamp = timestampMatch?.[1] - if (!isoTimestamp) { - return { timestamp: "", simplified: line } - } - - const timestamp = baselineMs !== null ? formatElapsedTime(isoTimestamp, baselineMs) : isoTimestamp.slice(11, 19) - - // Remove the timestamp from the line (handles both [timestamp] and [timestamp | formats) - let simplified = line.replace(/\[\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z\s*\|?\s*/, "") - - // Remove redundant metadata: pid, run, task IDs (they're same for entire log) - simplified = simplified.replace(/\|\s*pid:\d+\s*/g, "") - simplified = simplified.replace(/\|\s*run:\d+\s*/g, "") - simplified = simplified.replace(/\|\s*task:\d+\s*/g, "") - simplified = simplified.replace(/runTask\s*\|\s*/g, "") - - // Clean up extra pipes, spaces, and trailing brackets - simplified = simplified.replace(/\|\s*\|/g, "|") - simplified = simplified.replace(/^\s*\|\s*/, "") - simplified = simplified.replace(/\]\s*$/, "") // Remove trailing bracket if present - - return { timestamp, simplified } -} - -// Format a single line with syntax highlighting using React elements (XSS-safe) -function formatLine(line: string): React.ReactNode[] { - // Find all matches with their positions - type Match = { start: number; end: number; text: string; className: string } - const matches: Match[] = [] - - for (const { pattern, className, wrapGroup } of HIGHLIGHT_PATTERNS) { - // Reset regex state - pattern.lastIndex = 0 - let regexMatch - while ((regexMatch = pattern.exec(line)) !== null) { - const capturedText = wrapGroup !== undefined ? regexMatch[wrapGroup] : regexMatch[0] - // Skip if capture group didn't match - if (!capturedText) continue - const start = - wrapGroup !== undefined ? regexMatch.index + regexMatch[0].indexOf(capturedText) : regexMatch.index - matches.push({ - start, - end: start + capturedText.length, - text: capturedText, - className, - }) - } - } - - // Sort matches by position and filter overlapping ones - matches.sort((a, b) => a.start - b.start) - const filteredMatches: Match[] = [] - for (const m of matches) { - const lastMatch = filteredMatches[filteredMatches.length - 1] - if (!lastMatch || m.start >= lastMatch.end) { - filteredMatches.push(m) - } - } - - // Build result with highlighted spans - const result: React.ReactNode[] = [] - let currentPos = 0 - - for (const [i, m] of filteredMatches.entries()) { - // Add text before this match - if (m.start > currentPos) { - result.push(line.slice(currentPos, m.start)) - } - // Add highlighted match - result.push( - - {m.text} - , - ) - currentPos = m.end - } - - // Add remaining text - if (currentPos < line.length) { - result.push(line.slice(currentPos)) - } - - return result.length > 0 ? result : [line] -} - -// Determine the visual style for a log line based on its content -function getLineStyle(line: string): string { - if (line.includes("ERROR")) return "bg-red-950/30 border-l-2 border-red-500" - if (line.includes("WARN") || line.includes("WARNING")) return "bg-yellow-950/20 border-l-2 border-yellow-500" - if (line.includes("taskToolFailed")) return "bg-red-950/30 border-l-2 border-red-500" - if (line.includes("taskStarted") || line.includes("taskCreated")) return "bg-purple-950/20" - if (line.includes("EvalPass")) return "bg-green-950/30 border-l-2 border-green-500" - if (line.includes("EvalFail")) return "bg-red-950/30 border-l-2 border-red-500" - if (line.includes("taskCompleted") || line.includes("taskAborted")) return "bg-blue-950/20" - return "" -} - -// Format log content with basic highlighting (XSS-safe - no dangerouslySetInnerHTML) -function formatLogContent(log: string): React.ReactNode[] { - const lines = log.split("\n") - const baselineMs = extractFirstTimestamp(log) - - return lines.map((line, index) => { - if (!line.trim()) { - return ( -
- {" "} -
- ) - } - - const parsed = simplifyLogLine(line, baselineMs) - const lineStyle = getLineStyle(line) - - return ( -
- {/* Elapsed time */} - - {parsed.timestamp} - - {/* Log content - pl-12 ensures wrapped lines are indented under the timestamp */} - - {formatLine(parsed.simplified)} - -
- ) - }) -} - -export function Run({ run }: { run: Run }) { - const runStatus = useRunStatus(run) - const { tasks, tokenUsage, toolUsage, usageUpdatedAt, heartbeat, runners } = runStatus - - const [selectedTask, setSelectedTask] = useState(null) - const [taskLog, setTaskLog] = useState(null) - const [isLoadingLog, setIsLoadingLog] = useState(false) - const [copied, setCopied] = useState(false) - const [showKillDialog, setShowKillDialog] = useState(false) - const [isKilling, setIsKilling] = useState(false) - const [groupByStatus, setGroupByStatus] = useState(() => { - // Initialize from localStorage if available (client-side only) - if (typeof window !== "undefined") { - const stored = localStorage.getItem("evals-group-by-status") - return stored === "true" - } - return false - }) - - // Persist groupByStatus to localStorage - useEffect(() => { - localStorage.setItem("evals-group-by-status", String(groupByStatus)) - }, [groupByStatus]) - - // Determine if run is still active (has heartbeat or runners) - const isRunActive = !run.taskMetricsId && (!!heartbeat || (runners && runners.length > 0)) - - const onKillRun = useCallback(async () => { - setIsKilling(true) - try { - const result = await killRun(run.id) - if (result.killedContainers.length > 0) { - toast.success(`Killed ${result.killedContainers.length} container(s)`) - } else if (result.errors.length === 0) { - toast.info("No running containers found") - } else { - toast.error(result.errors.join(", ")) - } - } catch (error) { - console.error("Failed to kill run:", error) - toast.error("Failed to kill run") - } finally { - setIsKilling(false) - setShowKillDialog(false) - } - }, [run.id]) - - const onCopyLog = useCallback(async () => { - if (!taskLog) return - - try { - await navigator.clipboard.writeText(taskLog) - setCopied(true) - toast.success("Log copied to clipboard") - setTimeout(() => setCopied(false), 2000) - } catch (error) { - console.error("Failed to copy log:", error) - toast.error("Failed to copy log") - } - }, [taskLog]) - - // Handle ESC key to close the dialog - useEffect(() => { - const handleKeyDown = (e: KeyboardEvent) => { - if (e.key === "Escape" && selectedTask) { - setSelectedTask(null) - } - } - - document.addEventListener("keydown", handleKeyDown) - return () => document.removeEventListener("keydown", handleKeyDown) - }, [selectedTask]) - - const taskMetrics: Record = useMemo(() => { - // Reference usageUpdatedAt to trigger recomputation when Map contents change - void usageUpdatedAt - const metrics: Record = {} - - // Helper to calculate duration from database timestamps when streaming duration - // is unavailable (e.g., page was loaded after TaskStarted event was published) - const calculateDurationFromTimestamps = (task: TaskWithMetrics): number => { - if (!task.startedAt) return 0 - const startTime = new Date(task.startedAt).getTime() - const endTime = task.finishedAt ? new Date(task.finishedAt).getTime() : Date.now() - return endTime - startTime - } - - tasks?.forEach((task) => { - const streamingUsage = tokenUsage.get(task.id) - const dbMetrics = task.taskMetrics - - // For finished tasks, prefer DB values but fall back to streaming values - // This handles race conditions during timeout where DB might not have latest data - if (task.finishedAt) { - // Check if DB metrics have meaningful values (not just default/empty) - const dbHasData = dbMetrics && (dbMetrics.tokensIn > 0 || dbMetrics.tokensOut > 0 || dbMetrics.cost > 0) - if (dbHasData) { - // If DB duration is 0 but we have timestamps, calculate from timestamps - const duration = dbMetrics.duration || calculateDurationFromTimestamps(task) - metrics[task.id] = { ...dbMetrics, duration } - } else if (streamingUsage) { - // Fall back to streaming values if DB is empty/stale - // Use streaming duration, or calculate from timestamps if not available - const duration = streamingUsage.duration || calculateDurationFromTimestamps(task) - metrics[task.id] = { - tokensIn: streamingUsage.totalTokensIn, - tokensOut: streamingUsage.totalTokensOut, - tokensContext: streamingUsage.contextTokens, - duration, - cost: streamingUsage.totalCost, - } - } else { - // Task finished but no DB metrics and no streaming data - // (e.g., page loaded after task completed, metrics not persisted) - // Still provide duration calculated from timestamps - metrics[task.id] = { - tokensIn: 0, - tokensOut: 0, - tokensContext: 0, - duration: calculateDurationFromTimestamps(task), - cost: 0, - } - } - } else if (streamingUsage) { - // For running tasks, use streaming values - // Use streaming duration, or calculate from task.startedAt if not available - // (happens when page loads after TaskStarted event was already published) - const duration = streamingUsage.duration || calculateDurationFromTimestamps(task) - metrics[task.id] = { - tokensIn: streamingUsage.totalTokensIn, - tokensOut: streamingUsage.totalTokensOut, - tokensContext: streamingUsage.contextTokens, - duration, - cost: streamingUsage.totalCost, - } - } else if (task.startedAt) { - // Task has started (has startedAt in DB) but no streaming data yet - // This can happen when page loads after TaskStarted but before TokenUsageUpdated - metrics[task.id] = { - tokensIn: 0, - tokensOut: 0, - tokensContext: 0, - duration: calculateDurationFromTimestamps(task), - cost: 0, - } - } - }) - - return metrics - }, [tasks, tokenUsage, usageUpdatedAt]) - - const onViewTaskLog = useCallback( - async (task: Task) => { - // Only allow viewing logs for tasks that have started. - // Note: we treat presence of derived metrics as evidence of a started task, - // since this page may be rendered without streaming `tokenUsage` populated. - const hasStarted = !!task.startedAt || !!tokenUsage.get(task.id) || !!taskMetrics[task.id] - if (!hasStarted) { - toast.error("Task has not started yet") - return - } - - setSelectedTask(task) - setIsLoadingLog(true) - setTaskLog(null) - - try { - const response = await fetch(`/api/runs/${run.id}/logs/${task.id}`) - - if (!response.ok) { - const error = await response.json() - toast.error(error.error || "Failed to load log") - setSelectedTask(null) - return - } - - const data = await response.json() - setTaskLog(data.logContent) - } catch (error) { - console.error("Error loading task log:", error) - toast.error("Failed to load log") - setSelectedTask(null) - } finally { - setIsLoadingLog(false) - } - }, - [run.id, tokenUsage, taskMetrics], - ) - - // Collect all unique tool names from all tasks and sort by total attempts - const toolColumns = useMemo(() => { - // Reference usageUpdatedAt to trigger recomputation when Map contents change - void usageUpdatedAt - if (!tasks) return [] - - const toolTotals = new Map() - - for (const task of tasks) { - // Get both DB and streaming values - const dbToolUsage = task.taskMetrics?.toolUsage - const streamingToolUsage = toolUsage.get(task.id) - - // For finished tasks, prefer DB values but fall back to streaming values - // For running tasks, use streaming values - // This handles race conditions during timeout where DB might not have latest data - const taskToolUsage = task.finishedAt - ? dbToolUsage && Object.keys(dbToolUsage).length > 0 - ? dbToolUsage - : streamingToolUsage - : streamingToolUsage - - if (taskToolUsage) { - for (const [toolName, usage] of Object.entries(taskToolUsage)) { - const tool = toolName as ToolName - const current = toolTotals.get(tool) ?? 0 - toolTotals.set(tool, current + usage.attempts) - } - } - } - - // Sort by total attempts descending - return Array.from(toolTotals.entries()) - .sort((a, b) => b[1] - a[1]) - .map(([name]): ToolName => name) - // toolUsage ref is stable; usageUpdatedAt triggers recomputation when Map contents change - }, [tasks, toolUsage, usageUpdatedAt]) - - // Compute aggregate stats - const stats = useMemo(() => { - // Reference usageUpdatedAt to trigger recomputation when Map contents change - void usageUpdatedAt - if (!tasks) return null - - const passed = tasks.filter((t) => t.passed === true).length - const failed = tasks.filter((t) => t.passed === false).length - const completed = passed + failed - - let totalTokensIn = 0 - let totalTokensOut = 0 - let totalCost = 0 - let totalDuration = 0 - - // Aggregate tool usage from all tasks (both finished and running) - const toolUsageAggregate: ToolUsage = {} - - for (const task of tasks) { - const metrics = taskMetrics[task.id] - if (metrics) { - totalTokensIn += metrics.tokensIn - totalTokensOut += metrics.tokensOut - totalCost += metrics.cost - totalDuration += metrics.duration - } - - // Aggregate tool usage: prefer DB values for finished tasks, fall back to streaming values - // This handles race conditions during timeout where DB might not have latest data - const dbToolUsage = task.taskMetrics?.toolUsage - const streamingToolUsage = toolUsage.get(task.id) - const taskToolUsage = task.finishedAt - ? dbToolUsage && Object.keys(dbToolUsage).length > 0 - ? dbToolUsage - : streamingToolUsage - : streamingToolUsage - - if (taskToolUsage) { - for (const [key, usage] of Object.entries(taskToolUsage)) { - const tool = key as keyof ToolUsage - if (!toolUsageAggregate[tool]) { - toolUsageAggregate[tool] = { attempts: 0, failures: 0 } - } - toolUsageAggregate[tool].attempts += usage.attempts - toolUsageAggregate[tool].failures += usage.failures - } - } - } - - const remaining = tasks.length - completed - - return { - passed, - failed, - completed, - remaining, - passRate: completed > 0 ? ((passed / completed) * 100).toFixed(1) : null, - totalTokensIn, - totalTokensOut, - totalCost, - totalDuration, - toolUsage: toolUsageAggregate, - } - // Map refs are stable; usageUpdatedAt triggers recomputation when Map contents change - }, [tasks, taskMetrics, toolUsage, usageUpdatedAt]) - - // Calculate elapsed time (wall-clock time from run creation to completion or now) - const elapsedTime = useMemo(() => { - // Reference usageUpdatedAt to trigger recomputation for live elapsed time updates - void usageUpdatedAt - if (!tasks || tasks.length === 0) return null - - const startTime = new Date(run.createdAt).getTime() - - // If run is complete, find the latest finishedAt from tasks - if (run.taskMetricsId) { - const latestFinish = tasks.reduce((latest, task) => { - if (task.finishedAt) { - const finishTime = new Date(task.finishedAt).getTime() - return finishTime > latest ? finishTime : latest - } - return latest - }, startTime) - return latestFinish - startTime - } - - // If still running, use current time - return Date.now() - startTime - }, [tasks, run.createdAt, run.taskMetricsId, usageUpdatedAt]) - - // Task status categories - type TaskStatusCategory = "failed" | "in_progress" | "passed" | "not_started" - - const getTaskStatusCategory = useCallback( - (task: TaskWithMetrics): TaskStatusCategory => { - if (task.passed === false) return "failed" - if (task.passed === true) return "passed" - // Check streaming data, DB metrics, or startedAt timestamp - const hasStarted = !!task.startedAt || !!tokenUsage.get(task.id) || !!taskMetrics[task.id] - if (hasStarted) return "in_progress" - return "not_started" - }, - [tokenUsage, taskMetrics], - ) - - // Group tasks by status while preserving original index - const groupedTasks = useMemo(() => { - if (!tasks || !groupByStatus) return null - - const groups: Record> = { - failed: [], - in_progress: [], - passed: [], - not_started: [], - } - - tasks.forEach((task, index) => { - const status = getTaskStatusCategory(task) - groups[status].push({ task, originalIndex: index }) - }) - - return groups - }, [tasks, groupByStatus, getTaskStatusCategory]) - - const statusLabels = useMemo( - (): Record => ({ - failed: { label: "Failed", className: "text-red-500", count: groupedTasks?.failed.length ?? 0 }, - in_progress: { - label: "In Progress", - className: "text-yellow-500", - count: groupedTasks?.in_progress.length ?? 0, - }, - passed: { label: "Passed", className: "text-green-500", count: groupedTasks?.passed.length ?? 0 }, - not_started: { - label: "Not Started", - className: "text-muted-foreground", - count: groupedTasks?.not_started.length ?? 0, - }, - }), - [groupedTasks], - ) - - const statusOrder: TaskStatusCategory[] = ["failed", "in_progress", "passed", "not_started"] - - // Helper to render a task row - const renderTaskRow = (task: TaskWithMetrics, originalIndex: number) => { - const hasStarted = !!task.startedAt || !!tokenUsage.get(task.id) || !!taskMetrics[task.id] - return ( - hasStarted && onViewTaskLog(task)}> - - {originalIndex + 1} - - -
- -
- - {task.language}/{task.exercise} - {task.iteration > 1 && ( - (#{task.iteration}) - )} - - {hasStarted && ( - - - - - Click to view log - - )} -
-
-
- {taskMetrics[task.id] ? ( - <> - -
-
{formatTokens(taskMetrics[task.id]!.tokensIn)}
/ -
{formatTokens(taskMetrics[task.id]!.tokensOut)}
-
-
- - {formatTokens(taskMetrics[task.id]!.tokensContext)} - - {toolColumns.map((toolName) => { - const dbUsage = task.taskMetrics?.toolUsage?.[toolName] - const streamingUsage = toolUsage.get(task.id)?.[toolName] - const usage = task.finishedAt ? (dbUsage ?? streamingUsage) : streamingUsage - - const successRate = - usage && usage.attempts > 0 - ? ((usage.attempts - usage.failures) / usage.attempts) * 100 - : 100 - const rateColor = - successRate === 100 - ? "text-muted-foreground" - : successRate >= 80 - ? "text-yellow-500" - : "text-red-500" - return ( - - {usage ? ( -
- {usage.attempts} - {formatToolUsageSuccessRate(usage)} -
- ) : ( - - - )} -
- ) - })} - - {taskMetrics[task.id]!.duration ? formatDuration(taskMetrics[task.id]!.duration) : "-"} - - - {formatCurrency(taskMetrics[task.id]!.cost)} - - - ) : ( - - )} -
- ) - } - - return ( - <> -
- {!tasks ? ( - - ) : ( - <> - {/* View Toggle */} -
- - - - - - {groupByStatus ? "Show tasks in run order" : "Group tasks by status"} - - -
- - - {stats && ( - - - {/* Provider, Model title and status */} -
- {run.settings?.apiProvider && ( - - {run.settings.apiProvider} - - )} -
{run.model}
- - {run.description && ( - - - {run.description} - - )} - {isRunActive && ( - - - - - - Stop all containers for this run - - - )} -
- {/* Main Stats Row */} -
- {/* Pass Rate / Fail Rate / Remaining % */} -
-
- - {stats.completed > 0 - ? `${((stats.passed / stats.completed) * 100).toFixed(1)}%` - : "-"} - - / - - {stats.completed > 0 - ? `${((stats.failed / stats.completed) * 100).toFixed(1)}%` - : "-"} - - / - - {tasks.length > 0 - ? `${((stats.remaining / tasks.length) * 100).toFixed(1)}%` - : "-"} - -
-
- {stats.passed} - {" / "} - {stats.failed} - {" / "} - {stats.remaining} - {" of "} - {tasks.length} -
-
- - {/* Tokens */} -
-
- {formatTokens(stats.totalTokensIn)} - / - {formatTokens(stats.totalTokensOut)} -
-
Tokens In / Out
-
- - {/* Cost */} -
-
- {formatCurrency(stats.totalCost)} -
-
Cost
-
- - {/* Duration */} -
-
- {stats.totalDuration > 0 - ? formatDuration(stats.totalDuration) - : "-"} -
-
Duration
-
- - {/* Elapsed Time */} -
-
- {elapsedTime !== null ? formatDuration(elapsedTime) : "-"} -
-
Elapsed
-
- - {/* Estimated Time Remaining - only show if run is active and we have data */} - {!run.taskMetricsId && - elapsedTime !== null && - stats.completed > 0 && - stats.remaining > 0 && ( -
-
- ~ - {formatDuration( - (elapsedTime / stats.completed) * stats.remaining, - )} -
-
- Est. Remaining -
-
- )} -
- - {/* Tool Usage Row */} - {Object.keys(stats.toolUsage).length > 0 && ( -
- {Object.entries(stats.toolUsage) - .sort(([, a], [, b]) => b.attempts - a.attempts) - .map(([toolName, usage]) => { - const abbr = getToolAbbreviation(toolName) - const successRate = - usage.attempts > 0 - ? ((usage.attempts - usage.failures) / - usage.attempts) * - 100 - : 100 - const rateColor = - successRate === 100 - ? "text-green-500" - : successRate >= 80 - ? "text-yellow-500" - : "text-red-500" - return ( - - -
- - {abbr} - - - {usage.attempts} - - - {formatToolUsageSuccessRate(usage)} - -
-
- - {toolName} - -
- ) - })} -
- )} -
-
- )} - - # - Exercise - Tokens In / Out - Context - {toolColumns.map((toolName) => ( - - - {getToolAbbreviation(toolName)} - {toolName} - - - ))} - Duration - Cost - -
- - {groupByStatus && groupedTasks - ? // Grouped view - statusOrder.map((status) => { - const group = groupedTasks[status] - if (group.length === 0) return null - const { label, className } = statusLabels[status] - return ( - - - - - {label} ({group.length}) - - - - {group.map(({ task, originalIndex }) => - renderTaskRow(task, originalIndex), - )} - - ) - }) - : // Default order view - tasks.map((task, index) => renderTaskRow(task, index))} - -
- - )} -
- - {/* Task Log Dialog - Full Screen */} - setSelectedTask(null)}> - - -
- - - {selectedTask?.language}/{selectedTask?.exercise} - {selectedTask?.iteration && selectedTask.iteration > 1 && ( - (#{selectedTask.iteration}) - )} - - ( - {selectedTask?.passed === true - ? "Passed" - : selectedTask?.passed === false - ? "Failed" - : "Running"} - ) - - - {taskLog && ( - - )} -
-
-
- {isLoadingLog ? ( -
- -
- ) : taskLog ? ( - -
- {formatLogContent(taskLog)} -
-
- ) : ( -
- Log file not available (may have been cleared) -
- )} -
-
-
- - {/* Kill Run Confirmation Dialog */} - - - - Kill Run? - - This will stop the controller and all task runner containers for this run. Any running tasks - will be terminated immediately. This action cannot be undone. - - - - Cancel - - {isKilling ? ( - <> - - Killing... - - ) : ( - "Kill Run" - )} - - - - - - ) -} diff --git a/apps/web-evals/src/app/runs/[id]/task-status.tsx b/apps/web-evals/src/app/runs/[id]/task-status.tsx deleted file mode 100644 index bae785131a5..00000000000 --- a/apps/web-evals/src/app/runs/[id]/task-status.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import { CircleCheck, CircleDashed, CircleSlash, LoaderCircle } from "lucide-react" - -import type { Task } from "@roo-code/evals" - -type TaskStatusProps = { - task: Task - running: boolean -} - -export const TaskStatus = ({ task, running }: TaskStatusProps) => { - return task.passed === false ? ( - - ) : task.passed === true ? ( - - ) : running ? ( - - ) : ( - - ) -} diff --git a/apps/web-evals/src/app/runs/new/new-run.tsx b/apps/web-evals/src/app/runs/new/new-run.tsx deleted file mode 100644 index 8d44ef38e7c..00000000000 --- a/apps/web-evals/src/app/runs/new/new-run.tsx +++ /dev/null @@ -1,1083 +0,0 @@ -"use client" - -import { useCallback, useEffect, useMemo, useRef, useState } from "react" -import { useRouter } from "next/navigation" -import { z } from "zod" -import { useQuery } from "@tanstack/react-query" -import { useForm, FormProvider } from "react-hook-form" -import { zodResolver } from "@hookform/resolvers/zod" -import { toast } from "sonner" -import { - X, - Rocket, - Check, - ChevronsUpDown, - SlidersHorizontal, - Info, - Plus, - Minus, - Terminal, - MonitorPlay, -} from "lucide-react" - -import { - type ProviderSettings, - type GlobalSettings, - globalSettingsSchema, - providerSettingsSchema, - getModelId, - EVALS_SETTINGS, -} from "@roo-code/types" - -import { createRun } from "@/actions/runs" -import { getExercises } from "@/actions/exercises" - -import { - type CreateRun, - type ExecutionMethod, - createRunSchema, - CONCURRENCY_MIN, - CONCURRENCY_MAX, - CONCURRENCY_DEFAULT, - TIMEOUT_MIN, - TIMEOUT_MAX, - TIMEOUT_DEFAULT, - ITERATIONS_MIN, - ITERATIONS_MAX, - ITERATIONS_DEFAULT, -} from "@/lib/schemas" -import { cn } from "@/lib/utils" - -import { loadRooLastModelSelection, saveRooLastModelSelection } from "@/lib/roo-last-model-selection" -import { normalizeCreateRunForSubmit } from "@/lib/normalize-create-run" - -import { useOpenRouterModels } from "@/hooks/use-open-router-models" -import { useRooCodeCloudModels } from "@/hooks/use-roo-code-cloud-models" - -import { - Button, - FormControl, - FormField, - FormItem, - FormLabel, - FormMessage, - Input, - Textarea, - Tabs, - TabsList, - TabsTrigger, - MultiSelect, - Command, - CommandEmpty, - CommandGroup, - CommandInput, - CommandItem, - CommandList, - Popover, - PopoverContent, - PopoverTrigger, - Slider, - Label, - Tooltip, - TooltipContent, - TooltipTrigger, -} from "@/components/ui" - -import { SettingsDiff } from "./settings-diff" - -type ImportedSettings = { - apiConfigs: Record - globalSettings: GlobalSettings - currentApiConfigName: string -} - -type ModelSelection = { - id: string - model: string - popoverOpen: boolean -} - -type ConfigSelection = { - id: string - configName: string - popoverOpen: boolean -} - -export function NewRun() { - const router = useRouter() - const modelSelectionsByProviderRef = useRef>({}) - const modelValueByProviderRef = useRef>({}) - - const [provider, setModelSource] = useState<"roo" | "openrouter" | "other">("other") - const [executionMethod, setExecutionMethod] = useState("vscode") - const [commandExecutionTimeout, setCommandExecutionTimeout] = useState(20) - const [terminalShellIntegrationTimeout, setTerminalShellIntegrationTimeout] = useState(30) // seconds - - const [modelSelections, setModelSelections] = useState([ - { id: crypto.randomUUID(), model: "", popoverOpen: false }, - ]) - - const [importedSettings, setImportedSettings] = useState(null) - const [configSelections, setConfigSelections] = useState([ - { id: crypto.randomUUID(), configName: "", popoverOpen: false }, - ]) - - const openRouter = useOpenRouterModels() - const rooCodeCloud = useRooCodeCloudModels() - const models = provider === "openrouter" ? openRouter.data : rooCodeCloud.data - const searchValue = provider === "openrouter" ? openRouter.searchValue : rooCodeCloud.searchValue - const setSearchValue = provider === "openrouter" ? openRouter.setSearchValue : rooCodeCloud.setSearchValue - const onFilter = provider === "openrouter" ? openRouter.onFilter : rooCodeCloud.onFilter - - const exercises = useQuery({ queryKey: ["getExercises"], queryFn: () => getExercises() }) - - const [selectedExercises, setSelectedExercises] = useState([]) - - const form = useForm({ - resolver: zodResolver(createRunSchema), - defaultValues: { - model: "", - description: "", - suite: "full", - exercises: [], - settings: undefined, - concurrency: CONCURRENCY_DEFAULT, - timeout: TIMEOUT_DEFAULT, - iterations: ITERATIONS_DEFAULT, - jobToken: "", - executionMethod: "vscode", - }, - }) - - const { - register, - setValue, - clearErrors, - watch, - getValues, - formState: { isSubmitting }, - } = form - - const [suite, settings] = watch(["suite", "settings", "concurrency"]) - - const selectedModelIds = useMemo( - () => modelSelections.map((s) => s.model).filter((m) => m.length > 0), - [modelSelections], - ) - - const applyModelIds = useCallback( - (modelIds: string[]) => { - const unique = Array.from(new Set(modelIds.map((m) => m.trim()).filter((m) => m.length > 0))) - - if (unique.length === 0) { - setModelSelections([{ id: crypto.randomUUID(), model: "", popoverOpen: false }]) - setValue("model", "") - return - } - - setModelSelections(unique.map((model) => ({ id: crypto.randomUUID(), model, popoverOpen: false }))) - setValue("model", unique[0] ?? "") - }, - [setValue], - ) - - // Ensure the `exercises` field is registered so RHF always includes it in submit values. - useEffect(() => { - register("exercises") - }, [register]) - - // Load settings from localStorage on mount - useEffect(() => { - const savedConcurrency = localStorage.getItem("evals-concurrency") - - if (savedConcurrency) { - const parsed = parseInt(savedConcurrency, 10) - - if (!isNaN(parsed) && parsed >= CONCURRENCY_MIN && parsed <= CONCURRENCY_MAX) { - setValue("concurrency", parsed) - } - } - - const savedTimeout = localStorage.getItem("evals-timeout") - - if (savedTimeout) { - const parsed = parseInt(savedTimeout, 10) - - if (!isNaN(parsed) && parsed >= TIMEOUT_MIN && parsed <= TIMEOUT_MAX) { - setValue("timeout", parsed) - } - } - - const savedCommandTimeout = localStorage.getItem("evals-command-execution-timeout") - - if (savedCommandTimeout) { - const parsed = parseInt(savedCommandTimeout, 10) - - if (!isNaN(parsed) && parsed >= 20 && parsed <= 60) { - setCommandExecutionTimeout(parsed) - } - } - - const savedShellTimeout = localStorage.getItem("evals-shell-integration-timeout") - - if (savedShellTimeout) { - const parsed = parseInt(savedShellTimeout, 10) - - if (!isNaN(parsed) && parsed >= 30 && parsed <= 60) { - setTerminalShellIntegrationTimeout(parsed) - } - } - - const savedSuite = localStorage.getItem("evals-suite") - - if (savedSuite === "partial") { - setValue("suite", "partial") - const savedExercises = localStorage.getItem("evals-exercises") - if (savedExercises) { - try { - const parsed = JSON.parse(savedExercises) as string[] - if (Array.isArray(parsed)) { - setSelectedExercises(parsed) - setValue("exercises", parsed) - } - } catch { - // Invalid JSON, ignore. - } - } - } - }, [setValue]) - - // Track previous provider to detect switches - const [prevProvider, setPrevProvider] = useState(provider) - - // Preserve selections per provider; avoids cross-contamination while keeping UX stable. - useEffect(() => { - if (provider === prevProvider) return - - modelSelectionsByProviderRef.current[prevProvider] = modelSelections - modelValueByProviderRef.current[prevProvider] = getValues("model") - - const nextModelSelections = - modelSelectionsByProviderRef.current[provider] ?? - ([{ id: crypto.randomUUID(), model: "", popoverOpen: false }] satisfies ModelSelection[]) - - setModelSelections(nextModelSelections) - - const nextModelValue = - modelValueByProviderRef.current[provider] ?? - nextModelSelections.find((s) => s.model.trim().length > 0)?.model ?? - (provider === "other" && importedSettings && configSelections[0]?.configName - ? (getModelId(importedSettings.apiConfigs[configSelections[0].configName] ?? {}) ?? "") - : "") - - setValue("model", nextModelValue) - setPrevProvider(provider) - }, [provider, prevProvider, modelSelections, setValue, getValues, importedSettings, configSelections]) - - // When switching to Roo provider, restore last-used selection if current selection is empty - useEffect(() => { - if (provider !== "roo") return - if (selectedModelIds.length > 0) return - - const last = loadRooLastModelSelection() - if (last.length > 0) { - applyModelIds(last) - } - }, [applyModelIds, provider, selectedModelIds.length]) - - // Persist last-used Roo provider model selection - useEffect(() => { - if (provider !== "roo") return - saveRooLastModelSelection(selectedModelIds) - }, [provider, selectedModelIds]) - - // Extract unique languages from exercises - const languages = useMemo(() => { - if (!exercises.data) { - return [] - } - - const langs = new Set() - - for (const path of exercises.data) { - const lang = path.split("/")[0] - - if (lang) { - langs.add(lang) - } - } - - return Array.from(langs).sort() - }, [exercises.data]) - - const getExercisesForLanguage = useCallback( - (lang: string) => { - if (!exercises.data) { - return [] - } - - return exercises.data.filter((path) => path.startsWith(`${lang}/`)) - }, - [exercises.data], - ) - - const toggleLanguage = useCallback( - (lang: string) => { - const langExercises = getExercisesForLanguage(lang) - const allSelected = langExercises.every((ex) => selectedExercises.includes(ex)) - - let newSelected: string[] - - if (allSelected) { - newSelected = selectedExercises.filter((ex) => !ex.startsWith(`${lang}/`)) - } else { - const existing = new Set(selectedExercises) - - for (const ex of langExercises) { - existing.add(ex) - } - - newSelected = Array.from(existing) - } - - setSelectedExercises(newSelected) - setValue("exercises", newSelected) - localStorage.setItem("evals-exercises", JSON.stringify(newSelected)) - }, - [getExercisesForLanguage, selectedExercises, setValue], - ) - - const isLanguageSelected = useCallback( - (lang: string) => { - const langExercises = getExercisesForLanguage(lang) - return langExercises.length > 0 && langExercises.every((ex) => selectedExercises.includes(ex)) - }, - [getExercisesForLanguage, selectedExercises], - ) - - const isLanguagePartiallySelected = useCallback( - (lang: string) => { - const langExercises = getExercisesForLanguage(lang) - const selectedCount = langExercises.filter((ex) => selectedExercises.includes(ex)).length - return selectedCount > 0 && selectedCount < langExercises.length - }, - [getExercisesForLanguage, selectedExercises], - ) - - const addModelSelection = useCallback(() => { - setModelSelections((prev) => [...prev, { id: crypto.randomUUID(), model: "", popoverOpen: false }]) - }, []) - - const removeModelSelection = useCallback((id: string) => { - setModelSelections((prev) => prev.filter((s) => s.id !== id)) - }, []) - - const updateModelSelection = useCallback( - (id: string, model: string) => { - setModelSelections((prev) => prev.map((s) => (s.id === id ? { ...s, model, popoverOpen: false } : s))) - // Also set the form model field for validation (use first non-empty model). - setValue("model", model) - }, - [setValue], - ) - - const toggleModelPopover = useCallback((id: string, open: boolean) => { - setModelSelections((prev) => prev.map((s) => (s.id === id ? { ...s, popoverOpen: open } : s))) - }, []) - - const addConfigSelection = useCallback(() => { - setConfigSelections((prev) => [...prev, { id: crypto.randomUUID(), configName: "", popoverOpen: false }]) - }, []) - - const removeConfigSelection = useCallback((id: string) => { - setConfigSelections((prev) => prev.filter((s) => s.id !== id)) - }, []) - - const updateConfigSelection = useCallback( - (id: string, configName: string) => { - setConfigSelections((prev) => prev.map((s) => (s.id === id ? { ...s, configName, popoverOpen: false } : s))) - - // Also update the form settings for the first config (for validation). - if (importedSettings) { - const providerSettings = importedSettings.apiConfigs[configName] ?? {} - setValue("model", getModelId(providerSettings) ?? "") - setValue("settings", { ...EVALS_SETTINGS, ...providerSettings, ...importedSettings.globalSettings }) - } - }, - [importedSettings, setValue], - ) - - const toggleConfigPopover = useCallback((id: string, open: boolean) => { - setConfigSelections((prev) => prev.map((s) => (s.id === id ? { ...s, popoverOpen: open } : s))) - }, []) - - const onSubmit = useCallback( - async (values: CreateRun) => { - try { - const baseValues = normalizeCreateRunForSubmit(values, selectedExercises, suite) - - // Validate jobToken for Roo Code Cloud provider - if (provider === "roo" && !baseValues.jobToken?.trim()) { - toast.error("Roo Code Cloud Token is required") - return - } - - const selectionsToLaunch: { model: string; configName?: string }[] = [] - - if (provider === "other") { - for (const config of configSelections) { - if (config.configName) { - selectionsToLaunch.push({ model: "", configName: config.configName }) - } - } - } else { - for (const selection of modelSelections) { - if (selection.model) { - selectionsToLaunch.push({ model: selection.model }) - } - } - } - - if (selectionsToLaunch.length === 0) { - toast.error("Please select at least one model or config") - return - } - - const totalRuns = selectionsToLaunch.length - toast.info(totalRuns > 1 ? `Launching ${totalRuns} runs (every 20 seconds)...` : "Launching run...") - - for (let i = 0; i < selectionsToLaunch.length; i++) { - const selection = selectionsToLaunch[i]! - - // Wait 20 seconds between runs (except for the first one). - if (i > 0) { - await new Promise((resolve) => setTimeout(resolve, 20_000)) - } - - const runValues = { ...baseValues } - - if (provider === "openrouter") { - runValues.model = selection.model - runValues.settings = { - ...(runValues.settings || {}), - apiProvider: "openrouter", - openRouterModelId: selection.model, - commandExecutionTimeout, - terminalShellIntegrationTimeout: terminalShellIntegrationTimeout * 1000, - } - } else if (provider === "roo") { - runValues.model = selection.model - runValues.settings = { - ...(runValues.settings || {}), - apiProvider: "roo", - apiModelId: selection.model, - commandExecutionTimeout, - terminalShellIntegrationTimeout: terminalShellIntegrationTimeout * 1000, - } - } else if (provider === "other" && selection.configName && importedSettings) { - const providerSettings = importedSettings.apiConfigs[selection.configName] ?? {} - runValues.model = getModelId(providerSettings) ?? "" - runValues.settings = { - ...EVALS_SETTINGS, - ...providerSettings, - ...importedSettings.globalSettings, - commandExecutionTimeout, - terminalShellIntegrationTimeout: terminalShellIntegrationTimeout * 1000, - } - } - - try { - await createRun(runValues) - toast.success(`Run ${i + 1}/${totalRuns} launched`) - } catch (e) { - toast.error(`Run ${i + 1} failed: ${e instanceof Error ? e.message : "Unknown error"}`) - } - } - - router.push("/") - } catch (e) { - toast.error(e instanceof Error ? e.message : "An unknown error occurred.") - } - }, - [ - suite, - selectedExercises, - provider, - modelSelections, - configSelections, - importedSettings, - router, - commandExecutionTimeout, - terminalShellIntegrationTimeout, - ], - ) - - const onImportSettings = useCallback( - async (event: React.ChangeEvent) => { - const file = event.target.files?.[0] - - if (!file) { - return - } - - clearErrors("settings") - - try { - const { providerProfiles, globalSettings } = z - .object({ - providerProfiles: z.object({ - currentApiConfigName: z.string(), - apiConfigs: z.record(z.string(), providerSettingsSchema), - }), - globalSettings: globalSettingsSchema, - }) - .parse(JSON.parse(await file.text())) - - setImportedSettings({ - apiConfigs: providerProfiles.apiConfigs, - globalSettings, - currentApiConfigName: providerProfiles.currentApiConfigName, - }) - - const defaultConfigName = providerProfiles.currentApiConfigName - setConfigSelections([{ id: crypto.randomUUID(), configName: defaultConfigName, popoverOpen: false }]) - - const providerSettings = providerProfiles.apiConfigs[defaultConfigName] ?? {} - setValue("model", getModelId(providerSettings) ?? "") - setValue("settings", { ...EVALS_SETTINGS, ...providerSettings, ...globalSettings }) - - event.target.value = "" - } catch (e) { - console.error(e) - toast.error(e instanceof Error ? e.message : "An unknown error occurred.") - } - }, - [clearErrors, setValue], - ) - - return ( - <> - -
- ( - - setModelSource(value as "roo" | "openrouter" | "other")}> - - Import - Roo Code Cloud - OpenRouter - - - - {provider === "other" ? ( -
- - - - {importedSettings && Object.keys(importedSettings.apiConfigs).length > 0 && ( -
- - {configSelections.map((selection, index) => ( -
- - toggleConfigPopover(selection.id, open) - }> - - - - - - - - No config found. - - {Object.keys( - importedSettings.apiConfigs, - ).map((configName) => ( - - updateConfigSelection( - selection.id, - configName, - ) - }> - {configName} - {configName === - importedSettings.currentApiConfigName && ( - - (default) - - )} - - - ))} - - - - - - {index === configSelections.length - 1 ? ( - - ) : ( - - )} -
- ))} -
- )} - - {settings && ( - - )} -
- ) : ( - <> -
- {modelSelections.map((selection, index) => ( -
- toggleModelPopover(selection.id, open)}> - - - - - - - - No model found. - - {models?.map(({ id, name }) => ( - - updateModelSelection( - selection.id, - id, - ) - }> - {name} - - - ))} - - - - - - {index === modelSelections.length - 1 ? ( - - ) : ( - - )} -
- ))} -
- - )} - - -
- )} - /> - - {provider === "roo" && ( - ( - -
- Roo Code Cloud Token - - - - - -

- If you have access to the Roo Code Cloud repository and the - decryption key for the .env.* files, generate a token with: -

- - pnpm --filter @roo-code-cloud/auth production:create-auth-token - [email] [org] [ttl] - -
-
-
- - - - -
- )} - /> - )} - - ( - - Exercises -
- { - setValue("suite", value as "full" | "partial") - localStorage.setItem("evals-suite", value) - if (value === "full") { - setSelectedExercises([]) - setValue("exercises", []) - localStorage.removeItem("evals-exercises") - } - }}> - - All - Some - - - {suite === "partial" && languages.length > 0 && ( -
- {languages.map((lang) => ( - - ))} -
- )} -
- {suite === "partial" && ( - ({ value: path, label: path })) || []} - value={selectedExercises} - onValueChange={(value) => { - setSelectedExercises(value) - setValue("exercises", value) - localStorage.setItem("evals-exercises", JSON.stringify(value)) - }} - placeholder="Select" - variant="inverted" - maxCount={4} - /> - )} - -
- )} - /> - - {/* Concurrency, Timeout, and Iterations in a 3-column row */} -
- ( - - Concurrency - -
- { - field.onChange(value[0]) - localStorage.setItem("evals-concurrency", String(value[0])) - }} - /> -
{field.value}
-
-
- -
- )} - /> - - ( - - Timeout (Minutes) - -
- { - field.onChange(value[0]) - localStorage.setItem("evals-timeout", String(value[0])) - }} - /> -
{field.value}
-
-
- -
- )} - /> - - ( - - Iterations - -
- { - field.onChange(value[0]) - }} - /> -
{field.value}
-
-
- -
- )} - /> -
- - {/* Terminal timeouts in a 2-column row */} -
- -
- - - - - - -

- Maximum time in seconds to wait for terminal command execution to complete - before timing out. This applies to commands run via the execute_command - tool. -

-
-
-
-
- { - if (value !== undefined) { - setCommandExecutionTimeout(value) - localStorage.setItem("evals-command-execution-timeout", String(value)) - } - }} - /> -
{commandExecutionTimeout}
-
-
- - -
- - - - - - -

- Maximum time in seconds to wait for shell integration to initialize when - opening a new terminal. -

-
-
-
-
- { - if (value !== undefined) { - setTerminalShellIntegrationTimeout(value) - localStorage.setItem("evals-shell-integration-timeout", String(value)) - } - }} - /> -
{terminalShellIntegrationTimeout}
-
-
-
- - {/* Execution Method */} - ( - - Execution Method - { - const newExecutionMethod = value as ExecutionMethod - setExecutionMethod(newExecutionMethod) - setValue("executionMethod", newExecutionMethod) - }}> - - - - VSCode - - - - CLI - - - - - - )} - /> - - ( - - Description / Notes - -