Merge main

This commit is contained in:
cte 2025-03-26 01:29:50 -07:00
commit b27189f0f5
110 changed files with 3213 additions and 1991 deletions

View file

@ -0,0 +1,5 @@
---
"roo-cline": patch
---
Show openrouter key balance on the settings screen

View file

@ -1,5 +0,0 @@
---
"roo-cline": patch
---
Add settings migration to support renaming legacy settings files to new format

View file

@ -1,5 +0,0 @@
---
"roo-cline": patch
---
Add the openAiR1FormatEnabled field to enable this switch in OpenAI compatible mode to support the current QWQ and future additional classes of R1 models.

View file

@ -1,5 +1,33 @@
# Roo Code Changelog
## [3.10.5] - 2025-03-25
- Updated value of max tokens for gemini-2.5-pro-03-25 to 65,536 (thanks @linegel!)
- Fix logic around when we fire task completion events
## [3.10.4] - 2025-03-25
- Dynamically fetch instructions for creating/editing custom modes and MCP servers (thanks @diarmidmackenzie!)
- Added Gemini 2.5 Pro model to Google Gemini provider (thanks @samsilveira!)
- Add settings to control whether to auto-approve reads and writes outside of the workspace
- Update UX for chat text area (thanks @chadgauth!)
- Support a custom storage path for tasks (thanks @Chenjiayuan195!)
- Add a New Task command in the Command Palette (thanks @qdaxb!)
- Add R1 support checkbox to Open AI compatible provider to support QWQ (thanks @teddyOOXX!)
- Support test declarations in TypeScript tree-sitter queries (thanks @KJ7LNW!)
- Add Bedrock support for application-inference-profile (thanks @maekawataiki!)
- Rename and migrate global MCP and modes files (thanks @StevenTCramer!)
- Add watchPaths option to McpHub for file change detection (thanks @01Rian!)
- Read image responses from MCP calls (thanks @nevermorec!)
- Add taskCreated event to API and subscribe to Cline events earlier (thanks @wkordalski!)
- Fixes to numeric formatting suffix internationalization (thanks @feifei325!)
- Fix open tab support in the context mention suggestions (thanks @aheizi!)
- Better display of OpenRouter “overloaded” error messages
- Fix browser tool visibility in system prompt preview (thanks @cannuri!)
- Fix the supportsPromptCache value for OpenAI models (thanks @PeterDaveHello!)
- Fix readme links to docs (thanks @kvokka!)
- Run npm audit fix on all of our libraries
## [3.10.3] - 2025-03-23
- Update the welcome page to provide 1-click OAuth flows with LLM routers (thanks @dtrugman!)

View file

@ -77,7 +77,7 @@ Roo Code 3.10 brings powerful productivity enhancements!
### Multiple Modes
Roo Code adapts to your needs with specialized [modes](https://docs.roocode.com/basic-usage/modes):
Roo Code adapts to your needs with specialized [modes](https://docs.roocode.com/basic-usage/using-modes):
- **Code Mode:** For general-purpose coding tasks
- **Architect Mode:** For planning and technical leadership
@ -87,7 +87,7 @@ Roo Code adapts to your needs with specialized [modes](https://docs.roocode.com/
### Smart Tools
Roo Code comes with powerful [tools](https://docs.roocode.com/basic-usage/using-tools) that can:
Roo Code comes with powerful [tools](https://docs.roocode.com/basic-usage/how-tools-work) that can:
- Read and write files in your project
- Execute commands in your VS Code terminal
@ -180,24 +180,24 @@ Thanks to all our contributors who have helped make Roo Code better!
<!-- START CONTRIBUTORS SECTION - AUTO-GENERATED, DO NOT EDIT MANUALLY -->
| <a href="https://github.com/mrubens"><img src="https://avatars.githubusercontent.com/u/2600?v=4" width="100" height="100" alt="mrubens"/><br /><sub><b>mrubens</b></sub></a> | <a href="https://github.com/saoudrizwan"><img src="https://avatars.githubusercontent.com/u/7799382?v=4" width="100" height="100" alt="saoudrizwan"/><br /><sub><b>saoudrizwan</b></sub></a> | <a href="https://github.com/cte"><img src="https://avatars.githubusercontent.com/u/16332?v=4" width="100" height="100" alt="cte"/><br /><sub><b>cte</b></sub></a> | <a href="https://github.com/samhvw8"><img src="https://avatars.githubusercontent.com/u/12538214?v=4" width="100" height="100" alt="samhvw8"/><br /><sub><b>samhvw8</b></sub></a> | <a href="https://github.com/daniel-lxs"><img src="https://avatars.githubusercontent.com/u/57051444?v=4" width="100" height="100" alt="daniel-lxs"/><br /><sub><b>daniel-lxs</b></sub></a> | <a href="https://github.com/a8trejo"><img src="https://avatars.githubusercontent.com/u/62401433?v=4" width="100" height="100" alt="a8trejo"/><br /><sub><b>a8trejo</b></sub></a> |
| :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |
| <a href="https://github.com/ColemanRoo"><img src="https://avatars.githubusercontent.com/u/117104599?v=4" width="100" height="100" alt="ColemanRoo"/><br /><sub><b>ColemanRoo</b></sub></a> | <a href="https://github.com/stea9499"><img src="https://avatars.githubusercontent.com/u/4163795?v=4" width="100" height="100" alt="stea9499"/><br /><sub><b>stea9499</b></sub></a> | <a href="https://github.com/joemanley201"><img src="https://avatars.githubusercontent.com/u/8299960?v=4" width="100" height="100" alt="joemanley201"/><br /><sub><b>joemanley201</b></sub></a> | <a href="https://github.com/System233"><img src="https://avatars.githubusercontent.com/u/20336040?v=4" width="100" height="100" alt="System233"/><br /><sub><b>System233</b></sub></a> | <a href="https://github.com/nissa-seru"><img src="https://avatars.githubusercontent.com/u/119150866?v=4" width="100" height="100" alt="nissa-seru"/><br /><sub><b>nissa-seru</b></sub></a> | <a href="https://github.com/jquanton"><img src="https://avatars.githubusercontent.com/u/88576563?v=4" width="100" height="100" alt="jquanton"/><br /><sub><b>jquanton</b></sub></a> |
| <a href="https://github.com/NyxJae"><img src="https://avatars.githubusercontent.com/u/52313587?v=4" width="100" height="100" alt="NyxJae"/><br /><sub><b>NyxJae</b></sub></a> | <a href="https://github.com/MuriloFP"><img src="https://avatars.githubusercontent.com/u/50873657?v=4" width="100" height="100" alt="MuriloFP"/><br /><sub><b>MuriloFP</b></sub></a> | <a href="https://github.com/hannesrudolph"><img src="https://avatars.githubusercontent.com/u/49103247?v=4" width="100" height="100" alt="hannesrudolph"/><br /><sub><b>hannesrudolph</b></sub></a> | <a href="https://github.com/d-oit"><img src="https://avatars.githubusercontent.com/u/6849456?v=4" width="100" height="100" alt="d-oit"/><br /><sub><b>d-oit</b></sub></a> | <a href="https://github.com/punkpeye"><img src="https://avatars.githubusercontent.com/u/108313943?v=4" width="100" height="100" alt="punkpeye"/><br /><sub><b>punkpeye</b></sub></a> | <a href="https://github.com/monotykamary"><img src="https://avatars.githubusercontent.com/u/1130103?v=4" width="100" height="100" alt="monotykamary"/><br /><sub><b>monotykamary</b></sub></a> |
| <a href="https://github.com/lloydchang"><img src="https://avatars.githubusercontent.com/u/1329685?v=4" width="100" height="100" alt="lloydchang"/><br /><sub><b>lloydchang</b></sub></a> | <a href="https://github.com/vigneshsubbiah16"><img src="https://avatars.githubusercontent.com/u/51325334?v=4" width="100" height="100" alt="vigneshsubbiah16"/><br /><sub><b>vigneshsubbiah16</b></sub></a> | <a href="https://github.com/Szpadel"><img src="https://avatars.githubusercontent.com/u/1857251?v=4" width="100" height="100" alt="Szpadel"/><br /><sub><b>Szpadel</b></sub></a> | <a href="https://github.com/cannuri"><img src="https://avatars.githubusercontent.com/u/91494156?v=4" width="100" height="100" alt="cannuri"/><br /><sub><b>cannuri</b></sub></a> | <a href="https://github.com/lupuletic"><img src="https://avatars.githubusercontent.com/u/105351510?v=4" width="100" height="100" alt="lupuletic"/><br /><sub><b>lupuletic</b></sub></a> | <a href="https://github.com/feifei325"><img src="https://avatars.githubusercontent.com/u/46489071?v=4" width="100" height="100" alt="feifei325"/><br /><sub><b>feifei325</b></sub></a> |
| <a href="https://github.com/wkordalski"><img src="https://avatars.githubusercontent.com/u/3035587?v=4" width="100" height="100" alt="wkordalski"/><br /><sub><b>wkordalski</b></sub></a> | <a href="https://github.com/Smartsheet-JB-Brown"><img src="https://avatars.githubusercontent.com/u/171734120?v=4" width="100" height="100" alt="Smartsheet-JB-Brown"/><br /><sub><b>Smartsheet-JB-Brown</b></sub></a> | <a href="https://github.com/Premshay"><img src="https://avatars.githubusercontent.com/u/28099628?v=4" width="100" height="100" alt="Premshay"/><br /><sub><b>Premshay</b></sub></a> | <a href="https://github.com/psv2522"><img src="https://avatars.githubusercontent.com/u/87223770?v=4" width="100" height="100" alt="psv2522"/><br /><sub><b>psv2522</b></sub></a> | <a href="https://github.com/KJ7LNW"><img src="https://avatars.githubusercontent.com/u/93454819?v=4" width="100" height="100" alt="KJ7LNW"/><br /><sub><b>KJ7LNW</b></sub></a> | <a href="https://github.com/olweraltuve"><img src="https://avatars.githubusercontent.com/u/39308405?v=4" width="100" height="100" alt="olweraltuve"/><br /><sub><b>olweraltuve</b></sub></a> |
| <a href="https://github.com/qdaxb"><img src="https://avatars.githubusercontent.com/u/4157870?v=4" width="100" height="100" alt="qdaxb"/><br /><sub><b>qdaxb</b></sub></a> | <a href="https://github.com/RaySinner"><img src="https://avatars.githubusercontent.com/u/118297374?v=4" width="100" height="100" alt="RaySinner"/><br /><sub><b>RaySinner</b></sub></a> | <a href="https://github.com/afshawnlotfi"><img src="https://avatars.githubusercontent.com/u/6283745?v=4" width="100" height="100" alt="afshawnlotfi"/><br /><sub><b>afshawnlotfi</b></sub></a> | <a href="https://github.com/emshvac"><img src="https://avatars.githubusercontent.com/u/121588911?v=4" width="100" height="100" alt="emshvac"/><br /><sub><b>emshvac</b></sub></a> | <a href="https://github.com/pdecat"><img src="https://avatars.githubusercontent.com/u/318490?v=4" width="100" height="100" alt="pdecat"/><br /><sub><b>pdecat</b></sub></a> | <a href="https://github.com/pugazhendhi-m"><img src="https://avatars.githubusercontent.com/u/132246623?v=4" width="100" height="100" alt="pugazhendhi-m"/><br /><sub><b>pugazhendhi-m</b></sub></a> |
| <a href="https://github.com/Lunchb0ne"><img src="https://avatars.githubusercontent.com/u/22198661?v=4" width="100" height="100" alt="Lunchb0ne"/><br /><sub><b>Lunchb0ne</b></sub></a> | <a href="https://github.com/aheizi"><img src="https://avatars.githubusercontent.com/u/8243770?v=4" width="100" height="100" alt="aheizi"/><br /><sub><b>aheizi</b></sub></a> | <a href="https://github.com/sammcj"><img src="https://avatars.githubusercontent.com/u/862951?v=4" width="100" height="100" alt="sammcj"/><br /><sub><b>sammcj</b></sub></a> | <a href="https://github.com/dtrugman"><img src="https://avatars.githubusercontent.com/u/2451669?v=4" width="100" height="100" alt="dtrugman"/><br /><sub><b>dtrugman</b></sub></a> | <a href="https://github.com/aitoroses"><img src="https://avatars.githubusercontent.com/u/1699368?v=4" width="100" height="100" alt="aitoroses"/><br /><sub><b>aitoroses</b></sub></a> | <a href="https://github.com/yt3trees"><img src="https://avatars.githubusercontent.com/u/57471763?v=4" width="100" height="100" alt="yt3trees"/><br /><sub><b>yt3trees</b></sub></a> |
| <a href="https://github.com/yongjer"><img src="https://avatars.githubusercontent.com/u/54315206?v=4" width="100" height="100" alt="yongjer"/><br /><sub><b>yongjer</b></sub></a> | <a href="https://github.com/vincentsong"><img src="https://avatars.githubusercontent.com/u/2343574?v=4" width="100" height="100" alt="vincentsong"/><br /><sub><b>vincentsong</b></sub></a> | <a href="https://github.com/teddyOOXX"><img src="https://avatars.githubusercontent.com/u/121077180?v=4" width="100" height="100" alt="teddyOOXX"/><br /><sub><b>teddyOOXX</b></sub></a> | <a href="https://github.com/eonghk"><img src="https://avatars.githubusercontent.com/u/139964?v=4" width="100" height="100" alt="eonghk"/><br /><sub><b>eonghk</b></sub></a> | <a href="https://github.com/arthurauffray"><img src="https://avatars.githubusercontent.com/u/51604173?v=4" width="100" height="100" alt="arthurauffray"/><br /><sub><b>arthurauffray</b></sub></a> | <a href="https://github.com/heyseth"><img src="https://avatars.githubusercontent.com/u/8293842?v=4" width="100" height="100" alt="heyseth"/><br /><sub><b>heyseth</b></sub></a> |
| <a href="https://github.com/anton-otee"><img src="https://avatars.githubusercontent.com/u/149477749?v=4" width="100" height="100" alt="anton-otee"/><br /><sub><b>anton-otee</b></sub></a> | <a href="https://github.com/benzntech"><img src="https://avatars.githubusercontent.com/u/4044180?v=4" width="100" height="100" alt="benzntech"/><br /><sub><b>benzntech</b></sub></a> | <a href="https://github.com/GitlyHallows"><img src="https://avatars.githubusercontent.com/u/136527758?v=4" width="100" height="100" alt="GitlyHallows"/><br /><sub><b>GitlyHallows</b></sub></a> | <a href="https://github.com/jcbdev"><img src="https://avatars.githubusercontent.com/u/17152092?v=4" width="100" height="100" alt="jcbdev"/><br /><sub><b>jcbdev</b></sub></a> | <a href="https://github.com/mdp"><img src="https://avatars.githubusercontent.com/u/2868?v=4" width="100" height="100" alt="mdp"/><br /><sub><b>mdp</b></sub></a> | <a href="https://github.com/napter"><img src="https://avatars.githubusercontent.com/u/6260841?v=4" width="100" height="100" alt="napter"/><br /><sub><b>napter</b></sub></a> |
| <a href="https://github.com/philfung"><img src="https://avatars.githubusercontent.com/u/1054593?v=4" width="100" height="100" alt="philfung"/><br /><sub><b>philfung</b></sub></a> | <a href="https://github.com/AMHesch"><img src="https://avatars.githubusercontent.com/u/4777192?v=4" width="100" height="100" alt="AMHesch"/><br /><sub><b>AMHesch</b></sub></a> | <a href="https://github.com/Yoshino-Yukitaro"><img src="https://avatars.githubusercontent.com/u/67864326?v=4" width="100" height="100" alt="Yoshino-Yukitaro"/><br /><sub><b>Yoshino-Yukitaro</b></sub></a> | <a href="https://github.com/ashktn"><img src="https://avatars.githubusercontent.com/u/6723913?v=4" width="100" height="100" alt="ashktn"/><br /><sub><b>ashktn</b></sub></a> | <a href="https://github.com/bannzai"><img src="https://avatars.githubusercontent.com/u/10897361?v=4" width="100" height="100" alt="bannzai"/><br /><sub><b>bannzai</b></sub></a> | <a href="https://github.com/dairui1"><img src="https://avatars.githubusercontent.com/u/183250644?v=4" width="100" height="100" alt="dairui1"/><br /><sub><b>dairui1</b></sub></a> |
| <a href="https://github.com/dqroid"><img src="https://avatars.githubusercontent.com/u/192424994?v=4" width="100" height="100" alt="dqroid"/><br /><sub><b>dqroid</b></sub></a> | <a href="https://github.com/im47cn"><img src="https://avatars.githubusercontent.com/u/67424112?v=4" width="100" height="100" alt="im47cn"/><br /><sub><b>im47cn</b></sub></a> | <a href="https://github.com/kinandan"><img src="https://avatars.githubusercontent.com/u/186135699?v=4" width="100" height="100" alt="kinandan"/><br /><sub><b>kinandan</b></sub></a> | <a href="https://github.com/kohii"><img src="https://avatars.githubusercontent.com/u/6891780?v=4" width="100" height="100" alt="kohii"/><br /><sub><b>kohii</b></sub></a> | <a href="https://github.com/lightrabbit"><img src="https://avatars.githubusercontent.com/u/1521765?v=4" width="100" height="100" alt="lightrabbit"/><br /><sub><b>lightrabbit</b></sub></a> | <a href="https://github.com/olup"><img src="https://avatars.githubusercontent.com/u/13785588?v=4" width="100" height="100" alt="olup"/><br /><sub><b>olup</b></sub></a> |
| <a href="https://github.com/moqimoqidea"><img src="https://avatars.githubusercontent.com/u/39821951?v=4" width="100" height="100" alt="moqimoqidea"/><br /><sub><b>moqimoqidea</b></sub></a> | <a href="https://github.com/mosleyit"><img src="https://avatars.githubusercontent.com/u/189396442?v=4" width="100" height="100" alt="mosleyit"/><br /><sub><b>mosleyit</b></sub></a> | <a href="https://github.com/oprstchn"><img src="https://avatars.githubusercontent.com/u/16177972?v=4" width="100" height="100" alt="oprstchn"/><br /><sub><b>oprstchn</b></sub></a> | <a href="https://github.com/philipnext"><img src="https://avatars.githubusercontent.com/u/81944499?v=4" width="100" height="100" alt="philipnext"/><br /><sub><b>philipnext</b></sub></a> | <a href="https://github.com/refactorthis"><img src="https://avatars.githubusercontent.com/u/3012240?v=4" width="100" height="100" alt="refactorthis"/><br /><sub><b>refactorthis</b></sub></a> | <a href="https://github.com/samir-nimbly"><img src="https://avatars.githubusercontent.com/u/112695483?v=4" width="100" height="100" alt="samir-nimbly"/><br /><sub><b>samir-nimbly</b></sub></a> |
| <a href="https://github.com/shaybc"><img src="https://avatars.githubusercontent.com/u/8535905?v=4" width="100" height="100" alt="shaybc"/><br /><sub><b>shaybc</b></sub></a> | <a href="https://github.com/shohei-ihaya"><img src="https://avatars.githubusercontent.com/u/25131938?v=4" width="100" height="100" alt="shohei-ihaya"/><br /><sub><b>shohei-ihaya</b></sub></a> | <a href="https://github.com/student20880"><img src="https://avatars.githubusercontent.com/u/74263488?v=4" width="100" height="100" alt="student20880"/><br /><sub><b>student20880</b></sub></a> | <a href="https://github.com/PretzelVector"><img src="https://avatars.githubusercontent.com/u/95664360?v=4" width="100" height="100" alt="PretzelVector"/><br /><sub><b>PretzelVector</b></sub></a> | <a href="https://github.com/adamwlarson"><img src="https://avatars.githubusercontent.com/u/1392315?v=4" width="100" height="100" alt="adamwlarson"/><br /><sub><b>adamwlarson</b></sub></a> | <a href="https://github.com/alarno"><img src="https://avatars.githubusercontent.com/u/4355547?v=4" width="100" height="100" alt="alarno"/><br /><sub><b>alarno</b></sub></a> |
| <a href="https://github.com/andreastempsch"><img src="https://avatars.githubusercontent.com/u/117991125?v=4" width="100" height="100" alt="andreastempsch"/><br /><sub><b>andreastempsch</b></sub></a> | <a href="https://github.com/Atlogit"><img src="https://avatars.githubusercontent.com/u/86947554?v=4" width="100" height="100" alt="Atlogit"/><br /><sub><b>Atlogit</b></sub></a> | <a href="https://github.com/dleen"><img src="https://avatars.githubusercontent.com/u/1297964?v=4" width="100" height="100" alt="dleen"/><br /><sub><b>dleen</b></sub></a> | <a href="https://github.com/diarmidmackenzie"><img src="https://avatars.githubusercontent.com/u/16045703?v=4" width="100" height="100" alt="diarmidmackenzie"/><br /><sub><b>diarmidmackenzie</b></sub></a> | <a href="https://github.com/dbasclpy"><img src="https://avatars.githubusercontent.com/u/139889137?v=4" width="100" height="100" alt="dbasclpy"/><br /><sub><b>dbasclpy</b></sub></a> | <a href="https://github.com/celestial-vault"><img src="https://avatars.githubusercontent.com/u/58194240?v=4" width="100" height="100" alt="celestial-vault"/><br /><sub><b>celestial-vault</b></sub></a> |
| <a href="https://github.com/franekp"><img src="https://avatars.githubusercontent.com/u/9804230?v=4" width="100" height="100" alt="franekp"/><br /><sub><b>franekp</b></sub></a> | <a href="https://github.com/DeXtroTip"><img src="https://avatars.githubusercontent.com/u/21011087?v=4" width="100" height="100" alt="DeXtroTip"/><br /><sub><b>DeXtroTip</b></sub></a> | <a href="https://github.com/hesara"><img src="https://avatars.githubusercontent.com/u/1335918?v=4" width="100" height="100" alt="hesara"/><br /><sub><b>hesara</b></sub></a> | <a href="https://github.com/eltociear"><img src="https://avatars.githubusercontent.com/u/22633385?v=4" width="100" height="100" alt="eltociear"/><br /><sub><b>eltociear</b></sub></a> | <a href="https://github.com/Jdo300"><img src="https://avatars.githubusercontent.com/u/67338327?v=4" width="100" height="100" alt="Jdo300"/><br /><sub><b>Jdo300</b></sub></a> | <a href="https://github.com/libertyteeth"><img src="https://avatars.githubusercontent.com/u/32841567?v=4" width="100" height="100" alt="libertyteeth"/><br /><sub><b>libertyteeth</b></sub></a> |
| <a href="https://github.com/mamertofabian"><img src="https://avatars.githubusercontent.com/u/7698436?v=4" width="100" height="100" alt="mamertofabian"/><br /><sub><b>mamertofabian</b></sub></a> | <a href="https://github.com/marvijo-code"><img src="https://avatars.githubusercontent.com/u/82562019?v=4" width="100" height="100" alt="marvijo-code"/><br /><sub><b>marvijo-code</b></sub></a> | <a href="https://github.com/Sarke"><img src="https://avatars.githubusercontent.com/u/2719310?v=4" width="100" height="100" alt="Sarke"/><br /><sub><b>Sarke</b></sub></a> | <a href="https://github.com/StevenTCramer"><img src="https://avatars.githubusercontent.com/u/357219?v=4" width="100" height="100" alt="StevenTCramer"/><br /><sub><b>StevenTCramer</b></sub></a> | <a href="https://github.com/maekawataiki"><img src="https://avatars.githubusercontent.com/u/26317009?v=4" width="100" height="100" alt="maekawataiki"/><br /><sub><b>maekawataiki</b></sub></a> | <a href="https://github.com/tgfjt"><img src="https://avatars.githubusercontent.com/u/2628239?v=4" width="100" height="100" alt="tgfjt"/><br /><sub><b>tgfjt</b></sub></a> |
| <a href="https://github.com/vladstudio"><img src="https://avatars.githubusercontent.com/u/914320?v=4" width="100" height="100" alt="vladstudio"/><br /><sub><b>vladstudio</b></sub></a> | | | | | |
| <a href="https://github.com/mrubens"><img src="https://avatars.githubusercontent.com/u/2600?v=4" width="100" height="100" alt="mrubens"/><br /><sub><b>mrubens</b></sub></a> | <a href="https://github.com/saoudrizwan"><img src="https://avatars.githubusercontent.com/u/7799382?v=4" width="100" height="100" alt="saoudrizwan"/><br /><sub><b>saoudrizwan</b></sub></a> | <a href="https://github.com/cte"><img src="https://avatars.githubusercontent.com/u/16332?v=4" width="100" height="100" alt="cte"/><br /><sub><b>cte</b></sub></a> | <a href="https://github.com/samhvw8"><img src="https://avatars.githubusercontent.com/u/12538214?v=4" width="100" height="100" alt="samhvw8"/><br /><sub><b>samhvw8</b></sub></a> | <a href="https://github.com/daniel-lxs"><img src="https://avatars.githubusercontent.com/u/57051444?v=4" width="100" height="100" alt="daniel-lxs"/><br /><sub><b>daniel-lxs</b></sub></a> | <a href="https://github.com/a8trejo"><img src="https://avatars.githubusercontent.com/u/62401433?v=4" width="100" height="100" alt="a8trejo"/><br /><sub><b>a8trejo</b></sub></a> |
| :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |
| <a href="https://github.com/ColemanRoo"><img src="https://avatars.githubusercontent.com/u/117104599?v=4" width="100" height="100" alt="ColemanRoo"/><br /><sub><b>ColemanRoo</b></sub></a> | <a href="https://github.com/stea9499"><img src="https://avatars.githubusercontent.com/u/4163795?v=4" width="100" height="100" alt="stea9499"/><br /><sub><b>stea9499</b></sub></a> | <a href="https://github.com/joemanley201"><img src="https://avatars.githubusercontent.com/u/8299960?v=4" width="100" height="100" alt="joemanley201"/><br /><sub><b>joemanley201</b></sub></a> | <a href="https://github.com/System233"><img src="https://avatars.githubusercontent.com/u/20336040?v=4" width="100" height="100" alt="System233"/><br /><sub><b>System233</b></sub></a> | <a href="https://github.com/jquanton"><img src="https://avatars.githubusercontent.com/u/88576563?v=4" width="100" height="100" alt="jquanton"/><br /><sub><b>jquanton</b></sub></a> | <a href="https://github.com/nissa-seru"><img src="https://avatars.githubusercontent.com/u/119150866?v=4" width="100" height="100" alt="nissa-seru"/><br /><sub><b>nissa-seru</b></sub></a> |
| <a href="https://github.com/NyxJae"><img src="https://avatars.githubusercontent.com/u/52313587?v=4" width="100" height="100" alt="NyxJae"/><br /><sub><b>NyxJae</b></sub></a> | <a href="https://github.com/hannesrudolph"><img src="https://avatars.githubusercontent.com/u/49103247?v=4" width="100" height="100" alt="hannesrudolph"/><br /><sub><b>hannesrudolph</b></sub></a> | <a href="https://github.com/MuriloFP"><img src="https://avatars.githubusercontent.com/u/50873657?v=4" width="100" height="100" alt="MuriloFP"/><br /><sub><b>MuriloFP</b></sub></a> | <a href="https://github.com/punkpeye"><img src="https://avatars.githubusercontent.com/u/108313943?v=4" width="100" height="100" alt="punkpeye"/><br /><sub><b>punkpeye</b></sub></a> | <a href="https://github.com/d-oit"><img src="https://avatars.githubusercontent.com/u/6849456?v=4" width="100" height="100" alt="d-oit"/><br /><sub><b>d-oit</b></sub></a> | <a href="https://github.com/monotykamary"><img src="https://avatars.githubusercontent.com/u/1130103?v=4" width="100" height="100" alt="monotykamary"/><br /><sub><b>monotykamary</b></sub></a> |
| <a href="https://github.com/vigneshsubbiah16"><img src="https://avatars.githubusercontent.com/u/51325334?v=4" width="100" height="100" alt="vigneshsubbiah16"/><br /><sub><b>vigneshsubbiah16</b></sub></a> | <a href="https://github.com/lloydchang"><img src="https://avatars.githubusercontent.com/u/1329685?v=4" width="100" height="100" alt="lloydchang"/><br /><sub><b>lloydchang</b></sub></a> | <a href="https://github.com/cannuri"><img src="https://avatars.githubusercontent.com/u/91494156?v=4" width="100" height="100" alt="cannuri"/><br /><sub><b>cannuri</b></sub></a> | <a href="https://github.com/Szpadel"><img src="https://avatars.githubusercontent.com/u/1857251?v=4" width="100" height="100" alt="Szpadel"/><br /><sub><b>Szpadel</b></sub></a> | <a href="https://github.com/psv2522"><img src="https://avatars.githubusercontent.com/u/87223770?v=4" width="100" height="100" alt="psv2522"/><br /><sub><b>psv2522</b></sub></a> | <a href="https://github.com/Premshay"><img src="https://avatars.githubusercontent.com/u/28099628?v=4" width="100" height="100" alt="Premshay"/><br /><sub><b>Premshay</b></sub></a> |
| <a href="https://github.com/Smartsheet-JB-Brown"><img src="https://avatars.githubusercontent.com/u/171734120?v=4" width="100" height="100" alt="Smartsheet-JB-Brown"/><br /><sub><b>Smartsheet-JB-Brown</b></sub></a> | <a href="https://github.com/wkordalski"><img src="https://avatars.githubusercontent.com/u/3035587?v=4" width="100" height="100" alt="wkordalski"/><br /><sub><b>wkordalski</b></sub></a> | <a href="https://github.com/qdaxb"><img src="https://avatars.githubusercontent.com/u/4157870?v=4" width="100" height="100" alt="qdaxb"/><br /><sub><b>qdaxb</b></sub></a> | <a href="https://github.com/feifei325"><img src="https://avatars.githubusercontent.com/u/46489071?v=4" width="100" height="100" alt="feifei325"/><br /><sub><b>feifei325</b></sub></a> | <a href="https://github.com/lupuletic"><img src="https://avatars.githubusercontent.com/u/105351510?v=4" width="100" height="100" alt="lupuletic"/><br /><sub><b>lupuletic</b></sub></a> | <a href="https://github.com/KJ7LNW"><img src="https://avatars.githubusercontent.com/u/93454819?v=4" width="100" height="100" alt="KJ7LNW"/><br /><sub><b>KJ7LNW</b></sub></a> |
| <a href="https://github.com/olweraltuve"><img src="https://avatars.githubusercontent.com/u/39308405?v=4" width="100" height="100" alt="olweraltuve"/><br /><sub><b>olweraltuve</b></sub></a> | <a href="https://github.com/RaySinner"><img src="https://avatars.githubusercontent.com/u/118297374?v=4" width="100" height="100" alt="RaySinner"/><br /><sub><b>RaySinner</b></sub></a> | <a href="https://github.com/pugazhendhi-m"><img src="https://avatars.githubusercontent.com/u/132246623?v=4" width="100" height="100" alt="pugazhendhi-m"/><br /><sub><b>pugazhendhi-m</b></sub></a> | <a href="https://github.com/pdecat"><img src="https://avatars.githubusercontent.com/u/318490?v=4" width="100" height="100" alt="pdecat"/><br /><sub><b>pdecat</b></sub></a> | <a href="https://github.com/emshvac"><img src="https://avatars.githubusercontent.com/u/121588911?v=4" width="100" height="100" alt="emshvac"/><br /><sub><b>emshvac</b></sub></a> | <a href="https://github.com/afshawnlotfi"><img src="https://avatars.githubusercontent.com/u/6283745?v=4" width="100" height="100" alt="afshawnlotfi"/><br /><sub><b>afshawnlotfi</b></sub></a> |
| <a href="https://github.com/aitoroses"><img src="https://avatars.githubusercontent.com/u/1699368?v=4" width="100" height="100" alt="aitoroses"/><br /><sub><b>aitoroses</b></sub></a> | <a href="https://github.com/dtrugman"><img src="https://avatars.githubusercontent.com/u/2451669?v=4" width="100" height="100" alt="dtrugman"/><br /><sub><b>dtrugman</b></sub></a> | <a href="https://github.com/diarmidmackenzie"><img src="https://avatars.githubusercontent.com/u/16045703?v=4" width="100" height="100" alt="diarmidmackenzie"/><br /><sub><b>diarmidmackenzie</b></sub></a> | <a href="https://github.com/sammcj"><img src="https://avatars.githubusercontent.com/u/862951?v=4" width="100" height="100" alt="sammcj"/><br /><sub><b>sammcj</b></sub></a> | <a href="https://github.com/aheizi"><img src="https://avatars.githubusercontent.com/u/8243770?v=4" width="100" height="100" alt="aheizi"/><br /><sub><b>aheizi</b></sub></a> | <a href="https://github.com/Lunchb0ne"><img src="https://avatars.githubusercontent.com/u/22198661?v=4" width="100" height="100" alt="Lunchb0ne"/><br /><sub><b>Lunchb0ne</b></sub></a> |
| <a href="https://github.com/yt3trees"><img src="https://avatars.githubusercontent.com/u/57471763?v=4" width="100" height="100" alt="yt3trees"/><br /><sub><b>yt3trees</b></sub></a> | <a href="https://github.com/yongjer"><img src="https://avatars.githubusercontent.com/u/54315206?v=4" width="100" height="100" alt="yongjer"/><br /><sub><b>yongjer</b></sub></a> | <a href="https://github.com/vincentsong"><img src="https://avatars.githubusercontent.com/u/2343574?v=4" width="100" height="100" alt="vincentsong"/><br /><sub><b>vincentsong</b></sub></a> | <a href="https://github.com/teddyOOXX"><img src="https://avatars.githubusercontent.com/u/121077180?v=4" width="100" height="100" alt="teddyOOXX"/><br /><sub><b>teddyOOXX</b></sub></a> | <a href="https://github.com/eonghk"><img src="https://avatars.githubusercontent.com/u/139964?v=4" width="100" height="100" alt="eonghk"/><br /><sub><b>eonghk</b></sub></a> | <a href="https://github.com/arthurauffray"><img src="https://avatars.githubusercontent.com/u/51604173?v=4" width="100" height="100" alt="arthurauffray"/><br /><sub><b>arthurauffray</b></sub></a> |
| <a href="https://github.com/heyseth"><img src="https://avatars.githubusercontent.com/u/8293842?v=4" width="100" height="100" alt="heyseth"/><br /><sub><b>heyseth</b></sub></a> | <a href="https://github.com/anton-otee"><img src="https://avatars.githubusercontent.com/u/149477749?v=4" width="100" height="100" alt="anton-otee"/><br /><sub><b>anton-otee</b></sub></a> | <a href="https://github.com/benzntech"><img src="https://avatars.githubusercontent.com/u/4044180?v=4" width="100" height="100" alt="benzntech"/><br /><sub><b>benzntech</b></sub></a> | <a href="https://github.com/GitlyHallows"><img src="https://avatars.githubusercontent.com/u/136527758?v=4" width="100" height="100" alt="GitlyHallows"/><br /><sub><b>GitlyHallows</b></sub></a> | <a href="https://github.com/jcbdev"><img src="https://avatars.githubusercontent.com/u/17152092?v=4" width="100" height="100" alt="jcbdev"/><br /><sub><b>jcbdev</b></sub></a> | <a href="https://github.com/Chenjiayuan195"><img src="https://avatars.githubusercontent.com/u/30591313?v=4" width="100" height="100" alt="Chenjiayuan195"/><br /><sub><b>Chenjiayuan195</b></sub></a> |
| <a href="https://github.com/mdp"><img src="https://avatars.githubusercontent.com/u/2868?v=4" width="100" height="100" alt="mdp"/><br /><sub><b>mdp</b></sub></a> | <a href="https://github.com/napter"><img src="https://avatars.githubusercontent.com/u/6260841?v=4" width="100" height="100" alt="napter"/><br /><sub><b>napter</b></sub></a> | <a href="https://github.com/philfung"><img src="https://avatars.githubusercontent.com/u/1054593?v=4" width="100" height="100" alt="philfung"/><br /><sub><b>philfung</b></sub></a> | <a href="https://github.com/AMHesch"><img src="https://avatars.githubusercontent.com/u/4777192?v=4" width="100" height="100" alt="AMHesch"/><br /><sub><b>AMHesch</b></sub></a> | <a href="https://github.com/vladstudio"><img src="https://avatars.githubusercontent.com/u/914320?v=4" width="100" height="100" alt="vladstudio"/><br /><sub><b>vladstudio</b></sub></a> | <a href="https://github.com/Yoshino-Yukitaro"><img src="https://avatars.githubusercontent.com/u/67864326?v=4" width="100" height="100" alt="Yoshino-Yukitaro"/><br /><sub><b>Yoshino-Yukitaro</b></sub></a> |
| <a href="https://github.com/ashktn"><img src="https://avatars.githubusercontent.com/u/6723913?v=4" width="100" height="100" alt="ashktn"/><br /><sub><b>ashktn</b></sub></a> | <a href="https://github.com/bannzai"><img src="https://avatars.githubusercontent.com/u/10897361?v=4" width="100" height="100" alt="bannzai"/><br /><sub><b>bannzai</b></sub></a> | <a href="https://github.com/dairui1"><img src="https://avatars.githubusercontent.com/u/183250644?v=4" width="100" height="100" alt="dairui1"/><br /><sub><b>dairui1</b></sub></a> | <a href="https://github.com/dqroid"><img src="https://avatars.githubusercontent.com/u/192424994?v=4" width="100" height="100" alt="dqroid"/><br /><sub><b>dqroid</b></sub></a> | <a href="https://github.com/im47cn"><img src="https://avatars.githubusercontent.com/u/67424112?v=4" width="100" height="100" alt="im47cn"/><br /><sub><b>im47cn</b></sub></a> | <a href="https://github.com/kinandan"><img src="https://avatars.githubusercontent.com/u/186135699?v=4" width="100" height="100" alt="kinandan"/><br /><sub><b>kinandan</b></sub></a> |
| <a href="https://github.com/kohii"><img src="https://avatars.githubusercontent.com/u/6891780?v=4" width="100" height="100" alt="kohii"/><br /><sub><b>kohii</b></sub></a> | <a href="https://github.com/lightrabbit"><img src="https://avatars.githubusercontent.com/u/1521765?v=4" width="100" height="100" alt="lightrabbit"/><br /><sub><b>lightrabbit</b></sub></a> | <a href="https://github.com/olup"><img src="https://avatars.githubusercontent.com/u/13785588?v=4" width="100" height="100" alt="olup"/><br /><sub><b>olup</b></sub></a> | <a href="https://github.com/moqimoqidea"><img src="https://avatars.githubusercontent.com/u/39821951?v=4" width="100" height="100" alt="moqimoqidea"/><br /><sub><b>moqimoqidea</b></sub></a> | <a href="https://github.com/mosleyit"><img src="https://avatars.githubusercontent.com/u/189396442?v=4" width="100" height="100" alt="mosleyit"/><br /><sub><b>mosleyit</b></sub></a> | <a href="https://github.com/oprstchn"><img src="https://avatars.githubusercontent.com/u/16177972?v=4" width="100" height="100" alt="oprstchn"/><br /><sub><b>oprstchn</b></sub></a> |
| <a href="https://github.com/philipnext"><img src="https://avatars.githubusercontent.com/u/81944499?v=4" width="100" height="100" alt="philipnext"/><br /><sub><b>philipnext</b></sub></a> | <a href="https://github.com/refactorthis"><img src="https://avatars.githubusercontent.com/u/3012240?v=4" width="100" height="100" alt="refactorthis"/><br /><sub><b>refactorthis</b></sub></a> | <a href="https://github.com/samir-nimbly"><img src="https://avatars.githubusercontent.com/u/112695483?v=4" width="100" height="100" alt="samir-nimbly"/><br /><sub><b>samir-nimbly</b></sub></a> | <a href="https://github.com/shaybc"><img src="https://avatars.githubusercontent.com/u/8535905?v=4" width="100" height="100" alt="shaybc"/><br /><sub><b>shaybc</b></sub></a> | <a href="https://github.com/shohei-ihaya"><img src="https://avatars.githubusercontent.com/u/25131938?v=4" width="100" height="100" alt="shohei-ihaya"/><br /><sub><b>shohei-ihaya</b></sub></a> | <a href="https://github.com/student20880"><img src="https://avatars.githubusercontent.com/u/74263488?v=4" width="100" height="100" alt="student20880"/><br /><sub><b>student20880</b></sub></a> |
| <a href="https://github.com/cdlliuy"><img src="https://avatars.githubusercontent.com/u/17263036?v=4" width="100" height="100" alt="cdlliuy"/><br /><sub><b>cdlliuy</b></sub></a> | <a href="https://github.com/PretzelVector"><img src="https://avatars.githubusercontent.com/u/95664360?v=4" width="100" height="100" alt="PretzelVector"/><br /><sub><b>PretzelVector</b></sub></a> | <a href="https://github.com/nevermorec"><img src="https://avatars.githubusercontent.com/u/22953064?v=4" width="100" height="100" alt="nevermorec"/><br /><sub><b>nevermorec</b></sub></a> | <a href="https://github.com/adamwlarson"><img src="https://avatars.githubusercontent.com/u/1392315?v=4" width="100" height="100" alt="adamwlarson"/><br /><sub><b>adamwlarson</b></sub></a> | <a href="https://github.com/alarno"><img src="https://avatars.githubusercontent.com/u/4355547?v=4" width="100" height="100" alt="alarno"/><br /><sub><b>alarno</b></sub></a> | <a href="https://github.com/andreastempsch"><img src="https://avatars.githubusercontent.com/u/117991125?v=4" width="100" height="100" alt="andreastempsch"/><br /><sub><b>andreastempsch</b></sub></a> |
| <a href="https://github.com/Atlogit"><img src="https://avatars.githubusercontent.com/u/86947554?v=4" width="100" height="100" alt="Atlogit"/><br /><sub><b>Atlogit</b></sub></a> | <a href="https://github.com/chadgauth"><img src="https://avatars.githubusercontent.com/u/2413356?v=4" width="100" height="100" alt="chadgauth"/><br /><sub><b>chadgauth</b></sub></a> | <a href="https://github.com/dleen"><img src="https://avatars.githubusercontent.com/u/1297964?v=4" width="100" height="100" alt="dleen"/><br /><sub><b>dleen</b></sub></a> | <a href="https://github.com/dbasclpy"><img src="https://avatars.githubusercontent.com/u/139889137?v=4" width="100" height="100" alt="dbasclpy"/><br /><sub><b>dbasclpy</b></sub></a> | <a href="https://github.com/celestial-vault"><img src="https://avatars.githubusercontent.com/u/58194240?v=4" width="100" height="100" alt="celestial-vault"/><br /><sub><b>celestial-vault</b></sub></a> | <a href="https://github.com/franekp"><img src="https://avatars.githubusercontent.com/u/9804230?v=4" width="100" height="100" alt="franekp"/><br /><sub><b>franekp</b></sub></a> |
| <a href="https://github.com/DeXtroTip"><img src="https://avatars.githubusercontent.com/u/21011087?v=4" width="100" height="100" alt="DeXtroTip"/><br /><sub><b>DeXtroTip</b></sub></a> | <a href="https://github.com/hesara"><img src="https://avatars.githubusercontent.com/u/1335918?v=4" width="100" height="100" alt="hesara"/><br /><sub><b>hesara</b></sub></a> | <a href="https://github.com/eltociear"><img src="https://avatars.githubusercontent.com/u/22633385?v=4" width="100" height="100" alt="eltociear"/><br /><sub><b>eltociear</b></sub></a> | <a href="https://github.com/Jdo300"><img src="https://avatars.githubusercontent.com/u/67338327?v=4" width="100" height="100" alt="Jdo300"/><br /><sub><b>Jdo300</b></sub></a> | <a href="https://github.com/libertyteeth"><img src="https://avatars.githubusercontent.com/u/32841567?v=4" width="100" height="100" alt="libertyteeth"/><br /><sub><b>libertyteeth</b></sub></a> | <a href="https://github.com/mamertofabian"><img src="https://avatars.githubusercontent.com/u/7698436?v=4" width="100" height="100" alt="mamertofabian"/><br /><sub><b>mamertofabian</b></sub></a> |
| <a href="https://github.com/marvijo-code"><img src="https://avatars.githubusercontent.com/u/82562019?v=4" width="100" height="100" alt="marvijo-code"/><br /><sub><b>marvijo-code</b></sub></a> | <a href="https://github.com/kvokka"><img src="https://avatars.githubusercontent.com/u/15954013?v=4" width="100" height="100" alt="kvokka"/><br /><sub><b>kvokka</b></sub></a> | | | | |
<!-- END CONTRIBUTORS SECTION -->

531
package-lock.json generated
View file

@ -1,12 +1,12 @@
{
"name": "roo-cline",
"version": "3.10.3",
"version": "3.10.5",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "roo-cline",
"version": "3.10.3",
"version": "3.10.5",
"dependencies": {
"@anthropic-ai/bedrock-sdk": "^0.10.2",
"@anthropic-ai/sdk": "^0.37.0",
@ -2433,25 +2433,27 @@
}
},
"node_modules/@babel/helpers": {
"version": "7.26.0",
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.0.tgz",
"integrity": "sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==",
"version": "7.26.10",
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.10.tgz",
"integrity": "sha512-UPYc3SauzZ3JGgj87GgZ89JVdC5dj0AoetR5Bw6wj4niittNyFh6+eOGonYvJ1ao6B8lEa3Q3klS7ADZ53bc5g==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/template": "^7.25.9",
"@babel/types": "^7.26.0"
"@babel/template": "^7.26.9",
"@babel/types": "^7.26.10"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/parser": {
"version": "7.26.3",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.3.tgz",
"integrity": "sha512-WJ/CvmY8Mea8iDXo6a7RK2wbmJITT5fN3BEkRuFlxVyNx8jOKIIhmC4fSkTcPcf8JyavbBwIe6OpiCOBXt/IcA==",
"version": "7.26.10",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.10.tgz",
"integrity": "sha512-6aQR2zGE/QFi8JpDLjUZEPYOs7+mhKXm86VaKFiLP35JQwQb6bwUE+XbvkH0EptsYhbNBSUGaUBLKqxH1xSgsA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/types": "^7.26.3"
"@babel/types": "^7.26.10"
},
"bin": {
"parser": "bin/babel-parser.js"
@ -2694,14 +2696,15 @@
}
},
"node_modules/@babel/template": {
"version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz",
"integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==",
"version": "7.26.9",
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.26.9.tgz",
"integrity": "sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/code-frame": "^7.25.9",
"@babel/parser": "^7.25.9",
"@babel/types": "^7.25.9"
"@babel/code-frame": "^7.26.2",
"@babel/parser": "^7.26.9",
"@babel/types": "^7.26.9"
},
"engines": {
"node": ">=6.9.0"
@ -2735,10 +2738,11 @@
}
},
"node_modules/@babel/types": {
"version": "7.26.3",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.3.tgz",
"integrity": "sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==",
"version": "7.26.10",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.10.tgz",
"integrity": "sha512-emqcG3vHrpxUKTrxcblR36dcrcoRDvKmnL/dCL6ZsHaShW80qxCAcNhzQZrpeM765VzEos+xOi4s+r4IXzTwdQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-string-parser": "^7.25.9",
"@babel/helper-validator-identifier": "^7.25.9"
@ -3174,14 +3178,83 @@
"@noble/ciphers": "^1.0.0"
}
},
"node_modules/@esbuild/darwin-arm64": {
"version": "0.24.0",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.24.0.tgz",
"integrity": "sha512-CKyDpRbK1hXwv79soeTJNHb5EiG6ct3efd/FTPdzOWdbZZfGhpbcqIpiD0+vwmpu0wTIL97ZRPZu8vUt46nBSw==",
"node_modules/@esbuild/aix-ppc64": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.2.tgz",
"integrity": "sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==",
"cpu": [
"ppc64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"aix"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/android-arm": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.24.2.tgz",
"integrity": "sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/android-arm64": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.24.2.tgz",
"integrity": "sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/android-x64": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.24.2.tgz",
"integrity": "sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/darwin-arm64": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.24.2.tgz",
"integrity": "sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
@ -3190,6 +3263,346 @@
"node": ">=18"
}
},
"node_modules/@esbuild/darwin-x64": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.24.2.tgz",
"integrity": "sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/freebsd-arm64": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.2.tgz",
"integrity": "sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/freebsd-x64": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.24.2.tgz",
"integrity": "sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-arm": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.24.2.tgz",
"integrity": "sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-arm64": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.24.2.tgz",
"integrity": "sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-ia32": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.24.2.tgz",
"integrity": "sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw==",
"cpu": [
"ia32"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-loong64": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.24.2.tgz",
"integrity": "sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ==",
"cpu": [
"loong64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-mips64el": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.24.2.tgz",
"integrity": "sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw==",
"cpu": [
"mips64el"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-ppc64": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.24.2.tgz",
"integrity": "sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw==",
"cpu": [
"ppc64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-riscv64": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.24.2.tgz",
"integrity": "sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q==",
"cpu": [
"riscv64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-s390x": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.24.2.tgz",
"integrity": "sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw==",
"cpu": [
"s390x"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-x64": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.24.2.tgz",
"integrity": "sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/netbsd-arm64": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.24.2.tgz",
"integrity": "sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"netbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/netbsd-x64": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.24.2.tgz",
"integrity": "sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"netbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/openbsd-arm64": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.2.tgz",
"integrity": "sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"openbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/openbsd-x64": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.24.2.tgz",
"integrity": "sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"openbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/sunos-x64": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.24.2.tgz",
"integrity": "sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"sunos"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/win32-arm64": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.24.2.tgz",
"integrity": "sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/win32-ia32": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.24.2.tgz",
"integrity": "sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA==",
"cpu": [
"ia32"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/win32-x64": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.24.2.tgz",
"integrity": "sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@eslint-community/eslint-utils": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz",
@ -6713,9 +7126,10 @@
}
},
"node_modules/axios": {
"version": "1.7.9",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz",
"integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==",
"version": "1.8.3",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.8.3.tgz",
"integrity": "sha512-iP4DebzoNlP/YN2dpwCgb8zoCmhtkajzS48JvwmkSkXvPI3DHc7m+XYL5tGnSlJtR6nImXZmdCuN5aP8dh1d8A==",
"license": "MIT",
"dependencies": {
"follow-redirects": "^1.15.6",
"form-data": "^4.0.0",
@ -8556,11 +8970,12 @@
}
},
"node_modules/esbuild": {
"version": "0.24.0",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.0.tgz",
"integrity": "sha512-FuLPevChGDshgSicjisSooU0cemp/sGXR841D5LHMB7mTVOmsEHcAxaH3irL53+8YDIeVNQEySh4DaYU/iuPqQ==",
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.2.tgz",
"integrity": "sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"bin": {
"esbuild": "bin/esbuild"
},
@ -8568,30 +8983,31 @@
"node": ">=18"
},
"optionalDependencies": {
"@esbuild/aix-ppc64": "0.24.0",
"@esbuild/android-arm": "0.24.0",
"@esbuild/android-arm64": "0.24.0",
"@esbuild/android-x64": "0.24.0",
"@esbuild/darwin-arm64": "0.24.0",
"@esbuild/darwin-x64": "0.24.0",
"@esbuild/freebsd-arm64": "0.24.0",
"@esbuild/freebsd-x64": "0.24.0",
"@esbuild/linux-arm": "0.24.0",
"@esbuild/linux-arm64": "0.24.0",
"@esbuild/linux-ia32": "0.24.0",
"@esbuild/linux-loong64": "0.24.0",
"@esbuild/linux-mips64el": "0.24.0",
"@esbuild/linux-ppc64": "0.24.0",
"@esbuild/linux-riscv64": "0.24.0",
"@esbuild/linux-s390x": "0.24.0",
"@esbuild/linux-x64": "0.24.0",
"@esbuild/netbsd-x64": "0.24.0",
"@esbuild/openbsd-arm64": "0.24.0",
"@esbuild/openbsd-x64": "0.24.0",
"@esbuild/sunos-x64": "0.24.0",
"@esbuild/win32-arm64": "0.24.0",
"@esbuild/win32-ia32": "0.24.0",
"@esbuild/win32-x64": "0.24.0"
"@esbuild/aix-ppc64": "0.24.2",
"@esbuild/android-arm": "0.24.2",
"@esbuild/android-arm64": "0.24.2",
"@esbuild/android-x64": "0.24.2",
"@esbuild/darwin-arm64": "0.24.2",
"@esbuild/darwin-x64": "0.24.2",
"@esbuild/freebsd-arm64": "0.24.2",
"@esbuild/freebsd-x64": "0.24.2",
"@esbuild/linux-arm": "0.24.2",
"@esbuild/linux-arm64": "0.24.2",
"@esbuild/linux-ia32": "0.24.2",
"@esbuild/linux-loong64": "0.24.2",
"@esbuild/linux-mips64el": "0.24.2",
"@esbuild/linux-ppc64": "0.24.2",
"@esbuild/linux-riscv64": "0.24.2",
"@esbuild/linux-s390x": "0.24.2",
"@esbuild/linux-x64": "0.24.2",
"@esbuild/netbsd-arm64": "0.24.2",
"@esbuild/netbsd-x64": "0.24.2",
"@esbuild/openbsd-arm64": "0.24.2",
"@esbuild/openbsd-x64": "0.24.2",
"@esbuild/sunos-x64": "0.24.2",
"@esbuild/win32-arm64": "0.24.2",
"@esbuild/win32-ia32": "0.24.2",
"@esbuild/win32-x64": "0.24.2"
}
},
"node_modules/escalade": {
@ -15721,9 +16137,10 @@
"integrity": "sha512-GMXzWtsc57XAtguZgaQViUOzs0KTkk8ojr3/xAxXLITqf/3EMwxC0inyETfDFjH/Krbhuep0HNbbjI9i/q3F3g=="
},
"node_modules/undici": {
"version": "6.21.0",
"resolved": "https://registry.npmjs.org/undici/-/undici-6.21.0.tgz",
"integrity": "sha512-BUgJXc752Kou3oOIuU1i+yZZypyZRqNPW0vqoMPl8VaoalSfeR0D8/t4iAS3yirs79SSMTxTag+ZC86uswv+Cw==",
"version": "6.21.2",
"resolved": "https://registry.npmjs.org/undici/-/undici-6.21.2.tgz",
"integrity": "sha512-uROZWze0R0itiAKVPsYhFov9LxrPMHLMEQFszeI2gCN6bnIIZ8twzBCJcN2LJrBBLfrP0t1FW0g+JmKVl8Vk1g==",
"license": "MIT",
"engines": {
"node": ">=18.17"
}

View file

@ -3,7 +3,7 @@
"displayName": "Roo Code (prev. Roo Cline)",
"description": "A whole dev team of AI agents in your editor.",
"publisher": "RooVeterinaryInc",
"version": "3.10.3",
"version": "3.10.5",
"icon": "assets/icons/rocket.png",
"galleryBanner": {
"color": "#617A91",
@ -140,6 +140,11 @@
"title": "Add To Context",
"category": "Roo Code"
},
{
"command": "roo-cline.newTask",
"title": "New Task",
"category": "Roo Code"
},
{
"command": "roo-cline.terminalAddToContext",
"title": "Add Terminal Content to Context",
@ -164,6 +169,11 @@
"command": "roo-cline.terminalExplainCommandInCurrentTask",
"title": "Explain This Command (Current Task)",
"category": "Terminal"
},
{
"command": "roo-cline.setCustomStoragePath",
"title": "Set Custom Storage Path",
"category": "Roo Code"
}
],
"menus": {
@ -288,6 +298,11 @@
}
},
"description": "Settings for VSCode Language Model API"
},
"roo-cline.customStoragePath": {
"type": "string",
"default": "",
"description": "Custom storage path. Leave empty to use the default location. Supports absolute paths (e.g. 'D:\\RooCodeStorage')"
}
}
}

View file

@ -0,0 +1,22 @@
import * as vscode from "vscode"
import { COMMAND_IDS } from "../core/CodeActionProvider"
import { ClineProvider } from "../core/webview/ClineProvider"
import { t } from "../i18n"
export const handleNewTask = async (params: { prompt?: string } | null | undefined) => {
let prompt = params?.prompt
if (!prompt) {
prompt = await vscode.window.showInputBox({
prompt: t("common:input.task_prompt"),
placeHolder: t("common:input.task_placeholder"),
})
}
if (!prompt) {
await vscode.commands.executeCommand("roo-cline.SidebarProvider.focus")
return
}
await ClineProvider.handleCodeAction(COMMAND_IDS.NEW_TASK, "NEW_TASK", {
userInput: prompt,
})
}

View file

@ -4,6 +4,7 @@ import delay from "delay"
import { ClineProvider } from "../core/webview/ClineProvider"
import { registerHumanRelayCallback, unregisterHumanRelayCallback, handleHumanRelayResponse } from "./humanRelay"
import { handleNewTask } from "./handleTask"
// Store panel references in both modes
let sidebarPanel: vscode.WebviewView | undefined = undefined
@ -85,6 +86,11 @@ const getCommandsMap = ({ context, outputChannel, provider }: RegisterCommandOpt
"roo-cline.registerHumanRelayCallback": registerHumanRelayCallback,
"roo-cline.unregisterHumanRelayCallback": unregisterHumanRelayCallback,
"roo-cline.handleHumanRelayResponse": handleHumanRelayResponse,
"roo-cline.newTask": handleNewTask,
"roo-cline.setCustomStoragePath": async () => {
const { promptForCustomStoragePath } = await import("../shared/storagePathManager")
await promptForCustomStoragePath()
},
}
}

View file

@ -11,6 +11,7 @@ import pWaitFor from "p-wait-for"
import getFolderSize from "get-folder-size"
import { serializeError } from "serialize-error"
import * as vscode from "vscode"
import { isPathOutsideWorkspace } from "../utils/pathUtils"
import { TokenUsage } from "../exports/roo-code"
import { ApiHandler, buildApiHandler } from "../api"
@ -29,6 +30,7 @@ import {
everyLineHasLineNumbers,
} from "../integrations/misc/extract-text"
import { countFileLines } from "../integrations/misc/line-counter"
import { fetchInstructions } from "./prompts/instructions/instructions"
import { ExitCodeDetails } from "../integrations/terminal/TerminalProcess"
import { Terminal } from "../integrations/terminal/Terminal"
import { TerminalRegistry } from "../integrations/terminal/TerminalRegistry"
@ -294,9 +296,10 @@ export class Cline extends EventEmitter<ClineEvents> {
if (!globalStoragePath) {
throw new Error("Global storage uri is invalid")
}
const taskDir = path.join(globalStoragePath, "tasks", this.taskId)
await fs.mkdir(taskDir, { recursive: true })
return taskDir
// Use storagePathManager to retrieve the task storage directory
const { getTaskDirectoryPath } = await import("../shared/storagePathManager")
return getTaskDirectoryPath(globalStoragePath, this.taskId)
}
private async getSavedApiConversationHistory(): Promise<Anthropic.MessageParam[]> {
@ -1369,6 +1372,8 @@ export class Cline extends EventEmitter<ClineEvents> {
return `[${block.name} for '${block.params.command}']`
case "read_file":
return `[${block.name} for '${block.params.path}']`
case "fetch_instructions":
return `[${block.name} for '${block.params.task}']`
case "write_to_file":
return `[${block.name} for '${block.params.path}']`
case "apply_diff":
@ -1605,9 +1610,14 @@ export class Cline extends EventEmitter<ClineEvents> {
}
}
// Determine if the path is outside the workspace
const fullPath = relPath ? path.resolve(this.cwd, removeClosingTag("path", relPath)) : ""
const isOutsideWorkspace = isPathOutsideWorkspace(fullPath)
const sharedMessageProps: ClineSayTool = {
tool: fileExists ? "editedExistingFile" : "newFileCreated",
path: getReadablePath(this.cwd, removeClosingTag("path", relPath)),
isOutsideWorkspace,
}
try {
if (block.partial) {
@ -2244,9 +2254,15 @@ export class Cline extends EventEmitter<ClineEvents> {
const relPath: string | undefined = block.params.path
const startLineStr: string | undefined = block.params.start_line
const endLineStr: string | undefined = block.params.end_line
// Get the full path and determine if it's outside the workspace
const fullPath = relPath ? path.resolve(this.cwd, removeClosingTag("path", relPath)) : ""
const isOutsideWorkspace = isPathOutsideWorkspace(fullPath)
const sharedMessageProps: ClineSayTool = {
tool: "readFile",
path: getReadablePath(this.cwd, removeClosingTag("path", relPath)),
isOutsideWorkspace,
}
try {
if (block.partial) {
@ -2383,6 +2399,62 @@ export class Cline extends EventEmitter<ClineEvents> {
}
}
case "fetch_instructions": {
const task: string | undefined = block.params.task
const sharedMessageProps: ClineSayTool = {
tool: "fetchInstructions",
content: task,
}
try {
if (block.partial) {
const partialMessage = JSON.stringify({
...sharedMessageProps,
content: undefined,
} satisfies ClineSayTool)
await this.ask("tool", partialMessage, block.partial).catch(() => {})
break
} else {
if (!task) {
this.consecutiveMistakeCount++
pushToolResult(
await this.sayAndCreateMissingParamError("fetch_instructions", "task"),
)
break
}
this.consecutiveMistakeCount = 0
const completeMessage = JSON.stringify({
...sharedMessageProps,
content: task,
} satisfies ClineSayTool)
const didApprove = await askApproval("tool", completeMessage)
if (!didApprove) {
break
}
// now fetch the content and provide it to the agent.
const provider = this.providerRef.deref()
const mcpHub = provider?.getMcpHub()
if (!mcpHub) {
throw new Error("MCP hub not available")
}
const diffStrategy = this.diffStrategy
const context = provider?.context
const content = await fetchInstructions(task, { mcpHub, diffStrategy, context })
if (!content) {
pushToolResult(formatResponse.toolError(`Invalid instructions request: ${task}`))
break
}
pushToolResult(content)
break
}
} catch (error) {
await handleError("fetch instructions", error)
break
}
}
case "list_files": {
const relDirPath: string | undefined = block.params.path
const recursiveRaw: string | undefined = block.params.recursive
@ -2873,8 +2945,16 @@ export class Cline extends EventEmitter<ClineEvents> {
})
.filter(Boolean)
.join("\n\n") || "(Empty response)"
await this.say("mcp_server_response", resourceResultPretty)
pushToolResult(formatResponse.toolResult(resourceResultPretty))
// handle images (image must contain mimetype and blob)
let images: string[] = []
resourceResult?.contents.forEach((item) => {
if (item.mimeType?.startsWith("image") && item.blob) {
images.push(item.blob)
}
})
await this.say("mcp_server_response", resourceResultPretty, images)
pushToolResult(formatResponse.toolResult(resourceResultPretty, images))
break
}
} catch (error) {
@ -3124,7 +3204,6 @@ export class Cline extends EventEmitter<ClineEvents> {
telemetryService.captureTaskCompleted(this.taskId)
this.emit("taskCompleted", this.taskId, this.getTokenUsage())
console.log("TASK COMPLETED | event emitted")
await this.ask(
"command",
@ -3161,7 +3240,6 @@ export class Cline extends EventEmitter<ClineEvents> {
await this.say("completion_result", result, undefined, false)
telemetryService.captureTaskCompleted(this.taskId)
this.emit("taskCompleted", this.taskId, this.getTokenUsage())
console.log("TASK COMPLETED | event emitted")
}
// Complete command message.
@ -3185,7 +3263,6 @@ export class Cline extends EventEmitter<ClineEvents> {
await this.say("completion_result", result, undefined, false)
telemetryService.captureTaskCompleted(this.taskId)
this.emit("taskCompleted", this.taskId, this.getTokenUsage())
console.log("TASK COMPLETED | event emitted")
}
if (this.parentTask) {

View file

@ -7,6 +7,7 @@ export const ACTION_NAMES = {
FIX_LOGIC: "Roo Code: Fix Logic",
IMPROVE: "Roo Code: Improve Code",
ADD_TO_CONTEXT: "Roo Code: Add to Context",
NEW_TASK: "Roo Code: New Task",
} as const
export const COMMAND_IDS = {
@ -14,6 +15,7 @@ export const COMMAND_IDS = {
FIX: "roo-cline.fixCode",
IMPROVE: "roo-cline.improveCode",
ADD_TO_CONTEXT: "roo-cline.addToContext",
NEW_TASK: "roo-cline.newTask",
} as const
export class CodeActionProvider implements vscode.CodeActionProvider {

View file

@ -139,6 +139,10 @@ jest.mock("vscode", () => {
}
return {
CodeActionKind: {
QuickFix: { value: "quickfix" },
RefactorRewrite: { value: "refactor.rewrite" },
},
window: {
createTextEditorDecorationType: jest.fn().mockReturnValue({
dispose: jest.fn(),

View file

@ -25,6 +25,7 @@ export const toolUseNames = [
"attempt_completion",
"switch_mode",
"new_task",
"fetch_instructions",
] as const
// Converts array of tool call names into a union type ("execute_command" | "read_file" | ...)
@ -59,6 +60,7 @@ export const toolParamNames = [
"message",
"cwd",
"follow_up",
"task",
] as const
export type ToolParamName = (typeof toolParamNames)[number]
@ -82,6 +84,11 @@ export interface ReadFileToolUse extends ToolUse {
params: Partial<Pick<Record<ToolParamName, string>, "path" | "start_line" | "end_line">>
}
export interface FetchInstructionsToolUse extends ToolUse {
name: "fetch_instructions"
params: Partial<Pick<Record<ToolParamName, string>, "task">>
}
export interface WriteToFileToolUse extends ToolUse {
name: "write_to_file"
params: Partial<Pick<Record<ToolParamName, string>, "path" | "content" | "line_count">>

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,404 @@
import { McpHub } from "../../../services/mcp/McpHub"
import { DiffStrategy } from "../../diff/DiffStrategy"
export async function createMCPServerInstructions(
mcpHub: McpHub | undefined,
diffStrategy: DiffStrategy | undefined,
): Promise<string> {
if (!diffStrategy || !mcpHub) throw new Error("Missing MCP Hub or Diff Strategy")
return `You have the ability to create an MCP server and add it to a configuration file that will then expose the tools and resources for you to use with \`use_mcp_tool\` and \`access_mcp_resource\`.
When creating MCP servers, it's important to understand that they operate in a non-interactive environment. The server cannot initiate OAuth flows, open browser windows, or prompt for user input during runtime. All credentials and authentication tokens must be provided upfront through environment variables in the MCP settings configuration. For example, Spotify's API uses OAuth to get a refresh token for the user, but the MCP server cannot initiate this flow. While you can walk the user through obtaining an application client ID and secret, you may have to create a separate one-time setup script (like get-refresh-token.js) that captures and logs the final piece of the puzzle: the user's refresh token (i.e. you might run the script using execute_command which would open a browser for authentication, and then log the refresh token so that you can see it in the command output for you to use in the MCP settings configuration).
Unless the user specifies otherwise, new local MCP servers should be created in: ${await mcpHub.getMcpServersPath()}
### MCP Server Types and Configuration
MCP servers can be configured in two ways in the MCP settings file:
1. Local (Stdio) Server Configuration:
\`\`\`json
{
"mcpServers": {
"local-weather": {
"command": "node",
"args": ["/path/to/weather-server/build/index.js"],
"env": {
"OPENWEATHER_API_KEY": "your-api-key"
}
}
}
}
\`\`\`
2. Remote (SSE) Server Configuration:
\`\`\`json
{
"mcpServers": {
"remote-weather": {
"url": "https://api.example.com/mcp",
"headers": {
"Authorization": "Bearer your-api-key"
}
}
}
}
\`\`\`
Common configuration options for both types:
- \`disabled\`: (optional) Set to true to temporarily disable the server
- \`timeout\`: (optional) Maximum time in seconds to wait for server responses (default: 60)
- \`alwaysAllow\`: (optional) Array of tool names that don't require user confirmation
### Example Local MCP Server
For example, if the user wanted to give you the ability to retrieve weather information, you could create an MCP server that uses the OpenWeather API to get weather information, add it to the MCP settings configuration file, and then notice that you now have access to new tools and resources in the system prompt that you might use to show the user your new capabilities.
The following example demonstrates how to build a local MCP server that provides weather data functionality using the Stdio transport. While this example shows how to implement resources, resource templates, and tools, in practice you should prefer using tools since they are more flexible and can handle dynamic parameters. The resource and resource template implementations are included here mainly for demonstration purposes of the different MCP capabilities, but a real weather server would likely just expose tools for fetching weather data. (The following steps are for macOS)
1. Use the \`create-typescript-server\` tool to bootstrap a new project in the default MCP servers directory:
\`\`\`bash
cd ${await mcpHub.getMcpServersPath()}
npx @modelcontextprotocol/create-server weather-server
cd weather-server
# Install dependencies
npm install axios
\`\`\`
This will create a new project with the following structure:
\`\`\`
weather-server/
package.json
{
...
"type": "module", // added by default, uses ES module syntax (import/export) rather than CommonJS (require/module.exports) (Important to know if you create additional scripts in this server repository like a get-refresh-token.js script)
"scripts": {
"build": "tsc && node -e \"require('fs').chmodSync('build/index.js', '755')\"",
...
}
...
}
tsconfig.json
src/
weather-server/
index.ts # Main server implementation
\`\`\`
2. Replace \`src/index.ts\` with the following:
\`\`\`typescript
#!/usr/bin/env node
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
CallToolRequestSchema,
ErrorCode,
ListResourcesRequestSchema,
ListResourceTemplatesRequestSchema,
ListToolsRequestSchema,
McpError,
ReadResourceRequestSchema,
} from '@modelcontextprotocol/sdk/types.js';
import axios from 'axios';
const API_KEY = process.env.OPENWEATHER_API_KEY; // provided by MCP config
if (!API_KEY) {
throw new Error('OPENWEATHER_API_KEY environment variable is required');
}
interface OpenWeatherResponse {
main: {
temp: number;
humidity: number;
};
weather: [{ description: string }];
wind: { speed: number };
dt_txt?: string;
}
const isValidForecastArgs = (
args: any
): args is { city: string; days?: number } =>
typeof args === 'object' &&
args !== null &&
typeof args.city === 'string' &&
(args.days === undefined || typeof args.days === 'number');
class WeatherServer {
private server: Server;
private axiosInstance;
constructor() {
this.server = new Server(
{
name: 'example-weather-server',
version: '0.1.0',
},
{
capabilities: {
resources: {},
tools: {},
},
}
);
this.axiosInstance = axios.create({
baseURL: 'http://api.openweathermap.org/data/2.5',
params: {
appid: API_KEY,
units: 'metric',
},
});
this.setupResourceHandlers();
this.setupToolHandlers();
// Error handling
this.server.onerror = (error) => console.error('[MCP Error]', error);
process.on('SIGINT', async () => {
await this.server.close();
process.exit(0);
});
}
// MCP Resources represent any kind of UTF-8 encoded data that an MCP server wants to make available to clients, such as database records, API responses, log files, and more. Servers define direct resources with a static URI or dynamic resources with a URI template that follows the format \`[protocol]://[host]/[path]\`.
private setupResourceHandlers() {
// For static resources, servers can expose a list of resources:
this.server.setRequestHandler(ListResourcesRequestSchema, async () => ({
resources: [
// This is a poor example since you could use the resource template to get the same information but this demonstrates how to define a static resource
{
uri: \`weather://San Francisco/current\`, // Unique identifier for San Francisco weather resource
name: \`Current weather in San Francisco\`, // Human-readable name
mimeType: 'application/json', // Optional MIME type
// Optional description
description:
'Real-time weather data for San Francisco including temperature, conditions, humidity, and wind speed',
},
],
}));
// For dynamic resources, servers can expose resource templates:
this.server.setRequestHandler(
ListResourceTemplatesRequestSchema,
async () => ({
resourceTemplates: [
{
uriTemplate: 'weather://{city}/current', // URI template (RFC 6570)
name: 'Current weather for a given city', // Human-readable name
mimeType: 'application/json', // Optional MIME type
description: 'Real-time weather data for a specified city', // Optional description
},
],
})
);
// ReadResourceRequestSchema is used for both static resources and dynamic resource templates
this.server.setRequestHandler(
ReadResourceRequestSchema,
async (request) => {
const match = request.params.uri.match(
/^weather:\/\/([^/]+)\/current$/
);
if (!match) {
throw new McpError(
ErrorCode.InvalidRequest,
\`Invalid URI format: \${request.params.uri}\`
);
}
const city = decodeURIComponent(match[1]);
try {
const response = await this.axiosInstance.get(
'weather', // current weather
{
params: { q: city },
}
);
return {
contents: [
{
uri: request.params.uri,
mimeType: 'application/json',
text: JSON.stringify(
{
temperature: response.data.main.temp,
conditions: response.data.weather[0].description,
humidity: response.data.main.humidity,
wind_speed: response.data.wind.speed,
timestamp: new Date().toISOString(),
},
null,
2
),
},
],
};
} catch (error) {
if (axios.isAxiosError(error)) {
throw new McpError(
ErrorCode.InternalError,
\`Weather API error: \${
error.response?.data.message ?? error.message
}\`
);
}
throw error;
}
}
);
}
/* MCP Tools enable servers to expose executable functionality to the system. Through these tools, you can interact with external systems, perform computations, and take actions in the real world.
* - Like resources, tools are identified by unique names and can include descriptions to guide their usage. However, unlike resources, tools represent dynamic operations that can modify state or interact with external systems.
* - While resources and tools are similar, you should prefer to create tools over resources when possible as they provide more flexibility.
*/
private setupToolHandlers() {
this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: [
{
name: 'get_forecast', // Unique identifier
description: 'Get weather forecast for a city', // Human-readable description
inputSchema: {
// JSON Schema for parameters
type: 'object',
properties: {
city: {
type: 'string',
description: 'City name',
},
days: {
type: 'number',
description: 'Number of days (1-5)',
minimum: 1,
maximum: 5,
},
},
required: ['city'], // Array of required property names
},
},
],
}));
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
if (request.params.name !== 'get_forecast') {
throw new McpError(
ErrorCode.MethodNotFound,
\`Unknown tool: \${request.params.name}\`
);
}
if (!isValidForecastArgs(request.params.arguments)) {
throw new McpError(
ErrorCode.InvalidParams,
'Invalid forecast arguments'
);
}
const city = request.params.arguments.city;
const days = Math.min(request.params.arguments.days || 3, 5);
try {
const response = await this.axiosInstance.get<{
list: OpenWeatherResponse[];
}>('forecast', {
params: {
q: city,
cnt: days * 8,
},
});
return {
content: [
{
type: 'text',
text: JSON.stringify(response.data.list, null, 2),
},
],
};
} catch (error) {
if (axios.isAxiosError(error)) {
return {
content: [
{
type: 'text',
text: \`Weather API error: \${
error.response?.data.message ?? error.message
}\`,
},
],
isError: true,
};
}
throw error;
}
});
}
async run() {
const transport = new StdioServerTransport();
await this.server.connect(transport);
console.error('Weather MCP server running on stdio');
}
}
const server = new WeatherServer();
server.run().catch(console.error);
\`\`\`
(Remember: This is just an exampleyou may use different dependencies, break the implementation up into multiple files, etc.)
3. Build and compile the executable JavaScript file
\`\`\`bash
npm run build
\`\`\`
4. Whenever you need an environment variable such as an API key to configure the MCP server, walk the user through the process of getting the key. For example, they may need to create an account and go to a developer dashboard to generate the key. Provide step-by-step instructions and URLs to make it easy for the user to retrieve the necessary information. Then use the ask_followup_question tool to ask the user for the key, in this case the OpenWeather API key.
5. Install the MCP Server by adding the MCP server configuration to the settings file located at '${await mcpHub.getMcpSettingsFilePath()}'. The settings file may have other MCP servers already configured, so you would read it first and then add your new server to the existing \`mcpServers\` object.
IMPORTANT: Regardless of what else you see in the MCP settings file, you must default any new MCP servers you create to disabled=false and alwaysAllow=[].
\`\`\`json
{
"mcpServers": {
...,
"weather": {
"command": "node",
"args": ["/path/to/weather-server/build/index.js"],
"env": {
"OPENWEATHER_API_KEY": "user-provided-api-key"
}
},
}
}
\`\`\`
(Note: the user may also ask you to install the MCP server to the Claude desktop app, in which case you would read then modify \`~/Library/Application\ Support/Claude/claude_desktop_config.json\` on macOS for example. It follows the same format of a top level \`mcpServers\` object.)
6. After you have edited the MCP settings configuration file, the system will automatically run all the servers and expose the available tools and resources in the 'Connected MCP Servers' section.
7. Now that you have access to these new tools and resources, you may suggest ways the user can command you to invoke them - for example, with this new weather tool now available, you can invite the user to ask "what's the weather in San Francisco?"
## Editing MCP Servers
The user may ask to add tools or resources that may make sense to add to an existing MCP server (listed under 'Connected MCP Servers' above: ${
mcpHub
.getServers()
.map((server) => server.name)
.join(", ") || "(None running currently)"
}, e.g. if it would use the same API. This would be possible if you can locate the MCP server repository on the user's system by looking at the server arguments for a filepath. You might then use list_files and read_file to explore the files in the repository, and use write_to_file${diffStrategy ? " or apply_diff" : ""} to make changes to the files.
However some MCP servers may be running from installed packages rather than a local repository, in which case it may make more sense to create a new MCP server.
# MCP Servers Are Not Always Necessary
The user may not always request the use or creation of MCP servers. Instead, they might provide tasks that can be completed with existing tools. While using the MCP SDK to extend your capabilities can be useful, it's important to understand that this is just one specialized type of task you can accomplish. You should only implement MCP servers when the user explicitly requests it (e.g., "add a tool that...").
Remember: The MCP documentation and example provided above are to help you understand and work with existing MCP servers or create new ones when requested by the user. You already have access to tools and capabilities that can be used to accomplish a wide range of tasks.`
}

View file

@ -0,0 +1,52 @@
import * as path from "path"
import * as vscode from "vscode"
import { promises as fs } from "fs"
import { GlobalFileNames } from "../../../shared/globalFileNames"
export async function createModeInstructions(context: vscode.ExtensionContext | undefined): Promise<string> {
if (!context) throw new Error("Missing VSCode Extension Context")
const settingsDir = path.join(context.globalStorageUri.fsPath, "settings")
const customModesPath = path.join(settingsDir, GlobalFileNames.customModes)
return `
Custom modes can be configured in two ways:
1. Globally via '${customModesPath}' (created automatically on startup)
2. Per-workspace via '.roomodes' in the workspace root directory
When modes with the same slug exist in both files, the workspace-specific .roomodes version takes precedence. This allows projects to override global modes or define project-specific modes.
If asked to create a project mode, create it in .roomodes in the workspace root. If asked to create a global mode, use the global custom modes file.
- The following fields are required and must not be empty:
* slug: A valid slug (lowercase letters, numbers, and hyphens). Must be unique, and shorter is better.
* name: The display name for the mode
* roleDefinition: A detailed description of the mode's role and capabilities
* groups: Array of allowed tool groups (can be empty). Each group can be specified either as a string (e.g., "edit" to allow editing any file) or with file restrictions (e.g., ["edit", { fileRegex: "\\.md$", description: "Markdown files only" }] to only allow editing markdown files)
- The customInstructions field is optional.
- For multi-line text, include newline characters in the string like "This is the first line.\\nThis is the next line.\\n\\nThis is a double line break."
Both files should follow this structure:
{
"customModes": [
{
"slug": "designer", // Required: unique slug with lowercase letters, numbers, and hyphens
"name": "Designer", // Required: mode display name
"roleDefinition": "You are Roo, a UI/UX expert specializing in design systems and frontend development. Your expertise includes:\\n- Creating and maintaining design systems\\n- Implementing responsive and accessible web interfaces\\n- Working with CSS, HTML, and modern frontend frameworks\\n- Ensuring consistent user experiences across platforms", // Required: non-empty
"groups": [ // Required: array of tool groups (can be empty)
"read", // Read files group (read_file, fetch_instructions, search_files, list_files, list_code_definition_names)
"edit", // Edit files group (apply_diff, write_to_file) - allows editing any file
// Or with file restrictions:
// ["edit", { fileRegex: "\\.md$", description: "Markdown files only" }], // Edit group that only allows editing markdown files
"browser", // Browser group (browser_action)
"command", // Command group (execute_command)
"mcp" // MCP group (use_mcp_tool, access_mcp_resource)
],
"customInstructions": "Additional instructions for the Designer mode" // Optional
}
]
}`
}

View file

@ -0,0 +1,25 @@
import { createMCPServerInstructions } from "./create-mcp-server"
import { createModeInstructions } from "./create-mode"
import { McpHub } from "../../../services/mcp/McpHub"
import { DiffStrategy } from "../../diff/DiffStrategy"
import * as vscode from "vscode"
interface InstructionsDetail {
mcpHub?: McpHub
diffStrategy?: DiffStrategy
context?: vscode.ExtensionContext
}
export async function fetchInstructions(text: string, detail: InstructionsDetail): Promise<string> {
switch (text) {
case "create_mcp_server": {
return await createMCPServerInstructions(detail.mcpHub, detail.diffStrategy)
}
case "create_mode": {
return await createModeInstructions(detail.context)
}
default: {
return ""
}
}
}

View file

@ -106,15 +106,7 @@ describe("addCustomInstructions", () => {
})
it("should combine all instruction types when provided", async () => {
// Mock implementation to return different values based on the file path
mockedFs.readFile.mockImplementation(((filePath: any) => {
// For .clinerules-test-mode, return mode-specific rules
if (filePath.toString().includes(".clinerules-test-mode")) {
return Promise.resolve("mode specific rules")
}
// For any other read operation, return empty
return Promise.reject({ code: "ENOENT" })
}) as any)
mockedFs.readFile.mockResolvedValue("mode specific rules")
const result = await addCustomInstructions(
"mode instructions",

View file

@ -16,30 +16,12 @@ async function safeReadFile(filePath: string): Promise<string> {
}
}
async function findRuleInDirectory(dir: string, ruleFile: string): Promise<string> {
const filePath = path.join(dir, ruleFile)
const content = await safeReadFile(filePath)
if (content) {
return content
}
// Check if we've reached the root directory
const parentDir = path.dirname(dir)
if (parentDir === dir) {
return ""
}
// Recursively check parent directory
return findRuleInDirectory(parentDir, ruleFile)
}
export async function loadRuleFiles(cwd: string): Promise<string> {
const ruleFiles = [".clinerules", ".cursorrules", ".windsurfrules"]
let combinedRules = ""
for (const file of ruleFiles) {
const content = await findRuleInDirectory(cwd, file)
const content = await safeReadFile(path.join(cwd, file))
if (content) {
combinedRules += `\n# Rules from ${file}:\n${content}\n`
}
@ -48,17 +30,6 @@ export async function loadRuleFiles(cwd: string): Promise<string> {
return combinedRules
}
async function findCustomInstructionsFile(dir: string, filePattern: string): Promise<string> {
// First try to find as a direct file
const content = await findRuleInDirectory(dir, filePattern)
if (content) {
return content
}
// If not found as a file, check if it's raw content
return filePattern.trim()
}
export async function addCustomInstructions(
modeCustomInstructions: string,
globalCustomInstructions: string,
@ -83,16 +54,14 @@ export async function addCustomInstructions(
)
}
// Add global instructions first - try to find as file or use raw content
const globalContent = await findCustomInstructionsFile(cwd, globalCustomInstructions)
if (globalContent) {
sections.push(`Global Instructions:\n${globalContent}`)
// Add global instructions first
if (typeof globalCustomInstructions === "string" && globalCustomInstructions.trim()) {
sections.push(`Global Instructions:\n${globalCustomInstructions.trim()}`)
}
// Add mode-specific instructions - try to find as file or use raw content
const modeContent = await findCustomInstructionsFile(cwd, modeCustomInstructions)
if (modeContent) {
sections.push(`Mode-specific Instructions:\n${modeContent}`)
// Add mode-specific instructions after
if (typeof modeCustomInstructions === "string" && modeCustomInstructions.trim()) {
sections.push(`Mode-specific Instructions:\n${modeCustomInstructions.trim()}`)
}
// Add rules - include both mode-specific and generic rules if they exist

View file

@ -20,7 +20,7 @@ export async function getMcpServersSection(
?.map((tool) => {
const schemaStr = tool.inputSchema
? ` Input Schema:
${JSON.stringify(tool.inputSchema, null, 2).split("\n").join("\n ")}`
${JSON.stringify(tool.inputSchema, null, 2).split("\n").join("\n ")}`
: ""
return `- ${tool.name}: ${tool.description}\n${schemaStr}`
@ -67,402 +67,11 @@ ${connectedServers}`
return (
baseSection +
`
## Creating an MCP Server
The user may ask you something along the lines of "add a tool" that does some function, in other words to create an MCP server that provides tools and resources that may connect to external APIs for example. You have the ability to create an MCP server and add it to a configuration file that will then expose the tools and resources for you to use with \`use_mcp_tool\` and \`access_mcp_resource\`.
When creating MCP servers, it's important to understand that they operate in a non-interactive environment. The server cannot initiate OAuth flows, open browser windows, or prompt for user input during runtime. All credentials and authentication tokens must be provided upfront through environment variables in the MCP settings configuration. For example, Spotify's API uses OAuth to get a refresh token for the user, but the MCP server cannot initiate this flow. While you can walk the user through obtaining an application client ID and secret, you may have to create a separate one-time setup script (like get-refresh-token.js) that captures and logs the final piece of the puzzle: the user's refresh token (i.e. you might run the script using execute_command which would open a browser for authentication, and then log the refresh token so that you can see it in the command output for you to use in the MCP settings configuration).
Unless the user specifies otherwise, new local MCP servers should be created in: ${await mcpHub.getMcpServersPath()}
### MCP Server Types and Configuration
MCP servers can be configured in two ways in the MCP settings file:
1. Local (Stdio) Server Configuration:
\`\`\`json
{
"mcpServers": {
"local-weather": {
"command": "node",
"args": ["/path/to/weather-server/build/index.js"],
"env": {
"OPENWEATHER_API_KEY": "your-api-key"
}
}
}
}
\`\`\`
2. Remote (SSE) Server Configuration:
\`\`\`json
{
"mcpServers": {
"remote-weather": {
"url": "https://api.example.com/mcp",
"headers": {
"Authorization": "Bearer your-api-key"
}
}
}
}
\`\`\`
Common configuration options for both types:
- \`disabled\`: (optional) Set to true to temporarily disable the server
- \`timeout\`: (optional) Maximum time in seconds to wait for server responses (default: 60)
- \`alwaysAllow\`: (optional) Array of tool names that don't require user confirmation
### Example Local MCP Server
For example, if the user wanted to give you the ability to retrieve weather information, you could create an MCP server that uses the OpenWeather API to get weather information, add it to the MCP settings configuration file, and then notice that you now have access to new tools and resources in the system prompt that you might use to show the user your new capabilities.
The following example demonstrates how to build a local MCP server that provides weather data functionality using the Stdio transport. While this example shows how to implement resources, resource templates, and tools, in practice you should prefer using tools since they are more flexible and can handle dynamic parameters. The resource and resource template implementations are included here mainly for demonstration purposes of the different MCP capabilities, but a real weather server would likely just expose tools for fetching weather data. (The following steps are for macOS)
1. Use the \`create-typescript-server\` tool to bootstrap a new project in the default MCP servers directory:
\`\`\`bash
cd ${await mcpHub.getMcpServersPath()}
npx @modelcontextprotocol/create-server weather-server
cd weather-server
# Install dependencies
npm install axios
\`\`\`
This will create a new project with the following structure:
\`\`\`
weather-server/
package.json
{
...
"type": "module", // added by default, uses ES module syntax (import/export) rather than CommonJS (require/module.exports) (Important to know if you create additional scripts in this server repository like a get-refresh-token.js script)
"scripts": {
"build": "tsc && node -e \"require('fs').chmodSync('build/index.js', '755')\"",
...
}
...
}
tsconfig.json
src/
weather-server/
index.ts # Main server implementation
\`\`\`
2. Replace \`src/index.ts\` with the following:
\`\`\`typescript
#!/usr/bin/env node
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
CallToolRequestSchema,
ErrorCode,
ListResourcesRequestSchema,
ListResourceTemplatesRequestSchema,
ListToolsRequestSchema,
McpError,
ReadResourceRequestSchema,
} from '@modelcontextprotocol/sdk/types.js';
import axios from 'axios';
const API_KEY = process.env.OPENWEATHER_API_KEY; // provided by MCP config
if (!API_KEY) {
throw new Error('OPENWEATHER_API_KEY environment variable is required');
}
interface OpenWeatherResponse {
main: {
temp: number;
humidity: number;
};
weather: [{ description: string }];
wind: { speed: number };
dt_txt?: string;
}
const isValidForecastArgs = (
args: any
): args is { city: string; days?: number } =>
typeof args === 'object' &&
args !== null &&
typeof args.city === 'string' &&
(args.days === undefined || typeof args.days === 'number');
class WeatherServer {
private server: Server;
private axiosInstance;
constructor() {
this.server = new Server(
{
name: 'example-weather-server',
version: '0.1.0',
},
{
capabilities: {
resources: {},
tools: {},
},
}
);
this.axiosInstance = axios.create({
baseURL: 'http://api.openweathermap.org/data/2.5',
params: {
appid: API_KEY,
units: 'metric',
},
});
this.setupResourceHandlers();
this.setupToolHandlers();
// Error handling
this.server.onerror = (error) => console.error('[MCP Error]', error);
process.on('SIGINT', async () => {
await this.server.close();
process.exit(0);
});
}
// MCP Resources represent any kind of UTF-8 encoded data that an MCP server wants to make available to clients, such as database records, API responses, log files, and more. Servers define direct resources with a static URI or dynamic resources with a URI template that follows the format \`[protocol]://[host]/[path]\`.
private setupResourceHandlers() {
// For static resources, servers can expose a list of resources:
this.server.setRequestHandler(ListResourcesRequestSchema, async () => ({
resources: [
// This is a poor example since you could use the resource template to get the same information but this demonstrates how to define a static resource
{
uri: \`weather://San Francisco/current\`, // Unique identifier for San Francisco weather resource
name: \`Current weather in San Francisco\`, // Human-readable name
mimeType: 'application/json', // Optional MIME type
// Optional description
description:
'Real-time weather data for San Francisco including temperature, conditions, humidity, and wind speed',
},
],
}));
// For dynamic resources, servers can expose resource templates:
this.server.setRequestHandler(
ListResourceTemplatesRequestSchema,
async () => ({
resourceTemplates: [
{
uriTemplate: 'weather://{city}/current', // URI template (RFC 6570)
name: 'Current weather for a given city', // Human-readable name
mimeType: 'application/json', // Optional MIME type
description: 'Real-time weather data for a specified city', // Optional description
},
],
})
);
// ReadResourceRequestSchema is used for both static resources and dynamic resource templates
this.server.setRequestHandler(
ReadResourceRequestSchema,
async (request) => {
const match = request.params.uri.match(
/^weather:\/\/([^/]+)\/current$/
);
if (!match) {
throw new McpError(
ErrorCode.InvalidRequest,
\`Invalid URI format: \${request.params.uri}\`
);
}
const city = decodeURIComponent(match[1]);
try {
const response = await this.axiosInstance.get(
'weather', // current weather
{
params: { q: city },
}
);
return {
contents: [
{
uri: request.params.uri,
mimeType: 'application/json',
text: JSON.stringify(
{
temperature: response.data.main.temp,
conditions: response.data.weather[0].description,
humidity: response.data.main.humidity,
wind_speed: response.data.wind.speed,
timestamp: new Date().toISOString(),
},
null,
2
),
},
],
};
} catch (error) {
if (axios.isAxiosError(error)) {
throw new McpError(
ErrorCode.InternalError,
\`Weather API error: \${
error.response?.data.message ?? error.message
}\`
);
}
throw error;
}
}
);
}
/* MCP Tools enable servers to expose executable functionality to the system. Through these tools, you can interact with external systems, perform computations, and take actions in the real world.
* - Like resources, tools are identified by unique names and can include descriptions to guide their usage. However, unlike resources, tools represent dynamic operations that can modify state or interact with external systems.
* - While resources and tools are similar, you should prefer to create tools over resources when possible as they provide more flexibility.
*/
private setupToolHandlers() {
this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: [
{
name: 'get_forecast', // Unique identifier
description: 'Get weather forecast for a city', // Human-readable description
inputSchema: {
// JSON Schema for parameters
type: 'object',
properties: {
city: {
type: 'string',
description: 'City name',
},
days: {
type: 'number',
description: 'Number of days (1-5)',
minimum: 1,
maximum: 5,
},
},
required: ['city'], // Array of required property names
},
},
],
}));
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
if (request.params.name !== 'get_forecast') {
throw new McpError(
ErrorCode.MethodNotFound,
\`Unknown tool: \${request.params.name}\`
);
}
if (!isValidForecastArgs(request.params.arguments)) {
throw new McpError(
ErrorCode.InvalidParams,
'Invalid forecast arguments'
);
}
const city = request.params.arguments.city;
const days = Math.min(request.params.arguments.days || 3, 5);
try {
const response = await this.axiosInstance.get<{
list: OpenWeatherResponse[];
}>('forecast', {
params: {
q: city,
cnt: days * 8,
},
});
return {
content: [
{
type: 'text',
text: JSON.stringify(response.data.list, null, 2),
},
],
};
} catch (error) {
if (axios.isAxiosError(error)) {
return {
content: [
{
type: 'text',
text: \`Weather API error: \${
error.response?.data.message ?? error.message
}\`,
},
],
isError: true,
};
}
throw error;
}
});
}
async run() {
const transport = new StdioServerTransport();
await this.server.connect(transport);
console.error('Weather MCP server running on stdio');
}
}
const server = new WeatherServer();
server.run().catch(console.error);
\`\`\`
(Remember: This is just an exampleyou may use different dependencies, break the implementation up into multiple files, etc.)
3. Build and compile the executable JavaScript file
\`\`\`bash
npm run build
\`\`\`
4. Whenever you need an environment variable such as an API key to configure the MCP server, walk the user through the process of getting the key. For example, they may need to create an account and go to a developer dashboard to generate the key. Provide step-by-step instructions and URLs to make it easy for the user to retrieve the necessary information. Then use the ask_followup_question tool to ask the user for the key, in this case the OpenWeather API key.
5. Install the MCP Server by adding the MCP server configuration to the settings file located at '${await mcpHub.getMcpSettingsFilePath()}'. The settings file may have other MCP servers already configured, so you would read it first and then add your new server to the existing \`mcpServers\` object.
IMPORTANT: Regardless of what else you see in the MCP settings file, you must default any new MCP servers you create to disabled=false and alwaysAllow=[].
\`\`\`json
{
"mcpServers": {
...,
"weather": {
"command": "node",
"args": ["/path/to/weather-server/build/index.js"],
"env": {
"OPENWEATHER_API_KEY": "user-provided-api-key"
}
},
}
}
\`\`\`
(Note: the user may also ask you to install the MCP server to the Claude desktop app, in which case you would read then modify \`~/Library/Application\ Support/Claude/claude_desktop_config.json\` on macOS for example. It follows the same format of a top level \`mcpServers\` object.)
6. After you have edited the MCP settings configuration file, the system will automatically run all the servers and expose the available tools and resources in the 'Connected MCP Servers' section.
7. Now that you have access to these new tools and resources, you may suggest ways the user can command you to invoke them - for example, with this new weather tool now available, you can invite the user to ask "what's the weather in San Francisco?"
## Editing MCP Servers
The user may ask to add tools or resources that may make sense to add to an existing MCP server (listed under 'Connected MCP Servers' above: ${
mcpHub
.getServers()
.map((server) => server.name)
.join(", ") || "(None running currently)"
}, e.g. if it would use the same API. This would be possible if you can locate the MCP server repository on the user's system by looking at the server arguments for a filepath. You might then use list_files and read_file to explore the files in the repository, and use write_to_file${diffStrategy ? " or apply_diff" : ""} to make changes to the files.
However some MCP servers may be running from installed packages rather than a local repository, in which case it may make more sense to create a new MCP server.
# MCP Servers Are Not Always Necessary
The user may not always request the use or creation of MCP servers. Instead, they might provide tasks that can be completed with existing tools. While using the MCP SDK to extend your capabilities can be useful, it's important to understand that this is just one specialized type of task you can accomplish. You should only implement MCP servers when the user explicitly requests it (e.g., "add a tool that...").
Remember: The MCP documentation and example provided above are to help you understand and work with existing MCP servers or create new ones when requested by the user. You already have access to tools and capabilities that can be used to accomplish a wide range of tasks.`
The user may ask you something along the lines of "add a tool" that does some function, in other words to create an MCP server that provides tools and resources that may connect to external APIs for example. If they do, you should obtain detailed instructions on this topic using the fetch_instructions tool, like this:
<fetch_instructions>
<task>create_mcp_server</task>
</fetch_instructions>`
)
}

View file

@ -7,14 +7,10 @@ import { GlobalFileNames } from "../../../shared/globalFileNames"
export async function getModesSection(context: vscode.ExtensionContext): Promise<string> {
const settingsDir = path.join(context.globalStorageUri.fsPath, "settings")
await fs.mkdir(settingsDir, { recursive: true })
const customModesPath = path.join(settingsDir, GlobalFileNames.customModes)
// Get all modes with their overrides from extension state
const allModes = await getAllModesWithPrompts(context)
// Get enableCustomModeCreation setting from extension state
const shouldEnableCustomModeCreation = (await context.globalState.get<boolean>("enableCustomModeCreation")) ?? true
let modesContent = `====
MODES
@ -22,49 +18,12 @@ MODES
- These are the currently available modes:
${allModes.map((mode: ModeConfig) => ` * "${mode.name}" mode (${mode.slug}) - ${mode.roleDefinition.split(".")[0]}`).join("\n")}`
// Only include custom modes documentation if the feature is enabled
if (shouldEnableCustomModeCreation) {
modesContent += `
- Custom modes can be configured in two ways:
1. Globally via '${customModesPath}' (created automatically on startup)
2. Per-workspace via '.roomodes' in the workspace root directory
When modes with the same slug exist in both files, the workspace-specific .roomodes version takes precedence. This allows projects to override global modes or define project-specific modes.
If asked to create a project mode, create it in .roomodes in the workspace root. If asked to create a global mode, use the global custom modes file.
- The following fields are required and must not be empty:
* slug: A valid slug (lowercase letters, numbers, and hyphens). Must be unique, and shorter is better.
* name: The display name for the mode
* roleDefinition: A detailed description of the mode's role and capabilities
* groups: Array of allowed tool groups (can be empty). Each group can be specified either as a string (e.g., "edit" to allow editing any file) or with file restrictions (e.g., ["edit", { fileRegex: "\\.md$", description: "Markdown files only" }] to only allow editing markdown files)
- The customInstructions field is optional.
- For multi-line text, include newline characters in the string like "This is the first line.\\nThis is the next line.\\n\\nThis is a double line break."
Both files should follow this structure:
{
"customModes": [
{
"slug": "designer", // Required: unique slug with lowercase letters, numbers, and hyphens
"name": "Designer", // Required: mode display name
"roleDefinition": "You are Roo, a UI/UX expert specializing in design systems and frontend development. Your expertise includes:\\n- Creating and maintaining design systems\\n- Implementing responsive and accessible web interfaces\\n- Working with CSS, HTML, and modern frontend frameworks\\n- Ensuring consistent user experiences across platforms", // Required: non-empty
"groups": [ // Required: array of tool groups (can be empty)
"read", // Read files group (read_file, search_files, list_files, list_code_definition_names)
"edit", // Edit files group (apply_diff, write_to_file) - allows editing any file
// Or with file restrictions:
// ["edit", { fileRegex: "\\.md$", description: "Markdown files only" }], // Edit group that only allows editing markdown files
"browser", // Browser group (browser_action)
"command", // Command group (execute_command)
"mcp" // MCP group (use_mcp_tool, access_mcp_resource)
],
"customInstructions": "Additional instructions for the Designer mode" // Optional
}
]
}`
}
modesContent += `
If the user asks you to create or edit a new mode for this project, you should read the instructions by using the fetch_instructions tool, like this:
<fetch_instructions>
<task>create_mode</task>
</fetch_instructions>
`
return modesContent
}

View file

@ -0,0 +1,14 @@
export function getFetchInstructionsDescription(): string {
return `## fetch_instructions
Description: Request to fetch instructions to perform a task
Parameters:
- task: (required) The task to get instructions for. This can take the following values:
create_mcp_server
create_mode
Example: Requesting instructions to create an MCP Server
<fetch_instructions>
<task>create_mcp_server</task>
</fetch_instructions>`
}

View file

@ -1,5 +1,6 @@
import { getExecuteCommandDescription } from "./execute-command"
import { getReadFileDescription } from "./read-file"
import { getFetchInstructionsDescription } from "./fetch-instructions"
import { getWriteToFileDescription } from "./write-to-file"
import { getSearchFilesDescription } from "./search-files"
import { getListFilesDescription } from "./list-files"
@ -23,6 +24,7 @@ import { ToolArgs } from "./types"
const toolDescriptionMap: Record<string, (args: ToolArgs) => string | undefined> = {
execute_command: (args) => getExecuteCommandDescription(args),
read_file: (args) => getReadFileDescription(args),
fetch_instructions: () => getFetchInstructionsDescription(),
write_to_file: (args) => getWriteToFileDescription(args),
search_files: (args) => getSearchFilesDescription(args),
list_files: (args) => getListFilesDescription(args),
@ -97,6 +99,7 @@ export function getToolDescriptionsForMode(
export {
getExecuteCommandDescription,
getReadFileDescription,
getFetchInstructionsDescription,
getWriteToFileDescription,
getSearchFilesDescription,
getListFilesDescription,

View file

@ -36,7 +36,7 @@ import {
import { HistoryItem } from "../../shared/HistoryItem"
import { ApiConfigMeta, ExtensionMessage } from "../../shared/ExtensionMessage"
import { checkoutDiffPayloadSchema, checkoutRestorePayloadSchema, WebviewMessage } from "../../shared/WebviewMessage"
import { Mode, PromptComponent, defaultModeSlug, ModeConfig } from "../../shared/modes"
import { Mode, PromptComponent, defaultModeSlug, ModeConfig, getModeBySlug, getGroupName } from "../../shared/modes"
import { checkExistKey } from "../../shared/checkExistApiConfig"
import { EXPERIMENT_IDS, experiments as Experiments, experimentDefault, ExperimentId } from "../../shared/experiments"
import { formatLanguage } from "../../shared/language"
@ -971,10 +971,18 @@ export class ClineProvider extends EventEmitter<ClineProviderEvents> implements
await this.updateGlobalState("alwaysAllowReadOnly", message.bool ?? undefined)
await this.postStateToWebview()
break
case "alwaysAllowReadOnlyOutsideWorkspace":
await this.updateGlobalState("alwaysAllowReadOnlyOutsideWorkspace", message.bool ?? undefined)
await this.postStateToWebview()
break
case "alwaysAllowWrite":
await this.updateGlobalState("alwaysAllowWrite", message.bool ?? undefined)
await this.postStateToWebview()
break
case "alwaysAllowWriteOutsideWorkspace":
await this.updateGlobalState("alwaysAllowWriteOutsideWorkspace", message.bool ?? undefined)
await this.postStateToWebview()
break
case "alwaysAllowExecute":
await this.updateGlobalState("alwaysAllowExecute", message.bool ?? undefined)
await this.postStateToWebview()
@ -1706,10 +1714,6 @@ export class ClineProvider extends EventEmitter<ClineProviderEvents> implements
await this.updateGlobalState("enhancementApiConfigId", message.text)
await this.postStateToWebview()
break
case "enableCustomModeCreation":
await this.updateGlobalState("enableCustomModeCreation", message.bool ?? true)
await this.postStateToWebview()
break
case "autoApprovalEnabled":
await this.updateGlobalState("autoApprovalEnabled", message.bool ?? false)
await this.postStateToWebview()
@ -2095,9 +2099,25 @@ export class ClineProvider extends EventEmitter<ClineProviderEvents> implements
const rooIgnoreInstructions = this.getCurrentCline()?.rooIgnoreController?.getInstructions()
// Determine if browser tools can be used based on model support and user settings
const modelSupportsComputerUse = this.getCurrentCline()?.api.getModel().info.supportsComputerUse ?? false
const canUseBrowserTool = modelSupportsComputerUse && (browserToolEnabled ?? true)
// Determine if browser tools can be used based on model support, mode, and user settings
let modelSupportsComputerUse = false
// Create a temporary API handler to check if the model supports computer use
// This avoids relying on an active Cline instance which might not exist during preview
try {
const tempApiHandler = buildApiHandler(apiConfiguration)
modelSupportsComputerUse = tempApiHandler.getModel().info.supportsComputerUse ?? false
} catch (error) {
console.error("Error checking if model supports computer use:", error)
}
// Check if the current mode includes the browser tool group
const modeConfig = getModeBySlug(mode, customModes)
const modeSupportsBrowser = modeConfig?.groups.some((group) => getGroupName(group) === "browser") ?? false
// Only enable browser tools if the model supports it, the mode includes browser tools,
// and browser tools are enabled in settings
const canUseBrowserTool = modelSupportsComputerUse && modeSupportsBrowser && (browserToolEnabled ?? true)
const systemPrompt = await SYSTEM_PROMPT(
this.context,
@ -2264,15 +2284,15 @@ export class ClineProvider extends EventEmitter<ClineProviderEvents> implements
}
async ensureSettingsDirectoryExists(): Promise<string> {
const settingsDir = path.join(this.contextProxy.globalStorageUri.fsPath, "settings")
await fs.mkdir(settingsDir, { recursive: true })
return settingsDir
const { getSettingsDirectoryPath } = await import("../../shared/storagePathManager")
const globalStoragePath = this.contextProxy.globalStorageUri.fsPath
return getSettingsDirectoryPath(globalStoragePath)
}
private async ensureCacheDirectoryExists() {
const cacheDir = path.join(this.contextProxy.globalStorageUri.fsPath, "cache")
await fs.mkdir(cacheDir, { recursive: true })
return cacheDir
const { getCacheDirectoryPath } = await import("../../shared/storagePathManager")
const globalStoragePath = this.contextProxy.globalStorageUri.fsPath
return getCacheDirectoryPath(globalStoragePath)
}
private async readModelsFromCache(filename: string): Promise<Record<string, ModelInfo> | undefined> {
@ -2402,7 +2422,9 @@ export class ClineProvider extends EventEmitter<ClineProviderEvents> implements
const history = ((await this.getGlobalState("taskHistory")) as HistoryItem[] | undefined) || []
const historyItem = history.find((item) => item.id === id)
if (historyItem) {
const taskDirPath = path.join(this.contextProxy.globalStorageUri.fsPath, "tasks", id)
const { getTaskDirectoryPath } = await import("../../shared/storagePathManager")
const globalStoragePath = this.contextProxy.globalStorageUri.fsPath
const taskDirPath = await getTaskDirectoryPath(globalStoragePath, id)
const apiConversationHistoryFilePath = path.join(taskDirPath, GlobalFileNames.apiConversationHistory)
const uiMessagesFilePath = path.join(taskDirPath, GlobalFileNames.uiMessages)
const fileExists = await fileExistsAtPath(apiConversationHistoryFilePath)
@ -2507,7 +2529,9 @@ export class ClineProvider extends EventEmitter<ClineProviderEvents> implements
lastShownAnnouncementId,
customInstructions,
alwaysAllowReadOnly,
alwaysAllowReadOnlyOutsideWorkspace,
alwaysAllowWrite,
alwaysAllowWriteOutsideWorkspace,
alwaysAllowExecute,
alwaysAllowBrowser,
alwaysAllowMcp,
@ -2561,7 +2585,9 @@ export class ClineProvider extends EventEmitter<ClineProviderEvents> implements
apiConfiguration,
customInstructions,
alwaysAllowReadOnly: alwaysAllowReadOnly ?? false,
alwaysAllowReadOnlyOutsideWorkspace: alwaysAllowReadOnlyOutsideWorkspace ?? false,
alwaysAllowWrite: alwaysAllowWrite ?? false,
alwaysAllowWriteOutsideWorkspace: alwaysAllowWriteOutsideWorkspace ?? false,
alwaysAllowExecute: alwaysAllowExecute ?? false,
alwaysAllowBrowser: alwaysAllowBrowser ?? false,
alwaysAllowMcp: alwaysAllowMcp ?? false,
@ -2727,7 +2753,9 @@ export class ClineProvider extends EventEmitter<ClineProviderEvents> implements
lastShownAnnouncementId: stateValues.lastShownAnnouncementId,
customInstructions: stateValues.customInstructions,
alwaysAllowReadOnly: stateValues.alwaysAllowReadOnly ?? false,
alwaysAllowReadOnlyOutsideWorkspace: stateValues.alwaysAllowReadOnlyOutsideWorkspace ?? false,
alwaysAllowWrite: stateValues.alwaysAllowWrite ?? false,
alwaysAllowWriteOutsideWorkspace: stateValues.alwaysAllowWriteOutsideWorkspace ?? false,
alwaysAllowExecute: stateValues.alwaysAllowExecute ?? false,
alwaysAllowBrowser: stateValues.alwaysAllowBrowser ?? false,
alwaysAllowMcp: stateValues.alwaysAllowMcp ?? false,

View file

@ -167,6 +167,10 @@ jest.mock("vscode", () => ({
joinPath: jest.fn(),
file: jest.fn(),
},
CodeActionKind: {
QuickFix: { value: "quickfix" },
RefactorRewrite: { value: "refactor.rewrite" },
},
window: {
showInformationMessage: jest.fn(),
showErrorMessage: jest.fn(),
@ -434,7 +438,9 @@ describe("ClineProvider", () => {
},
customInstructions: undefined,
alwaysAllowReadOnly: false,
alwaysAllowReadOnlyOutsideWorkspace: false,
alwaysAllowWrite: false,
alwaysAllowWriteOutsideWorkspace: false,
alwaysAllowExecute: false,
alwaysAllowBrowser: false,
alwaysAllowMcp: false,
@ -1344,29 +1350,27 @@ describe("ClineProvider", () => {
})
// Tests for browser tool support
test("correctly extracts modelSupportsComputerUse from Cline instance", async () => {
// Setup Cline instance with mocked api.getModel()
const { Cline } = require("../../Cline")
const mockCline = new Cline()
mockCline.api = {
test("correctly determines model support for computer use without Cline instance", async () => {
// Mock buildApiHandler to return an API handler with supportsComputerUse: true
const { buildApiHandler } = require("../../../api")
;(buildApiHandler as jest.Mock).mockImplementation(() => ({
getModel: jest.fn().mockReturnValue({
id: "claude-3-sonnet",
info: { supportsComputerUse: true },
}),
}
await provider.addClineToStack(mockCline)
}))
// Mock SYSTEM_PROMPT to verify supportsComputerUse is passed correctly
const systemPromptModule = require("../../prompts/system")
const systemPromptSpy = jest.spyOn(systemPromptModule, "SYSTEM_PROMPT")
// Mock getState to return browserToolEnabled: true
// Mock getState to return browserToolEnabled: true and a mode that supports browser
jest.spyOn(provider, "getState").mockResolvedValue({
apiConfiguration: {
apiProvider: "openrouter",
},
browserToolEnabled: true,
mode: "code",
mode: "code", // code mode includes browser tool group
experiments: experimentDefault,
} as any)
@ -1385,16 +1389,14 @@ describe("ClineProvider", () => {
})
test("correctly handles when model doesn't support computer use", async () => {
// Setup Cline instance with mocked api.getModel() that doesn't support computer use
const { Cline } = require("../../Cline")
const mockCline = new Cline()
mockCline.api = {
// Mock buildApiHandler to return an API handler with supportsComputerUse: false
const { buildApiHandler } = require("../../../api")
;(buildApiHandler as jest.Mock).mockImplementation(() => ({
getModel: jest.fn().mockReturnValue({
id: "non-computer-use-model",
info: { supportsComputerUse: false },
}),
}
await provider.addClineToStack(mockCline)
}))
// Mock SYSTEM_PROMPT to verify supportsComputerUse is passed correctly
const systemPromptModule = require("../../prompts/system")
@ -1426,16 +1428,14 @@ describe("ClineProvider", () => {
})
test("correctly handles when browserToolEnabled is false", async () => {
// Setup Cline instance with mocked api.getModel() that supports computer use
const { Cline } = require("../../Cline")
const mockCline = new Cline()
mockCline.api = {
// Mock buildApiHandler to return an API handler with supportsComputerUse: true
const { buildApiHandler } = require("../../../api")
;(buildApiHandler as jest.Mock).mockImplementation(() => ({
getModel: jest.fn().mockReturnValue({
id: "claude-3-sonnet",
info: { supportsComputerUse: true },
}),
}
await provider.addClineToStack(mockCline)
}))
// Mock SYSTEM_PROMPT to verify supportsComputerUse is passed correctly
const systemPromptModule = require("../../prompts/system")
@ -1466,38 +1466,92 @@ describe("ClineProvider", () => {
expect(callArgs[2]).toBe(false)
})
test("correctly calculates canUseBrowserTool as combination of model support and setting", async () => {
// Setup Cline instance with mocked api.getModel()
const { Cline } = require("../../Cline")
const mockCline = new Cline()
mockCline.api = {
test("correctly handles when mode doesn't include browser tool group", async () => {
// Mock buildApiHandler to return an API handler with supportsComputerUse: true
const { buildApiHandler } = require("../../../api")
;(buildApiHandler as jest.Mock).mockImplementation(() => ({
getModel: jest.fn().mockReturnValue({
id: "claude-3-sonnet",
info: { supportsComputerUse: true },
}),
}
await provider.addClineToStack(mockCline)
}))
// Mock SYSTEM_PROMPT to verify supportsComputerUse is passed correctly
const systemPromptModule = require("../../prompts/system")
const systemPromptSpy = jest.spyOn(systemPromptModule, "SYSTEM_PROMPT")
// Mock getState to return a mode that doesn't include browser tool group
jest.spyOn(provider, "getState").mockResolvedValue({
apiConfiguration: {
apiProvider: "openrouter",
},
browserToolEnabled: true,
mode: "custom-mode-without-browser", // Custom mode without browser tool group
experiments: experimentDefault,
} as any)
// Mock getModeBySlug to return a mode without browser tool group
const modesModule = require("../../../shared/modes")
jest.spyOn(modesModule, "getModeBySlug").mockReturnValue({
slug: "custom-mode-without-browser",
name: "Custom Mode",
roleDefinition: "Custom role",
groups: ["read", "edit"], // No browser group
})
// Trigger getSystemPrompt
const handler = getMessageHandler()
await handler({ type: "getSystemPrompt", mode: "custom-mode-without-browser" })
// Verify SYSTEM_PROMPT was called
expect(systemPromptSpy).toHaveBeenCalled()
// Get the actual arguments passed to SYSTEM_PROMPT
const callArgs = systemPromptSpy.mock.calls[0]
// Verify the supportsComputerUse parameter (3rd parameter, index 2)
// Even though model supports it and browserToolEnabled is true, the mode doesn't include browser tool group
expect(callArgs[2]).toBe(false)
})
test("correctly calculates canUseBrowserTool based on all three conditions", async () => {
// Mock buildApiHandler
const { buildApiHandler } = require("../../../api")
// Mock SYSTEM_PROMPT
const systemPromptModule = require("../../prompts/system")
const systemPromptSpy = jest.spyOn(systemPromptModule, "SYSTEM_PROMPT")
// Test all combinations of model support and browserToolEnabled
// Mock getModeBySlug
const modesModule = require("../../../shared/modes")
// Test all combinations of model support, mode support, and browserToolEnabled
const testCases = [
{ modelSupports: true, settingEnabled: true, expected: true },
{ modelSupports: true, settingEnabled: false, expected: false },
{ modelSupports: false, settingEnabled: true, expected: false },
{ modelSupports: false, settingEnabled: false, expected: false },
{ modelSupports: true, modeSupports: true, settingEnabled: true, expected: true },
{ modelSupports: true, modeSupports: true, settingEnabled: false, expected: false },
{ modelSupports: true, modeSupports: false, settingEnabled: true, expected: false },
{ modelSupports: false, modeSupports: true, settingEnabled: true, expected: false },
{ modelSupports: false, modeSupports: false, settingEnabled: false, expected: false },
]
for (const testCase of testCases) {
// Reset mocks
systemPromptSpy.mockClear()
// Update mock Cline instance
mockCline.api.getModel = jest.fn().mockReturnValue({
id: "test-model",
info: { supportsComputerUse: testCase.modelSupports },
// Mock buildApiHandler to return appropriate model support
;(buildApiHandler as jest.Mock).mockImplementation(() => ({
getModel: jest.fn().mockReturnValue({
id: "test-model",
info: { supportsComputerUse: testCase.modelSupports },
}),
}))
// Mock getModeBySlug to return appropriate mode support
jest.spyOn(modesModule, "getModeBySlug").mockReturnValue({
slug: "test-mode",
name: "Test Mode",
roleDefinition: "Test role",
groups: testCase.modeSupports ? ["read", "browser"] : ["read"],
})
// Mock getState
@ -1506,13 +1560,13 @@ describe("ClineProvider", () => {
apiProvider: "openrouter",
},
browserToolEnabled: testCase.settingEnabled,
mode: "code",
mode: "test-mode",
experiments: experimentDefault,
} as any)
// Trigger getSystemPrompt
const handler = getMessageHandler()
await handler({ type: "getSystemPrompt", mode: "code" })
await handler({ type: "getSystemPrompt", mode: "test-mode" })
// Verify SYSTEM_PROMPT was called
expect(systemPromptSpy).toHaveBeenCalled()

View file

@ -182,7 +182,9 @@ export type GlobalStateKey =
| "lastShownAnnouncementId"
| "customInstructions"
| "alwaysAllowReadOnly"
| "alwaysAllowReadOnlyOutsideWorkspace"
| "alwaysAllowWrite"
| "alwaysAllowWriteOutsideWorkspace"
| "alwaysAllowExecute"
| "alwaysAllowBrowser"
| "alwaysAllowMcp"
@ -238,7 +240,6 @@ export type GlobalStateKey =
| "enhancementApiConfigId"
| "experiments" // Map of experiment IDs to their enabled state
| "autoApprovalEnabled"
| "enableCustomModeCreation" // Enable the ability for Roo to create custom modes
| "customModes" // Array of custom modes
| "unboundModelId"
| "requestyModelId"

View file

@ -1,4 +1,8 @@
{
"input": {
"task_prompt": "Què vols que faci Roo?",
"task_placeholder": "Escriu la teva tasca aquí"
},
"extension": {
"name": "Roo Code",
"description": "Tot un equip de desenvolupadors d'IA al teu editor."
@ -48,7 +52,9 @@
"hmr_not_running": "El servidor de desenvolupament local no està executant-se, l'HMR no funcionarà. Si us plau, executa 'npm run dev' abans de llançar l'extensió per habilitar l'HMR.",
"retrieve_current_mode": "Error en recuperar el mode actual de l'estat.",
"failed_delete_repo": "Ha fallat l'eliminació del repositori o branca associada: {{error}}",
"failed_remove_directory": "Ha fallat l'eliminació del directori de tasques: {{error}}"
"failed_remove_directory": "Ha fallat l'eliminació del directori de tasques: {{error}}",
"custom_storage_path_unusable": "La ruta d'emmagatzematge personalitzada \"{{path}}\" no és utilitzable, s'utilitzarà la ruta predeterminada",
"cannot_access_path": "No es pot accedir a la ruta {{path}}: {{error}}"
},
"warnings": {
"no_terminal_content": "No s'ha seleccionat contingut de terminal",
@ -62,7 +68,9 @@
"mcp_server_connected": "Servidor MCP {{serverName}} connectat",
"mcp_server_deleted": "Servidor MCP eliminat: {{serverName}}",
"mcp_server_not_found": "Servidor \"{{serverName}}\" no trobat a la configuració",
"settings_imported": "Configuració importada correctament."
"settings_imported": "Configuració importada correctament.",
"custom_storage_path_set": "Ruta d'emmagatzematge personalitzada establerta: {{path}}",
"default_storage_path": "S'ha reprès l'ús de la ruta d'emmagatzematge predeterminada"
},
"answers": {
"yes": "Sí",
@ -74,5 +82,11 @@
"tasks": {
"canceled": "Error de tasca: Ha estat aturada i cancel·lada per l'usuari.",
"deleted": "Fallada de tasca: Ha estat aturada i eliminada per l'usuari."
},
"storage": {
"prompt_custom_path": "Introdueix una ruta d'emmagatzematge personalitzada per a l'historial de converses o deixa-ho buit per utilitzar la ubicació predeterminada",
"path_placeholder": "D:\\RooCodeStorage",
"enter_absolute_path": "Introdueix una ruta completa (p. ex. D:\\RooCodeStorage o /home/user/storage)",
"enter_valid_path": "Introdueix una ruta vàlida"
}
}

View file

@ -48,7 +48,9 @@
"hmr_not_running": "Der lokale Entwicklungsserver läuft nicht, HMR wird nicht funktionieren. Bitte führen Sie 'npm run dev' vor dem Start der Erweiterung aus, um HMR zu aktivieren.",
"retrieve_current_mode": "Fehler beim Abrufen des aktuellen Modus aus dem Zustand.",
"failed_delete_repo": "Fehler beim Löschen des zugehörigen Shadow-Repositorys oder -Zweigs: {{error}}",
"failed_remove_directory": "Fehler beim Entfernen des Aufgabenverzeichnisses: {{error}}"
"failed_remove_directory": "Fehler beim Entfernen des Aufgabenverzeichnisses: {{error}}",
"custom_storage_path_unusable": "Benutzerdefinierter Speicherpfad \"{{path}}\" ist nicht verwendbar, Standardpfad wird verwendet",
"cannot_access_path": "Zugriff auf Pfad {{path}} nicht möglich: {{error}}"
},
"warnings": {
"no_terminal_content": "Kein Terminal-Inhalt ausgewählt",
@ -62,7 +64,9 @@
"mcp_server_connected": "MCP-Server {{serverName}} verbunden",
"mcp_server_deleted": "MCP-Server gelöscht: {{serverName}}",
"mcp_server_not_found": "Server \"{{serverName}}\" nicht in der Konfiguration gefunden",
"settings_imported": "Einstellungen erfolgreich importiert."
"settings_imported": "Einstellungen erfolgreich importiert.",
"custom_storage_path_set": "Benutzerdefinierter Speicherpfad festgelegt: {{path}}",
"default_storage_path": "Auf Standardspeicherpfad zurückgesetzt"
},
"answers": {
"yes": "Ja",
@ -74,5 +78,15 @@
"tasks": {
"canceled": "Aufgabenfehler: Die Aufgabe wurde vom Benutzer gestoppt und abgebrochen.",
"deleted": "Aufgabenfehler: Die Aufgabe wurde vom Benutzer gestoppt und gelöscht."
},
"storage": {
"prompt_custom_path": "Gib den benutzerdefinierten Speicherpfad für den Gesprächsverlauf ein, leer lassen für Standardspeicherort",
"path_placeholder": "D:\\RooCodeStorage",
"enter_absolute_path": "Bitte gib einen absoluten Pfad ein (z.B. D:\\RooCodeStorage oder /home/user/storage)",
"enter_valid_path": "Bitte gib einen gültigen Pfad ein"
},
"input": {
"task_prompt": "Was soll Roo tun?",
"task_placeholder": "Gib deine Aufgabe hier ein"
}
}

View file

@ -48,7 +48,9 @@
"hmr_not_running": "Local development server is not running, HMR will not work. Please run 'npm run dev' before launching the extension to enable HMR.",
"retrieve_current_mode": "Error: failed to retrieve current mode from state.",
"failed_delete_repo": "Failed to delete associated shadow repository or branch: {{error}}",
"failed_remove_directory": "Failed to remove task directory: {{error}}"
"failed_remove_directory": "Failed to remove task directory: {{error}}",
"custom_storage_path_unusable": "Custom storage path \"{{path}}\" is unusable, will use default path",
"cannot_access_path": "Cannot access path {{path}}: {{error}}"
},
"warnings": {
"no_terminal_content": "No terminal content selected",
@ -62,7 +64,9 @@
"mcp_server_connected": "{{serverName}} MCP server connected",
"mcp_server_deleted": "Deleted MCP server: {{serverName}}",
"mcp_server_not_found": "Server \"{{serverName}}\" not found in configuration",
"settings_imported": "Settings imported successfully."
"settings_imported": "Settings imported successfully.",
"custom_storage_path_set": "Custom storage path set: {{path}}",
"default_storage_path": "Reverted to using default storage path"
},
"answers": {
"yes": "Yes",
@ -74,5 +78,15 @@
"tasks": {
"canceled": "Task error: It was stopped and canceled by the user.",
"deleted": "Task failure: It was stopped and deleted by the user."
},
"storage": {
"prompt_custom_path": "Enter custom conversation history storage path, leave empty to use default location",
"path_placeholder": "D:\\RooCodeStorage",
"enter_absolute_path": "Please enter an absolute path (e.g. D:\\RooCodeStorage or /home/user/storage)",
"enter_valid_path": "Please enter a valid path"
},
"input": {
"task_prompt": "What should Roo do?",
"task_placeholder": "Type your task here"
}
}

View file

@ -48,7 +48,9 @@
"hmr_not_running": "El servidor de desarrollo local no está en ejecución, HMR no funcionará. Por favor, ejecuta 'npm run dev' antes de lanzar la extensión para habilitar HMR.",
"retrieve_current_mode": "Error al recuperar el modo actual del estado.",
"failed_delete_repo": "Error al eliminar el repositorio o rama asociada: {{error}}",
"failed_remove_directory": "Error al eliminar el directorio de tareas: {{error}}"
"failed_remove_directory": "Error al eliminar el directorio de tareas: {{error}}",
"custom_storage_path_unusable": "La ruta de almacenamiento personalizada \"{{path}}\" no es utilizable, se usará la ruta predeterminada",
"cannot_access_path": "No se puede acceder a la ruta {{path}}: {{error}}"
},
"warnings": {
"no_terminal_content": "No hay contenido de terminal seleccionado",
@ -62,7 +64,9 @@
"mcp_server_connected": "Servidor MCP {{serverName}} conectado",
"mcp_server_deleted": "Servidor MCP eliminado: {{serverName}}",
"mcp_server_not_found": "Servidor \"{{serverName}}\" no encontrado en la configuración",
"settings_imported": "Configuración importada correctamente."
"settings_imported": "Configuración importada correctamente.",
"custom_storage_path_set": "Ruta de almacenamiento personalizada establecida: {{path}}",
"default_storage_path": "Se ha vuelto a usar la ruta de almacenamiento predeterminada"
},
"answers": {
"yes": "Sí",
@ -74,5 +78,15 @@
"tasks": {
"canceled": "Error de tarea: Fue detenida y cancelada por el usuario.",
"deleted": "Fallo de tarea: Fue detenida y eliminada por el usuario."
},
"storage": {
"prompt_custom_path": "Ingresa la ruta de almacenamiento personalizada para el historial de conversaciones, déjala vacía para usar la ubicación predeterminada",
"path_placeholder": "D:\\RooCodeStorage",
"enter_absolute_path": "Por favor, ingresa una ruta absoluta (por ejemplo, D:\\RooCodeStorage o /home/user/storage)",
"enter_valid_path": "Por favor, ingresa una ruta válida"
},
"input": {
"task_prompt": "¿Qué debe hacer Roo?",
"task_placeholder": "Escribe tu tarea aquí"
}
}

View file

@ -48,7 +48,9 @@
"hmr_not_running": "Le serveur de développement local n'est pas en cours d'exécution, HMR ne fonctionnera pas. Veuillez exécuter 'npm run dev' avant de lancer l'extension pour activer l'HMR.",
"retrieve_current_mode": "Erreur lors de la récupération du mode actuel à partir du state.",
"failed_delete_repo": "Échec de la suppression du repo fantôme ou de la branche associée : {{error}}",
"failed_remove_directory": "Échec de la suppression du répertoire de tâches : {{error}}"
"failed_remove_directory": "Échec de la suppression du répertoire de tâches : {{error}}",
"custom_storage_path_unusable": "Le chemin de stockage personnalisé \"{{path}}\" est inutilisable, le chemin par défaut sera utilisé",
"cannot_access_path": "Impossible d'accéder au chemin {{path}} : {{error}}"
},
"warnings": {
"no_terminal_content": "Aucun contenu de terminal sélectionné",
@ -62,7 +64,9 @@
"mcp_server_connected": "Serveur MCP {{serverName}} connecté",
"mcp_server_deleted": "Serveur MCP supprimé : {{serverName}}",
"mcp_server_not_found": "Serveur \"{{serverName}}\" introuvable dans la configuration",
"settings_imported": "Paramètres importés avec succès."
"settings_imported": "Paramètres importés avec succès.",
"custom_storage_path_set": "Chemin de stockage personnalisé défini : {{path}}",
"default_storage_path": "Retour au chemin de stockage par défaut"
},
"answers": {
"yes": "Oui",
@ -74,5 +78,15 @@
"tasks": {
"canceled": "Erreur de tâche : Elle a été arrêtée et annulée par l'utilisateur.",
"deleted": "Échec de la tâche : Elle a été arrêtée et supprimée par l'utilisateur."
},
"storage": {
"prompt_custom_path": "Entrez le chemin de stockage personnalisé pour l'historique des conversations, laissez vide pour utiliser l'emplacement par défaut",
"path_placeholder": "D:\\RooCodeStorage",
"enter_absolute_path": "Veuillez entrer un chemin absolu (ex. D:\\RooCodeStorage ou /home/user/storage)",
"enter_valid_path": "Veuillez entrer un chemin valide"
},
"input": {
"task_prompt": "Que doit faire Roo ?",
"task_placeholder": "Écris ta tâche ici"
}
}

View file

@ -48,7 +48,9 @@
"hmr_not_running": "स्थानीय विकास सर्वर चल नहीं रहा है, HMR काम नहीं करेगा। कृपया HMR सक्षम करने के लिए एक्सटेंशन लॉन्च करने से पहले 'npm run dev' चलाएँ।",
"retrieve_current_mode": "स्टेट से वर्तमान मोड प्राप्त करने में त्रुटि।",
"failed_delete_repo": "संबंधित शैडो रिपॉजिटरी या ब्रांच हटाने में विफल: {{error}}",
"failed_remove_directory": "टास्क डायरेक्टरी हटाने में विफल: {{error}}"
"failed_remove_directory": "टास्क डायरेक्टरी हटाने में विफल: {{error}}",
"custom_storage_path_unusable": "कस्टम स्टोरेज पाथ \"{{path}}\" उपयोग योग्य नहीं है, डिफ़ॉल्ट पाथ का उपयोग किया जाएगा",
"cannot_access_path": "पाथ {{path}} तक पहुंच नहीं पा रहे हैं: {{error}}"
},
"warnings": {
"no_terminal_content": "कोई टर्मिनल सामग्री चयनित नहीं",
@ -62,7 +64,9 @@
"mcp_server_connected": "{{serverName}} MCP सर्वर कनेक्टेड",
"mcp_server_deleted": "MCP सर्वर हटाया गया: {{serverName}}",
"mcp_server_not_found": "सर्वर \"{{serverName}}\" कॉन्फ़िगरेशन में नहीं मिला",
"settings_imported": "सेटिंग्स सफलतापूर्वक आयात की गईं।"
"settings_imported": "सेटिंग्स सफलतापूर्वक आयात की गईं।",
"custom_storage_path_set": "कस्टम स्टोरेज पाथ सेट किया गया: {{path}}",
"default_storage_path": "डिफ़ॉल्ट स्टोरेज पाथ का उपयोग पुनः शुरू किया गया"
},
"answers": {
"yes": "हां",
@ -74,5 +78,15 @@
"tasks": {
"canceled": "टास्क त्रुटि: इसे उपयोगकर्ता द्वारा रोका और रद्द किया गया था।",
"deleted": "टास्क विफलता: इसे उपयोगकर्ता द्वारा रोका और हटाया गया था।"
},
"storage": {
"prompt_custom_path": "वार्तालाप इतिहास के लिए कस्टम स्टोरेज पाथ दर्ज करें, डिफ़ॉल्ट स्थान का उपयोग करने के लिए खाली छोड़ दें",
"path_placeholder": "D:\\RooCodeStorage",
"enter_absolute_path": "कृपया एक पूर्ण पाथ दर्ज करें (उदाहरण: D:\\RooCodeStorage या /home/user/storage)",
"enter_valid_path": "कृपया एक वैध पाथ दर्ज करें"
},
"input": {
"task_prompt": "Roo को क्या करना है?",
"task_placeholder": "अपना कार्य यहाँ लिखें"
}
}

View file

@ -48,7 +48,9 @@
"hmr_not_running": "Il server di sviluppo locale non è in esecuzione, l'HMR non funzionerà. Esegui 'npm run dev' prima di avviare l'estensione per abilitare l'HMR.",
"retrieve_current_mode": "Errore durante il recupero della modalità corrente dallo stato.",
"failed_delete_repo": "Impossibile eliminare il repository o il ramo associato: {{error}}",
"failed_remove_directory": "Impossibile rimuovere la directory delle attività: {{error}}"
"failed_remove_directory": "Impossibile rimuovere la directory delle attività: {{error}}",
"custom_storage_path_unusable": "Il percorso di archiviazione personalizzato \"{{path}}\" non è utilizzabile, verrà utilizzato il percorso predefinito",
"cannot_access_path": "Impossibile accedere al percorso {{path}}: {{error}}"
},
"warnings": {
"no_terminal_content": "Nessun contenuto del terminale selezionato",
@ -62,7 +64,9 @@
"mcp_server_connected": "Server MCP {{serverName}} connesso",
"mcp_server_deleted": "Server MCP eliminato: {{serverName}}",
"mcp_server_not_found": "Server \"{{serverName}}\" non trovato nella configurazione",
"settings_imported": "Impostazioni importate con successo."
"settings_imported": "Impostazioni importate con successo.",
"custom_storage_path_set": "Percorso di archiviazione personalizzato impostato: {{path}}",
"default_storage_path": "Tornato al percorso di archiviazione predefinito"
},
"answers": {
"yes": "Sì",
@ -74,5 +78,15 @@
"tasks": {
"canceled": "Errore attività: È stata interrotta e annullata dall'utente.",
"deleted": "Fallimento attività: È stata interrotta ed eliminata dall'utente."
},
"storage": {
"prompt_custom_path": "Inserisci il percorso di archiviazione personalizzato per la cronologia delle conversazioni, lascia vuoto per utilizzare la posizione predefinita",
"path_placeholder": "D:\\RooCodeStorage",
"enter_absolute_path": "Inserisci un percorso assoluto (ad esempio D:\\RooCodeStorage o /home/user/storage)",
"enter_valid_path": "Inserisci un percorso valido"
},
"input": {
"task_prompt": "Cosa deve fare Roo?",
"task_placeholder": "Scrivi il tuo compito qui"
}
}

View file

@ -48,7 +48,9 @@
"hmr_not_running": "ローカル開発サーバーが実行されていないため、HMRは機能しません。HMRを有効にするには、拡張機能を起動する前に'npm run dev'を実行してください。",
"retrieve_current_mode": "現在のモードを状態から取得する際にエラーが発生しました。",
"failed_delete_repo": "関連するシャドウリポジトリまたはブランチの削除に失敗しました:{{error}}",
"failed_remove_directory": "タスクディレクトリの削除に失敗しました:{{error}}"
"failed_remove_directory": "タスクディレクトリの削除に失敗しました:{{error}}",
"custom_storage_path_unusable": "カスタムストレージパス \"{{path}}\" が使用できないため、デフォルトパスを使用します",
"cannot_access_path": "パス {{path}} にアクセスできません:{{error}}"
},
"warnings": {
"no_terminal_content": "選択されたターミナルコンテンツがありません",
@ -62,7 +64,9 @@
"mcp_server_connected": "MCPサーバー{{serverName}}が接続されました",
"mcp_server_deleted": "MCPサーバーが削除されました{{serverName}}",
"mcp_server_not_found": "サーバー\"{{serverName}}\"が設定内に見つかりません",
"settings_imported": "設定のインポートに成功しました。"
"settings_imported": "設定のインポートに成功しました。",
"custom_storage_path_set": "カスタムストレージパスが設定されました:{{path}}",
"default_storage_path": "デフォルトのストレージパスに戻りました"
},
"answers": {
"yes": "はい",
@ -74,5 +78,15 @@
"tasks": {
"canceled": "タスクエラー:ユーザーによって停止およびキャンセルされました。",
"deleted": "タスク失敗:ユーザーによって停止および削除されました。"
},
"storage": {
"prompt_custom_path": "会話履歴のカスタムストレージパスを入力してください。デフォルトの場所を使用する場合は空のままにしてください",
"path_placeholder": "D:\\RooCodeStorage",
"enter_absolute_path": "絶対パスを入力してくださいD:\\RooCodeStorage または /home/user/storage",
"enter_valid_path": "有効なパスを入力してください"
},
"input": {
"task_prompt": "Rooにどんなことをさせますか",
"task_placeholder": "タスクをここに入力してください"
}
}

View file

@ -48,7 +48,9 @@
"hmr_not_running": "로컬 개발 서버가 실행되고 있지 않아 HMR이 작동하지 않습니다. HMR을 활성화하려면 확장 프로그램을 실행하기 전에 'npm run dev'를 실행하세요.",
"retrieve_current_mode": "상태에서 현재 모드를 검색하는 데 오류가 발생했습니다.",
"failed_delete_repo": "관련 shadow 저장소 또는 브랜치 삭제 실패: {{error}}",
"failed_remove_directory": "작업 디렉토리 제거 실패: {{error}}"
"failed_remove_directory": "작업 디렉토리 제거 실패: {{error}}",
"custom_storage_path_unusable": "사용자 지정 저장 경로 \"{{path}}\"를 사용할 수 없어 기본 경로를 사용합니다",
"cannot_access_path": "경로 {{path}}에 접근할 수 없습니다: {{error}}"
},
"warnings": {
"no_terminal_content": "선택된 터미널 내용이 없습니다",
@ -62,7 +64,9 @@
"mcp_server_connected": "{{serverName}} MCP 서버 연결됨",
"mcp_server_deleted": "MCP 서버 삭제됨: {{serverName}}",
"mcp_server_not_found": "구성에서 서버 \"{{serverName}}\"을(를) 찾을 수 없습니다",
"settings_imported": "설정이 성공적으로 가져와졌습니다."
"settings_imported": "설정이 성공적으로 가져와졌습니다.",
"custom_storage_path_set": "사용자 지정 저장 경로 설정됨: {{path}}",
"default_storage_path": "기본 저장 경로로 되돌아갔습니다"
},
"answers": {
"yes": "예",
@ -74,5 +78,15 @@
"tasks": {
"canceled": "작업 오류: 사용자에 의해 중지 및 취소되었습니다.",
"deleted": "작업 실패: 사용자에 의해 중지 및 삭제되었습니다."
},
"storage": {
"prompt_custom_path": "대화 내역을 위한 사용자 지정 저장 경로를 입력하세요. 기본 위치를 사용하려면 비워두세요",
"path_placeholder": "D:\\RooCodeStorage",
"enter_absolute_path": "절대 경로를 입력하세요 (예: D:\\RooCodeStorage 또는 /home/user/storage)",
"enter_valid_path": "유효한 경로를 입력하세요"
},
"input": {
"task_prompt": "Roo에게 무엇을 시킬까요?",
"task_placeholder": "여기에 작업을 입력하세요"
}
}

View file

@ -48,7 +48,9 @@
"hmr_not_running": "Lokalny serwer deweloperski nie jest uruchomiony, HMR nie będzie działać. Uruchom 'npm run dev' przed uruchomieniem rozszerzenia, aby włączyć HMR.",
"retrieve_current_mode": "Błąd podczas pobierania bieżącego trybu ze stanu.",
"failed_delete_repo": "Nie udało się usunąć powiązanego repozytorium lub gałęzi pomocniczej: {{error}}",
"failed_remove_directory": "Nie udało się usunąć katalogu zadania: {{error}}"
"failed_remove_directory": "Nie udało się usunąć katalogu zadania: {{error}}",
"custom_storage_path_unusable": "Niestandardowa ścieżka przechowywania \"{{path}}\" nie jest użyteczna, zostanie użyta domyślna ścieżka",
"cannot_access_path": "Nie można uzyskać dostępu do ścieżki {{path}}: {{error}}"
},
"warnings": {
"no_terminal_content": "Nie wybrano zawartości terminala",
@ -62,7 +64,9 @@
"mcp_server_connected": "Serwer MCP {{serverName}} połączony",
"mcp_server_deleted": "Usunięto serwer MCP: {{serverName}}",
"mcp_server_not_found": "Serwer \"{{serverName}}\" nie znaleziony w konfiguracji",
"settings_imported": "Ustawienia zostały pomyślnie zaimportowane."
"settings_imported": "Ustawienia zostały pomyślnie zaimportowane.",
"custom_storage_path_set": "Ustawiono niestandardową ścieżkę przechowywania: {{path}}",
"default_storage_path": "Wznowiono używanie domyślnej ścieżki przechowywania"
},
"answers": {
"yes": "Tak",
@ -74,5 +78,15 @@
"tasks": {
"canceled": "Błąd zadania: Zostało zatrzymane i anulowane przez użytkownika.",
"deleted": "Niepowodzenie zadania: Zostało zatrzymane i usunięte przez użytkownika."
},
"storage": {
"prompt_custom_path": "Wprowadź niestandardową ścieżkę przechowywania dla historii konwersacji lub pozostaw puste, aby użyć lokalizacji domyślnej",
"path_placeholder": "D:\\RooCodeStorage",
"enter_absolute_path": "Wprowadź pełną ścieżkę (np. D:\\RooCodeStorage lub /home/user/storage)",
"enter_valid_path": "Wprowadź prawidłową ścieżkę"
},
"input": {
"task_prompt": "Co ma zrobić Roo?",
"task_placeholder": "Wpisz swoje zadanie tutaj"
}
}

View file

@ -1,4 +1,8 @@
{
"input": {
"task_prompt": "O que você quer que o Roo faça?",
"task_placeholder": "Digite sua tarefa aqui"
},
"extension": {
"name": "Roo Code",
"description": "Uma equipe completa de desenvolvedores com IA em seu editor."
@ -48,7 +52,9 @@
"hmr_not_running": "O servidor de desenvolvimento local não está em execução, o HMR não funcionará. Por favor, execute 'npm run dev' antes de iniciar a extensão para habilitar o HMR.",
"retrieve_current_mode": "Erro ao recuperar o modo atual do estado.",
"failed_delete_repo": "Falha ao excluir o repositório ou ramificação associada: {{error}}",
"failed_remove_directory": "Falha ao remover o diretório de tarefas: {{error}}"
"failed_remove_directory": "Falha ao remover o diretório de tarefas: {{error}}",
"custom_storage_path_unusable": "O caminho de armazenamento personalizado \"{{path}}\" não pode ser usado, será usado o caminho padrão",
"cannot_access_path": "Não é possível acessar o caminho {{path}}: {{error}}"
},
"warnings": {
"no_terminal_content": "Nenhum conteúdo do terminal selecionado",
@ -62,7 +68,9 @@
"mcp_server_connected": "Servidor MCP {{serverName}} conectado",
"mcp_server_deleted": "Servidor MCP excluído: {{serverName}}",
"mcp_server_not_found": "Servidor \"{{serverName}}\" não encontrado na configuração",
"settings_imported": "Configurações importadas com sucesso."
"settings_imported": "Configurações importadas com sucesso.",
"custom_storage_path_set": "Caminho de armazenamento personalizado definido: {{path}}",
"default_storage_path": "Retornado ao caminho de armazenamento padrão"
},
"answers": {
"yes": "Sim",
@ -74,5 +82,11 @@
"tasks": {
"canceled": "Erro na tarefa: Foi interrompida e cancelada pelo usuário.",
"deleted": "Falha na tarefa: Foi interrompida e excluída pelo usuário."
},
"storage": {
"prompt_custom_path": "Digite o caminho de armazenamento personalizado para o histórico de conversas, deixe em branco para usar o local padrão",
"path_placeholder": "D:\\RooCodeStorage",
"enter_absolute_path": "Por favor, digite um caminho absoluto (ex: D:\\RooCodeStorage ou /home/user/storage)",
"enter_valid_path": "Por favor, digite um caminho válido"
}
}

View file

@ -48,7 +48,9 @@
"hmr_not_running": "Yerel geliştirme sunucusu çalışmıyor, HMR çalışmayacak. HMR'yi etkinleştirmek için uzantıyı başlatmadan önce lütfen 'npm run dev' komutunu çalıştırın.",
"retrieve_current_mode": "Mevcut mod durumdan alınırken hata oluştu.",
"failed_delete_repo": "İlişkili gölge depo veya dal silinemedi: {{error}}",
"failed_remove_directory": "Görev dizini kaldırılamadı: {{error}}"
"failed_remove_directory": "Görev dizini kaldırılamadı: {{error}}",
"custom_storage_path_unusable": "Özel depolama yolu \"{{path}}\" kullanılamıyor, varsayılan yol kullanılacak",
"cannot_access_path": "{{path}} yoluna erişilemiyor: {{error}}"
},
"warnings": {
"no_terminal_content": "Seçili terminal içeriği yok",
@ -62,7 +64,9 @@
"mcp_server_connected": "{{serverName}} MCP sunucusu bağlandı",
"mcp_server_deleted": "MCP sunucusu silindi: {{serverName}}",
"mcp_server_not_found": "Yapılandırmada \"{{serverName}}\" sunucusu bulunamadı",
"settings_imported": "Ayarlar başarıyla içe aktarıldı."
"settings_imported": "Ayarlar başarıyla içe aktarıldı.",
"custom_storage_path_set": "Özel depolama yolu ayarlandı: {{path}}",
"default_storage_path": "Varsayılan depolama yoluna geri dönüldü"
},
"answers": {
"yes": "Evet",
@ -74,5 +78,15 @@
"tasks": {
"canceled": "Görev hatası: Kullanıcı tarafından durduruldu ve iptal edildi.",
"deleted": "Görev başarısız: Kullanıcı tarafından durduruldu ve silindi."
},
"storage": {
"prompt_custom_path": "Konuşma geçmişi için özel depolama yolunu girin, varsayılan konumu kullanmak için boş bırakın",
"path_placeholder": "D:\\RooCodeStorage",
"enter_absolute_path": "Lütfen mutlak bir yol girin (örn. D:\\RooCodeStorage veya /home/user/storage)",
"enter_valid_path": "Lütfen geçerli bir yol girin"
},
"input": {
"task_prompt": "Roo ne yapsın?",
"task_placeholder": "Görevini buraya yaz"
}
}

View file

@ -48,7 +48,9 @@
"hmr_not_running": "Máy chủ phát triển cục bộ không chạy, HMR sẽ không hoạt động. Vui lòng chạy 'npm run dev' trước khi khởi chạy tiện ích mở rộng để bật HMR.",
"retrieve_current_mode": "Lỗi không thể truy xuất chế độ hiện tại từ trạng thái.",
"failed_delete_repo": "Không thể xóa kho lưu trữ hoặc nhánh liên quan: {{error}}",
"failed_remove_directory": "Không thể xóa thư mục nhiệm vụ: {{error}}"
"failed_remove_directory": "Không thể xóa thư mục nhiệm vụ: {{error}}",
"custom_storage_path_unusable": "Đường dẫn lưu trữ tùy chỉnh \"{{path}}\" không thể sử dụng được, sẽ sử dụng đường dẫn mặc định",
"cannot_access_path": "Không thể truy cập đường dẫn {{path}}: {{error}}"
},
"warnings": {
"no_terminal_content": "Không có nội dung terminal được chọn",
@ -62,7 +64,9 @@
"mcp_server_connected": "Máy chủ MCP {{serverName}} đã kết nối",
"mcp_server_deleted": "Đã xóa máy chủ MCP: {{serverName}}",
"mcp_server_not_found": "Không tìm thấy máy chủ \"{{serverName}}\" trong cấu hình",
"settings_imported": "Cài đặt đã được nhập thành công."
"settings_imported": "Cài đặt đã được nhập thành công.",
"custom_storage_path_set": "Đã thiết lập đường dẫn lưu trữ tùy chỉnh: {{path}}",
"default_storage_path": "Đã quay lại sử dụng đường dẫn lưu trữ mặc định"
},
"answers": {
"yes": "Có",
@ -74,5 +78,15 @@
"tasks": {
"canceled": "Lỗi nhiệm vụ: Nó đã bị dừng và hủy bởi người dùng.",
"deleted": "Nhiệm vụ thất bại: Nó đã bị dừng và xóa bởi người dùng."
},
"storage": {
"prompt_custom_path": "Nhập đường dẫn lưu trữ tùy chỉnh cho lịch sử hội thoại, để trống để sử dụng vị trí mặc định",
"path_placeholder": "D:\\RooCodeStorage",
"enter_absolute_path": "Vui lòng nhập đường dẫn tuyệt đối (ví dụ: D:\\RooCodeStorage hoặc /home/user/storage)",
"enter_valid_path": "Vui lòng nhập đường dẫn hợp lệ"
},
"input": {
"task_prompt": "Bạn muốn Roo làm gì?",
"task_placeholder": "Nhập nhiệm vụ của bạn ở đây"
}
}

View file

@ -48,7 +48,9 @@
"hmr_not_running": "本地开发服务器未运行HMR将不起作用。请在启动扩展前运行'npm run dev'以启用HMR。",
"retrieve_current_mode": "从状态中检索当前模式失败。",
"failed_delete_repo": "删除关联的影子仓库或分支失败:{{error}}",
"failed_remove_directory": "删除任务目录失败:{{error}}"
"failed_remove_directory": "删除任务目录失败:{{error}}",
"custom_storage_path_unusable": "自定义存储路径 \"{{path}}\" 不可用,将使用默认路径",
"cannot_access_path": "无法访问路径 {{path}}{{error}}"
},
"warnings": {
"no_terminal_content": "没有选择终端内容",
@ -62,7 +64,9 @@
"mcp_server_connected": "{{serverName}}MCP服务器已连接",
"mcp_server_deleted": "已删除MCP服务器{{serverName}}",
"mcp_server_not_found": "在配置中未找到服务器\"{{serverName}}\"",
"settings_imported": "设置已成功导入。"
"settings_imported": "设置已成功导入。",
"custom_storage_path_set": "自定义存储路径已设置:{{path}}",
"default_storage_path": "已恢复使用默认存储路径"
},
"answers": {
"yes": "是",
@ -74,5 +78,15 @@
"tasks": {
"canceled": "任务错误:它已被用户停止并取消。",
"deleted": "任务失败:它已被用户停止并删除。"
},
"storage": {
"prompt_custom_path": "输入自定义会话历史存储路径,留空以使用默认位置",
"path_placeholder": "D:\\RooCodeStorage",
"enter_absolute_path": "请输入绝对路径(例如 D:\\RooCodeStorage 或 /home/user/storage",
"enter_valid_path": "请输入有效的路径"
},
"input": {
"task_prompt": "让Roo做什么",
"task_placeholder": "在这里输入任务"
}
}

View file

@ -48,7 +48,9 @@
"hmr_not_running": "本地開發服務器未運行HMR將不起作用。請在啟動擴展前運行'npm run dev'以啟用HMR。",
"retrieve_current_mode": "從狀態中檢索當前模式失敗。",
"failed_delete_repo": "刪除關聯的影子倉庫或分支失敗:{{error}}",
"failed_remove_directory": "刪除任務目錄失敗:{{error}}"
"failed_remove_directory": "刪除任務目錄失敗:{{error}}",
"custom_storage_path_unusable": "自定義存儲路徑 \"{{path}}\" 不可用,將使用默認路徑",
"cannot_access_path": "無法訪問路徑 {{path}}{{error}}"
},
"warnings": {
"no_terminal_content": "沒有選擇終端內容",
@ -62,7 +64,9 @@
"mcp_server_connected": "{{serverName}}MCP服務器已連接",
"mcp_server_deleted": "已刪除MCP服務器{{serverName}}",
"mcp_server_not_found": "在配置中未找到服務器\"{{serverName}}\"",
"settings_imported": "設置已成功導入。"
"settings_imported": "設置已成功導入。",
"custom_storage_path_set": "自定義存儲路徑已設置:{{path}}",
"default_storage_path": "已恢復使用默認存儲路徑"
},
"answers": {
"yes": "是",
@ -74,5 +78,15 @@
"tasks": {
"canceled": "任務錯誤:它已被用戶停止並取消。",
"deleted": "任務失敗:它已被用戶停止並刪除。"
},
"storage": {
"prompt_custom_path": "輸入自定義會話歷史存儲路徑,留空以使用默認位置",
"path_placeholder": "D:\\RooCodeStorage",
"enter_absolute_path": "請輸入絕對路徑(例如 D:\\RooCodeStorage 或 /home/user/storage",
"enter_valid_path": "請輸入有效的路徑"
},
"input": {
"task_prompt": "讓Roo做什麼",
"task_placeholder": "在這裡輸入任務"
}
}

View file

@ -23,23 +23,6 @@ export async function openImage(dataUri: string) {
interface OpenFileOptions {
create?: boolean
content?: string
searchParents?: boolean
startFromWorkspace?: boolean
}
async function findFileInParentDirs(searchPath: string, fileName: string): Promise<string | null> {
try {
const fullPath = path.join(searchPath, fileName)
await vscode.workspace.fs.stat(vscode.Uri.file(fullPath))
return fullPath
} catch {
const parentDir = path.dirname(searchPath)
if (parentDir === searchPath) {
// Hit root
return null
}
return findFileInParentDirs(parentDir, fileName)
}
}
export async function openFile(filePath: string, options: OpenFileOptions = {}) {
@ -51,17 +34,7 @@ export async function openFile(filePath: string, options: OpenFileOptions = {})
}
// If path starts with ./, resolve it relative to workspace root
let fullPath = filePath.startsWith("./") ? path.join(workspaceRoot, filePath.slice(2)) : filePath
// Handle recursive search
if (options.searchParents) {
const startDir = options.startFromWorkspace ? workspaceRoot : path.dirname(fullPath)
const fileName = path.basename(filePath)
const foundPath = await findFileInParentDirs(startDir, fileName)
if (foundPath) {
fullPath = foundPath
}
}
const fullPath = filePath.startsWith("./") ? path.join(workspaceRoot, filePath.slice(2)) : filePath
const uri = vscode.Uri.file(fullPath)

View file

@ -42,6 +42,7 @@ const BaseConfigSchema = z.object({
disabled: z.boolean().optional(),
timeout: z.number().min(1).max(3600).optional().default(60),
alwaysAllow: z.array(z.string()).default([]),
watchPaths: z.array(z.string()).optional(), // paths to watch for changes and restart server
})
// Custom error messages for better user feedback
@ -102,7 +103,7 @@ export class McpHub {
private providerRef: WeakRef<ClineProvider>
private disposables: vscode.Disposable[] = []
private settingsWatcher?: vscode.FileSystemWatcher
private fileWatchers: Map<string, FSWatcher> = new Map()
private fileWatchers: Map<string, FSWatcher[]> = new Map()
private isDisposed: boolean = false
connections: McpConnection[] = []
isConnecting: boolean = false
@ -562,29 +563,68 @@ export class McpHub {
}
private setupFileWatcher(name: string, config: z.infer<typeof ServerConfigSchema>) {
// Initialize an empty array for this server if it doesn't exist
if (!this.fileWatchers.has(name)) {
this.fileWatchers.set(name, [])
}
const watchers = this.fileWatchers.get(name) || []
// Only stdio type has args
if (config.type === "stdio") {
// Setup watchers for custom watchPaths if defined
if (config.watchPaths && config.watchPaths.length > 0) {
console.log(`Setting up custom path watchers for ${name} MCP server...`)
const watchPathsWatcher = chokidar.watch(config.watchPaths, {
// persistent: true,
// ignoreInitial: true,
// awaitWriteFinish: true,
})
watchPathsWatcher.on("change", async (changedPath) => {
console.log(`Detected change in custom path ${changedPath}. Restarting server ${name}...`)
try {
await this.restartConnection(name)
} catch (error) {
console.error(`Failed to restart server ${name} after change in ${changedPath}:`, error)
}
})
watchers.push(watchPathsWatcher)
}
// Also setup the fallback build/index.js watcher if applicable
const filePath = config.args?.find((arg: string) => arg.includes("build/index.js"))
if (filePath) {
// we use chokidar instead of onDidSaveTextDocument because it doesn't require the file to be open in the editor. The settings config is better suited for onDidSave since that will be manually updated by the user or Cline (and we want to detect save events, not every file change)
const watcher = chokidar.watch(filePath, {
console.log(`Setting up build/index.js watcher for ${name} MCP server...`)
// we use chokidar instead of onDidSaveTextDocument because it doesn't require the file to be open in the editor
const indexJsWatcher = chokidar.watch(filePath, {
// persistent: true,
// ignoreInitial: true,
// awaitWriteFinish: true, // This helps with atomic writes
})
watcher.on("change", () => {
indexJsWatcher.on("change", async () => {
console.log(`Detected change in ${filePath}. Restarting server ${name}...`)
this.restartConnection(name)
try {
await this.restartConnection(name)
} catch (error) {
console.error(`Failed to restart server ${name} after change in ${filePath}:`, error)
}
})
this.fileWatchers.set(name, watcher)
watchers.push(indexJsWatcher)
}
// Update the fileWatchers map with all watchers for this server
if (watchers.length > 0) {
this.fileWatchers.set(name, watchers)
}
}
}
private removeAllFileWatchers() {
this.fileWatchers.forEach((watcher) => watcher.close())
this.fileWatchers.forEach((watchers) => watchers.forEach((watcher) => watcher.close()))
this.fileWatchers.clear()
}

View file

@ -120,7 +120,9 @@ export interface ExtensionState {
customModePrompts?: CustomModePrompts
customSupportPrompts?: CustomSupportPrompts
alwaysAllowReadOnly?: boolean
alwaysAllowReadOnlyOutsideWorkspace?: boolean
alwaysAllowWrite?: boolean
alwaysAllowWriteOutsideWorkspace?: boolean
alwaysAllowExecute?: boolean
alwaysAllowBrowser?: boolean
alwaysAllowMcp?: boolean
@ -151,7 +153,6 @@ export interface ExtensionState {
terminalShellIntegrationTimeout?: number
mcpEnabled: boolean
enableMcpServerCreation: boolean
enableCustomModeCreation?: boolean
mode: Mode
modeApiConfigs?: Record<Mode, string>
enhancementApiConfigId?: string
@ -179,6 +180,7 @@ export interface ClineSayTool {
| "appliedDiff"
| "newFileCreated"
| "readFile"
| "fetchInstructions"
| "listFilesTopLevel"
| "listFilesRecursive"
| "listCodeDefinitionNames"
@ -193,6 +195,7 @@ export interface ClineSayTool {
filePattern?: string
mode?: string
reason?: string
isOutsideWorkspace?: boolean
}
// Must keep in sync with system prompt.

View file

@ -22,7 +22,9 @@ export interface WebviewMessage {
| "customInstructions"
| "allowedCommands"
| "alwaysAllowReadOnly"
| "alwaysAllowReadOnlyOutsideWorkspace"
| "alwaysAllowWrite"
| "alwaysAllowWriteOutsideWorkspace"
| "alwaysAllowExecute"
| "webviewDidLaunch"
| "newTask"
@ -80,7 +82,6 @@ export interface WebviewMessage {
| "terminalShellIntegrationTimeout"
| "mcpEnabled"
| "enableMcpServerCreation"
| "enableCustomModeCreation"
| "searchCommits"
| "alwaysApproveResubmit"
| "requestDelaySeconds"

View file

@ -881,6 +881,14 @@ export const openAiModelInfoSaneDefaults: ModelInfo = {
export type GeminiModelId = keyof typeof geminiModels
export const geminiDefaultModelId: GeminiModelId = "gemini-2.0-flash-001"
export const geminiModels = {
"gemini-2.5-pro-exp-03-25": {
maxTokens: 65_536,
contextWindow: 1_048_576,
supportsImages: true,
supportsPromptCache: false,
inputPrice: 0,
outputPrice: 0,
},
"gemini-2.0-flash-001": {
maxTokens: 8192,
contextWindow: 1_048_576,
@ -989,7 +997,7 @@ export const openAiNativeModels = {
maxTokens: 100_000,
contextWindow: 200_000,
supportsImages: false,
supportsPromptCache: false,
supportsPromptCache: true,
inputPrice: 1.1,
outputPrice: 4.4,
reasoningEffort: "medium",
@ -998,7 +1006,7 @@ export const openAiNativeModels = {
maxTokens: 100_000,
contextWindow: 200_000,
supportsImages: false,
supportsPromptCache: false,
supportsPromptCache: true,
inputPrice: 1.1,
outputPrice: 4.4,
reasoningEffort: "high",
@ -1007,7 +1015,7 @@ export const openAiNativeModels = {
maxTokens: 100_000,
contextWindow: 200_000,
supportsImages: false,
supportsPromptCache: false,
supportsPromptCache: true,
inputPrice: 1.1,
outputPrice: 4.4,
reasoningEffort: "low",
@ -1016,7 +1024,7 @@ export const openAiNativeModels = {
maxTokens: 100_000,
contextWindow: 200_000,
supportsImages: true,
supportsPromptCache: false,
supportsPromptCache: true,
inputPrice: 15,
outputPrice: 60,
},
@ -1024,7 +1032,7 @@ export const openAiNativeModels = {
maxTokens: 32_768,
contextWindow: 128_000,
supportsImages: true,
supportsPromptCache: false,
supportsPromptCache: true,
inputPrice: 15,
outputPrice: 60,
},
@ -1032,7 +1040,7 @@ export const openAiNativeModels = {
maxTokens: 65_536,
contextWindow: 128_000,
supportsImages: true,
supportsPromptCache: false,
supportsPromptCache: true,
inputPrice: 1.1,
outputPrice: 4.4,
},
@ -1040,7 +1048,7 @@ export const openAiNativeModels = {
maxTokens: 16_384,
contextWindow: 128_000,
supportsImages: true,
supportsPromptCache: false,
supportsPromptCache: true,
inputPrice: 75,
outputPrice: 150,
},
@ -1048,7 +1056,7 @@ export const openAiNativeModels = {
maxTokens: 16_384,
contextWindow: 128_000,
supportsImages: true,
supportsPromptCache: false,
supportsPromptCache: true,
inputPrice: 2.5,
outputPrice: 10,
},
@ -1056,7 +1064,7 @@ export const openAiNativeModels = {
maxTokens: 16_384,
contextWindow: 128_000,
supportsImages: true,
supportsPromptCache: false,
supportsPromptCache: true,
inputPrice: 0.15,
outputPrice: 0.6,
},

View file

@ -64,7 +64,9 @@ export const GLOBAL_STATE_KEYS = [
"lastShownAnnouncementId",
"customInstructions",
"alwaysAllowReadOnly",
"alwaysAllowReadOnlyOutsideWorkspace",
"alwaysAllowWrite",
"alwaysAllowWriteOutsideWorkspace",
"alwaysAllowExecute",
"alwaysAllowBrowser",
"alwaysAllowMcp",
@ -120,7 +122,6 @@ export const GLOBAL_STATE_KEYS = [
"enhancementApiConfigId",
"experiments", // Map of experiment IDs to their enabled state.
"autoApprovalEnabled",
"enableCustomModeCreation", // Enable the ability for Roo to create custom modes.
"customModes", // Array of custom modes.
"unboundModelId",
"requestyModelId",

View file

@ -0,0 +1,149 @@
import * as vscode from "vscode"
import * as path from "path"
import * as fs from "fs/promises"
import { t } from "../i18n"
/**
* Gets the base storage path for conversations
* If a custom path is configured, uses that path
* Otherwise uses the default VSCode extension global storage path
*/
export async function getStorageBasePath(defaultPath: string): Promise<string> {
// Get user-configured custom storage path
let customStoragePath = ""
try {
// This is the line causing the error in tests
const config = vscode.workspace.getConfiguration("roo-cline")
customStoragePath = config.get<string>("customStoragePath", "")
} catch (error) {
console.warn("Could not access VSCode configuration - using default path")
return defaultPath
}
// If no custom path is set, use default path
if (!customStoragePath) {
return defaultPath
}
try {
// Ensure custom path exists
await fs.mkdir(customStoragePath, { recursive: true })
// Test if path is writable
const testFile = path.join(customStoragePath, ".write_test")
await fs.writeFile(testFile, "test")
await fs.rm(testFile)
return customStoragePath
} catch (error) {
// If path is unusable, report error and fall back to default path
console.error(`Custom storage path is unusable: ${error instanceof Error ? error.message : String(error)}`)
if (vscode.window) {
vscode.window.showErrorMessage(t("common:errors.custom_storage_path_unusable", { path: customStoragePath }))
}
return defaultPath
}
}
/**
* Gets the storage directory path for a task
*/
export async function getTaskDirectoryPath(globalStoragePath: string, taskId: string): Promise<string> {
const basePath = await getStorageBasePath(globalStoragePath)
const taskDir = path.join(basePath, "tasks", taskId)
await fs.mkdir(taskDir, { recursive: true })
return taskDir
}
/**
* Gets the settings directory path
*/
export async function getSettingsDirectoryPath(globalStoragePath: string): Promise<string> {
const basePath = await getStorageBasePath(globalStoragePath)
const settingsDir = path.join(basePath, "settings")
await fs.mkdir(settingsDir, { recursive: true })
return settingsDir
}
/**
* Gets the cache directory path
*/
export async function getCacheDirectoryPath(globalStoragePath: string): Promise<string> {
const basePath = await getStorageBasePath(globalStoragePath)
const cacheDir = path.join(basePath, "cache")
await fs.mkdir(cacheDir, { recursive: true })
return cacheDir
}
/**
* Prompts the user to set a custom storage path
* Displays an input box allowing the user to enter a custom path
*/
export async function promptForCustomStoragePath(): Promise<void> {
if (!vscode.window || !vscode.workspace) {
console.error("VS Code API not available")
return
}
let currentPath = ""
try {
const currentConfig = vscode.workspace.getConfiguration("roo-cline")
currentPath = currentConfig.get<string>("customStoragePath", "")
} catch (error) {
console.error("Could not access configuration")
return
}
const result = await vscode.window.showInputBox({
value: currentPath,
placeHolder: t("common:storage.path_placeholder"),
prompt: t("common:storage.prompt_custom_path"),
validateInput: (input) => {
if (!input) {
return null // Allow empty value (use default path)
}
try {
// Validate path format
path.parse(input)
// Check if path is absolute
if (!path.isAbsolute(input)) {
return t("common:storage.enter_absolute_path")
}
return null // Path format is valid
} catch (e) {
return t("common:storage.enter_valid_path")
}
},
})
// If user canceled the operation, result will be undefined
if (result !== undefined) {
try {
const currentConfig = vscode.workspace.getConfiguration("roo-cline")
await currentConfig.update("customStoragePath", result, vscode.ConfigurationTarget.Global)
if (result) {
try {
// Test if path is accessible
await fs.mkdir(result, { recursive: true })
vscode.window.showInformationMessage(t("common:info.custom_storage_path_set", { path: result }))
} catch (error) {
vscode.window.showErrorMessage(
t("common:errors.cannot_access_path", {
path: result,
error: error instanceof Error ? error.message : String(error),
}),
)
}
} else {
vscode.window.showInformationMessage(t("common:info.default_storage_path"))
}
} catch (error) {
console.error("Failed to update configuration", error)
}
}
}

View file

@ -25,24 +25,16 @@ export const createPrompt = (template: string, params: PromptParams): string =>
}
interface SupportPromptConfig {
label: string
description: string
template: string
}
const supportPromptConfigs: Record<string, SupportPromptConfig> = {
ENHANCE: {
label: "Enhance Prompt",
description:
"Use prompt enhancement to get tailored suggestions or improvements for your inputs. This ensures Roo understands your intent and provides the best possible responses. Available via the ✨ icon in chat.",
template: `Generate an enhanced version of this prompt (reply with only the enhanced prompt - no conversation, explanations, lead-in, bullet points, placeholders, or surrounding quotes):
\${userInput}`,
},
EXPLAIN: {
label: "Explain Code",
description:
"Get detailed explanations of code snippets, functions, or entire files. Useful for understanding complex code or learning new patterns. Available in code actions (lightbulb icon in the editor) and the editor context menu (right-click on selected code).",
template: `Explain the following code from file path @/\${filePath}:
\${userInput}
@ -56,9 +48,6 @@ Please provide a clear and concise explanation of what this code does, including
3. Important patterns or techniques used`,
},
FIX: {
label: "Fix Issues",
description:
"Get help identifying and resolving bugs, errors, or code quality issues. Provides step-by-step guidance for fixing problems. Available in code actions (lightbulb icon in the editor) and the editor context menu (right-click on selected code).",
template: `Fix any issues in the following code from file path @/\${filePath}
\${diagnosticText}
\${userInput}
@ -74,9 +63,6 @@ Please:
4. Explain what was fixed and why`,
},
IMPROVE: {
label: "Improve Code",
description:
"Receive suggestions for code optimization, better practices, and architectural improvements while maintaining functionality. Available in code actions (lightbulb icon in the editor) and the editor context menu (right-click on selected code).",
template: `Improve the following code from file path @/\${filePath}:
\${userInput}
@ -93,18 +79,12 @@ Please suggest improvements for:
Provide the improved code along with explanations for each enhancement.`,
},
ADD_TO_CONTEXT: {
label: "Add to Context",
description:
"Add context to your current task or conversation. Useful for providing additional information or clarifications. Available in code actions (lightbulb icon in the editor). and the editor context menu (right-click on selected code).",
template: `\${filePath}:
\`\`\`
\${selectedText}
\`\`\``,
},
TERMINAL_ADD_TO_CONTEXT: {
label: "Add Terminal Content to Context",
description:
"Add terminal output to your current task or conversation. Useful for providing command outputs or logs. Available in the terminal context menu (right-click on selected terminal content).",
template: `\${userInput}
Terminal output:
\`\`\`
@ -112,9 +92,6 @@ Terminal output:
\`\`\``,
},
TERMINAL_FIX: {
label: "Fix Terminal Command",
description:
"Get help fixing terminal commands that failed or need improvement. Available in the terminal context menu (right-click on selected terminal content).",
template: `\${userInput}
Fix this terminal command:
\`\`\`
@ -127,9 +104,6 @@ Please:
3. Explain what was fixed and why`,
},
TERMINAL_EXPLAIN: {
label: "Explain Terminal Command",
description:
"Get detailed explanations of terminal commands and their outputs. Available in the terminal context menu (right-click on selected terminal content).",
template: `\${userInput}
Explain this terminal command:
\`\`\`
@ -141,6 +115,9 @@ Please provide:
2. Explanation of each part/flag
3. Expected output and behavior`,
},
NEW_TASK: {
template: `\${userInput}`,
},
} as const
type SupportPromptType = keyof typeof supportPromptConfigs
@ -158,15 +135,6 @@ export const supportPrompt = {
export type { SupportPromptType }
// Expose labels and descriptions for UI
export const supportPromptLabels = Object.fromEntries(
Object.entries(supportPromptConfigs).map(([key, config]) => [key, config.label]),
) as Record<SupportPromptType, string>
export const supportPromptDescriptions = Object.fromEntries(
Object.entries(supportPromptConfigs).map(([key, config]) => [key, config.description]),
) as Record<SupportPromptType, string>
export type CustomSupportPrompts = {
[key: string]: string | undefined
}

View file

@ -8,6 +8,7 @@ export type ToolGroupConfig = {
export const TOOL_DISPLAY_NAMES = {
execute_command: "run commands",
read_file: "read files",
fetch_instructions: "fetch instructions",
write_to_file: "write files",
apply_diff: "apply changes",
search_files: "search files",
@ -25,7 +26,7 @@ export const TOOL_DISPLAY_NAMES = {
// Define available tool groups
export const TOOL_GROUPS: Record<string, ToolGroupConfig> = {
read: {
tools: ["read_file", "search_files", "list_files", "list_code_definition_names"],
tools: ["read_file", "fetch_instructions", "search_files", "list_files", "list_code_definition_names"],
},
edit: {
tools: ["apply_diff", "write_to_file", "insert_content", "search_and_replace"],

24
src/utils/pathUtils.ts Normal file
View file

@ -0,0 +1,24 @@
import * as vscode from "vscode"
import * as path from "path"
/**
* Checks if a file path is outside all workspace folders
* @param filePath The file path to check
* @returns true if the path is outside all workspace folders, false otherwise
*/
export function isPathOutsideWorkspace(filePath: string): boolean {
// If there are no workspace folders, consider everything outside workspace for safety
if (!vscode.workspace.workspaceFolders || vscode.workspace.workspaceFolders.length === 0) {
return true
}
// Normalize and resolve the path to handle .. and . components correctly
const absolutePath = path.resolve(filePath)
// Check if the path is within any workspace folder
return !vscode.workspace.workspaceFolders.some((folder) => {
const folderPath = folder.uri.fsPath
// Path is inside a workspace if it equals the workspace path or is a subfolder
return absolutePath === folderPath || absolutePath.startsWith(folderPath + path.sep)
})
}

View file

@ -484,27 +484,27 @@
}
},
"node_modules/@babel/helpers": {
"version": "7.26.7",
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.7.tgz",
"integrity": "sha512-8NHiL98vsi0mbPQmYAGWwfcFaOy4j2HY49fXJCfuDcdE7fMIsH9a7GdaeXpIBsbT7307WU8KCMp5pUVDNL4f9A==",
"version": "7.26.10",
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.10.tgz",
"integrity": "sha512-UPYc3SauzZ3JGgj87GgZ89JVdC5dj0AoetR5Bw6wj4niittNyFh6+eOGonYvJ1ao6B8lEa3Q3klS7ADZ53bc5g==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/template": "^7.25.9",
"@babel/types": "^7.26.7"
"@babel/template": "^7.26.9",
"@babel/types": "^7.26.10"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/parser": {
"version": "7.26.7",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.7.tgz",
"integrity": "sha512-kEvgGGgEjRUutvdVvZhbn/BxVt+5VSpwXz1j3WYXQbXDo8KzFOPNG2GQbdAiNq8g6wn1yKk7C/qrke03a84V+w==",
"version": "7.26.10",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.10.tgz",
"integrity": "sha512-6aQR2zGE/QFi8JpDLjUZEPYOs7+mhKXm86VaKFiLP35JQwQb6bwUE+XbvkH0EptsYhbNBSUGaUBLKqxH1xSgsA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/types": "^7.26.7"
"@babel/types": "^7.26.10"
},
"bin": {
"parser": "bin/babel-parser.js"
@ -2160,9 +2160,9 @@
}
},
"node_modules/@babel/runtime": {
"version": "7.26.7",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.7.tgz",
"integrity": "sha512-AOPI3D+a8dXnja+iwsUqGRjr1BbZIe771sXdapOtYI531gSqpi92vXivKcq2asu/DFpdl1ceFAKZyRzK2PCVcQ==",
"version": "7.26.10",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.10.tgz",
"integrity": "sha512-2WJMeRQPHKSPemqk/awGrAiuFfzBmOIPXKizAsVhWH9YJqLZ0H+HS4c8loHGgW6utJ3E/ejXQUsiGaQy2NZ9Fw==",
"license": "MIT",
"dependencies": {
"regenerator-runtime": "^0.14.0"
@ -2172,15 +2172,15 @@
}
},
"node_modules/@babel/template": {
"version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz",
"integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==",
"version": "7.26.9",
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.26.9.tgz",
"integrity": "sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/code-frame": "^7.25.9",
"@babel/parser": "^7.25.9",
"@babel/types": "^7.25.9"
"@babel/code-frame": "^7.26.2",
"@babel/parser": "^7.26.9",
"@babel/types": "^7.26.9"
},
"engines": {
"node": ">=6.9.0"
@ -2206,9 +2206,9 @@
}
},
"node_modules/@babel/types": {
"version": "7.26.7",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.7.tgz",
"integrity": "sha512-t8kDRGrKXyp6+tjUh7hw2RLyclsW4TRoRvRHtSyAX9Bb5ldlFh+90YAYY6awRXrlB4G5G2izNeGySpATlFzmOg==",
"version": "7.26.10",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.10.tgz",
"integrity": "sha512-emqcG3vHrpxUKTrxcblR36dcrcoRDvKmnL/dCL6ZsHaShW80qxCAcNhzQZrpeM765VzEos+xOi4s+r4IXzTwdQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -6053,16 +6053,16 @@
}
},
"node_modules/@storybook/core": {
"version": "8.5.6",
"resolved": "https://registry.npmjs.org/@storybook/core/-/core-8.5.6.tgz",
"integrity": "sha512-ibgTGI3mcSsADABIQuhHWL8rxqF6CvooKIWpkZsB9kwNActS3OJzfCSAZDcgtvRkwaarPVjYX/sAOBzjqQNkXg==",
"version": "8.6.7",
"resolved": "https://registry.npmjs.org/@storybook/core/-/core-8.6.7.tgz",
"integrity": "sha512-FcvLFA+Qn3+D6LgQkk0MOXA5FBz8DGc0UZmZuVbIwIUV4MV4ywCMwtKdG0cyhtzQg0YNyfiIYWJr7lZ4jLLhYg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@storybook/csf": "0.1.12",
"@storybook/theming": "8.6.7",
"better-opn": "^3.0.2",
"browser-assert": "^1.2.1",
"esbuild": "^0.18.0 || ^0.19.0 || ^0.20.0 || ^0.21.0 || ^0.22.0 || ^0.23.0 || ^0.24.0",
"esbuild": "^0.18.0 || ^0.19.0 || ^0.20.0 || ^0.21.0 || ^0.22.0 || ^0.23.0 || ^0.24.0 || ^0.25.0",
"esbuild-register": "^3.5.0",
"jsdoc-type-pratt-parser": "^4.0.0",
"process": "^0.11.10",
@ -6098,6 +6098,20 @@
"storybook": "^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0"
}
},
"node_modules/@storybook/core/node_modules/@storybook/theming": {
"version": "8.6.7",
"resolved": "https://registry.npmjs.org/@storybook/theming/-/theming-8.6.7.tgz",
"integrity": "sha512-F/i4XS5bew9dvtNiHvDJF0mko1IUbPM9PUjTYPaw6cK8ytS0kdec703MsJ/GUA7seeEWBeGdZjV3ua0pys650A==",
"dev": true,
"license": "MIT",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/storybook"
},
"peerDependencies": {
"storybook": "^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0"
}
},
"node_modules/@storybook/core/node_modules/semver": {
"version": "7.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz",
@ -18989,9 +19003,9 @@
}
},
"node_modules/recast": {
"version": "0.23.9",
"resolved": "https://registry.npmjs.org/recast/-/recast-0.23.9.tgz",
"integrity": "sha512-Hx/BGIbwj+Des3+xy5uAtAbdCyqK9y9wbBcDFDYanLS9JnMqf7OeF87HQwUimE87OEc72mr6tkKUKMBBL+hF9Q==",
"version": "0.23.11",
"resolved": "https://registry.npmjs.org/recast/-/recast-0.23.11.tgz",
"integrity": "sha512-YTUo+Flmw4ZXiWfQKGcwwc11KnoRAYgzAE2E7mXKCjSviTKShtxBsN6YUUBB2gtaBzKzeKunxhUwNHQuRryhWA==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -20141,13 +20155,13 @@
}
},
"node_modules/storybook": {
"version": "8.5.6",
"resolved": "https://registry.npmjs.org/storybook/-/storybook-8.5.6.tgz",
"integrity": "sha512-mrcYAA5CP6QBrq5O9grz2eqBoEfJNsK3b+Iz+PdGYqpr04oMC7rg1h80murV2pRwsbHxIWBFpLpXAVX8tMK01w==",
"version": "8.6.7",
"resolved": "https://registry.npmjs.org/storybook/-/storybook-8.6.7.tgz",
"integrity": "sha512-9gktoFMQDSCINNGQH869d/sar9rVtAhr0HchcvDA6bssAqgQJvTphY4qC9lH54SxfTJm/7Sy+BKEngMK+dziJg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@storybook/core": "8.5.6"
"@storybook/core": "8.6.7"
},
"bin": {
"getstorybook": "bin/index.cjs",

View file

@ -272,7 +272,11 @@ export const ChatRowContent = ({
<>
<div style={headerStyle}>
{toolIcon(tool.tool === "appliedDiff" ? "diff" : "edit")}
<span style={{ fontWeight: "bold" }}>{t("chat:fileOperations.wantsToEdit")}</span>
<span style={{ fontWeight: "bold" }}>
{tool.isOutsideWorkspace
? t("chat:fileOperations.wantsToEditOutsideWorkspace")
: t("chat:fileOperations.wantsToEdit")}
</span>
</div>
<CodeAccordian
progressStatus={message.progressStatus}
@ -307,7 +311,9 @@ export const ChatRowContent = ({
{toolIcon("file-code")}
<span style={{ fontWeight: "bold" }}>
{message.type === "ask"
? t("chat:fileOperations.wantsToRead")
? tool.isOutsideWorkspace
? t("chat:fileOperations.wantsToReadOutsideWorkspace")
: t("chat:fileOperations.wantsToRead")
: t("chat:fileOperations.didRead")}
</span>
</div>
@ -359,6 +365,21 @@ export const ChatRowContent = ({
</div>
</>
)
case "fetchInstructions":
return (
<>
<div style={headerStyle}>
{toolIcon("file-code")}
<span style={{ fontWeight: "bold" }}>{t("chat:instructions.wantsToFetch")}</span>
</div>
<CodeAccordian
isLoading={message.partial}
code={tool.content!}
isExpanded={isExpanded}
onToggleExpand={onToggleExpand}
/>
</>
)
case "listFilesTopLevel":
return (
<>

View file

@ -25,6 +25,8 @@ import Thumbnails from "../common/Thumbnails"
import { MAX_IMAGES_PER_MESSAGE } from "./ChatView"
import ContextMenu from "./ContextMenu"
import { VolumeX } from "lucide-react"
import { IconButton } from "./IconButton"
import { cn } from "@/lib/utils"
interface ChatTextAreaProps {
inputValue: string
@ -113,7 +115,7 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
return () => window.removeEventListener("message", messageHandler)
}, [setInputValue, searchRequestId])
const [thumbnailsHeight, setThumbnailsHeight] = useState(0)
const [isDraggingOver, setIsDraggingOver] = useState(false)
const [textAreaBaseHeight, setTextAreaBaseHeight] = useState<number | undefined>(undefined)
const [showContextMenu, setShowContextMenu] = useState(false)
const [cursorPosition, setCursorPosition] = useState(0)
@ -547,14 +549,6 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
[shouldDisableImages, setSelectedImages, cursorPosition, setInputValue, inputValue, t],
)
const handleThumbnailsHeightChange = useCallback((height: number) => setThumbnailsHeight(height), [])
useEffect(() => {
if (selectedImages.length === 0) {
setThumbnailsHeight(0)
}
}, [selectedImages])
const handleMenuMouseDown = useCallback(() => {
setIsMouseDownOnMenu(true)
}, [])
@ -592,75 +586,51 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
[updateCursorPosition],
)
const [isTtsPlaying, setIsTtsPlaying] = useState(false)
const handleDrop = useCallback(
async (e: React.DragEvent<HTMLDivElement>) => {
e.preventDefault()
setIsDraggingOver(false)
useEvent("message", (event: MessageEvent) => {
const message: ExtensionMessage = event.data
const text = e.dataTransfer.getData("text")
if (text) {
// Split text on newlines to handle multiple files
const lines = text.split(/\r?\n/).filter((line) => line.trim() !== "")
if (message.type === "ttsStart") {
setIsTtsPlaying(true)
} else if (message.type === "ttsStop") {
setIsTtsPlaying(false)
}
})
if (lines.length > 0) {
// Process each line as a separate file path
let newValue = inputValue.slice(0, cursorPosition)
let totalLength = 0
return (
<div
className="chat-text-area"
style={{
opacity: textAreaDisabled ? 0.5 : 1,
position: "relative",
display: "flex",
flexDirection: "column",
gap: "8px",
backgroundColor: "var(--vscode-input-background)",
margin: "10px 15px",
padding: "8px",
outline: "none",
border: "1px solid",
borderColor: isFocused ? "var(--vscode-focusBorder)" : "transparent",
borderRadius: "2px",
}}
onDrop={async (e) => {
e.preventDefault()
const files = Array.from(e.dataTransfer.files)
const text = e.dataTransfer.getData("text")
// Using a standard for loop instead of forEach for potential performance gains.
for (let i = 0; i < lines.length; i++) {
const line = lines[i]
// Convert each path to a mention-friendly format
const mentionText = convertToMentionPath(line, cwd)
newValue += mentionText
totalLength += mentionText.length
if (text) {
// Split text on newlines to handle multiple files
const lines = text.split(/\r?\n/).filter((line) => line.trim() !== "")
if (lines.length > 0) {
// Process each line as a separate file path
let newValue = inputValue.slice(0, cursorPosition)
let totalLength = 0
lines.forEach((line, index) => {
// Convert each path to a mention-friendly format
const mentionText = convertToMentionPath(line, cwd)
newValue += mentionText
totalLength += mentionText.length
// Add space after each mention except the last one
if (index < lines.length - 1) {
newValue += " "
totalLength += 1
}
})
// Add space after the last mention and append the rest of the input
newValue += " " + inputValue.slice(cursorPosition)
totalLength += 1
setInputValue(newValue)
const newCursorPosition = cursorPosition + totalLength
setCursorPosition(newCursorPosition)
setIntendedCursorPosition(newCursorPosition)
// Add space after each mention except the last one
if (i < lines.length - 1) {
newValue += " "
totalLength += 1
}
}
return
// Add space after the last mention and append the rest of the input
newValue += " " + inputValue.slice(cursorPosition)
totalLength += 1
setInputValue(newValue)
const newCursorPosition = cursorPosition + totalLength
setCursorPosition(newCursorPosition)
setIntendedCursorPosition(newCursorPosition)
}
return
}
const files = Array.from(e.dataTransfer.files)
if (!textAreaDisabled && files.length > 0) {
const acceptedTypes = ["png", "jpeg", "webp"]
const imageFiles = files.filter((file) => {
const [type, subtype] = file.type.split("/")
@ -699,159 +669,256 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
console.warn(t("chat:noValidImages"))
}
}
}}
onDragOver={(e) => {
e.preventDefault()
}}>
{showContextMenu && (
<div ref={contextMenuContainerRef}>
<ContextMenu
onSelect={handleMentionSelect}
searchQuery={searchQuery}
onMouseDown={handleMenuMouseDown}
selectedIndex={selectedMenuIndex}
setSelectedIndex={setSelectedMenuIndex}
selectedType={selectedType}
queryItems={queryItems}
modes={getAllModes(customModes)}
loading={searchLoading}
dynamicSearchResults={fileSearchResults}
/>
</div>
)}
}
},
[
cursorPosition,
cwd,
inputValue,
setInputValue,
setCursorPosition,
setIntendedCursorPosition,
textAreaDisabled,
shouldDisableImages,
setSelectedImages,
t,
],
)
<div
style={{
position: "relative",
flex: "1 1 auto",
display: "flex",
flexDirection: "column-reverse",
minHeight: 0,
overflow: "hidden",
}}>
const [isTtsPlaying, setIsTtsPlaying] = useState(false)
useEvent("message", (event: MessageEvent) => {
const message: ExtensionMessage = event.data
if (message.type === "ttsStart") {
setIsTtsPlaying(true)
} else if (message.type === "ttsStop") {
setIsTtsPlaying(false)
}
})
const placeholderBottomText = `\n(${t("chat:addContext")}${shouldDisableImages ? `, ${t("chat:dragFiles")}` : `, ${t("chat:dragFilesImages")}`})`
return (
<div
className={cn(
"relative",
"flex",
"flex-col",
"gap-2",
"bg-editor-background",
"m-2 mt-1",
"p-1.5",
"outline-none",
"border",
"border-none",
"w-[calc(100%-16px)]",
"ml-auto",
"mr-auto",
"box-border",
)}>
<div className="relative">
<div
ref={highlightLayerRef}
style={{
position: "absolute",
inset: 0,
pointerEvents: "none",
whiteSpace: "pre-wrap",
wordWrap: "break-word",
color: "transparent",
overflow: "hidden",
fontFamily: "var(--vscode-font-family)",
fontSize: "var(--vscode-editor-font-size)",
lineHeight: "var(--vscode-editor-line-height)",
padding: "2px",
paddingRight: "8px",
marginBottom: thumbnailsHeight > 0 ? `${thumbnailsHeight + 16}px` : 0,
zIndex: 1,
}}
/>
<DynamicTextArea
ref={(el) => {
if (typeof ref === "function") {
ref(el)
} else if (ref) {
ref.current = el
className={cn("chat-text-area", "relative", "flex", "flex-col", "outline-none")}
onDrop={handleDrop}
onDragOver={(e) => {
//Only allowed to drop images/files on shift key pressed
if (!e.shiftKey) {
setIsDraggingOver(false)
return
}
textAreaRef.current = el
e.preventDefault()
setIsDraggingOver(true)
e.dataTransfer.dropEffect = "copy"
}}
value={inputValue}
disabled={textAreaDisabled}
onChange={(e) => {
handleInputChange(e)
updateHighlights()
}}
onFocus={() => setIsFocused(true)}
onKeyDown={handleKeyDown}
onKeyUp={handleKeyUp}
onBlur={handleBlur}
onPaste={handlePaste}
onSelect={updateCursorPosition}
onMouseUp={updateCursorPosition}
onHeightChange={(height) => {
if (textAreaBaseHeight === undefined || height < textAreaBaseHeight) {
setTextAreaBaseHeight(height)
onDragLeave={(e) => {
e.preventDefault()
const rect = e.currentTarget.getBoundingClientRect()
if (
e.clientX <= rect.left ||
e.clientX >= rect.right ||
e.clientY <= rect.top ||
e.clientY >= rect.bottom
) {
setIsDraggingOver(false)
}
onHeightChange?.(height)
}}
placeholder={placeholderText}
minRows={3}
maxRows={15}
autoFocus={true}
style={{
width: "100%",
outline: "none",
boxSizing: "border-box",
backgroundColor: "transparent",
color: "var(--vscode-input-foreground)",
borderRadius: 2,
fontFamily: "var(--vscode-font-family)",
fontSize: "var(--vscode-editor-font-size)",
lineHeight: "var(--vscode-editor-line-height)",
resize: "none",
overflowX: "hidden",
overflowY: "auto",
border: "none",
padding: "2px",
paddingRight: "8px",
marginBottom: thumbnailsHeight > 0 ? `${thumbnailsHeight + 16}px` : 0,
cursor: textAreaDisabled ? "not-allowed" : undefined,
flex: "0 1 auto",
zIndex: 2,
scrollbarWidth: "none",
}}
onScroll={() => updateHighlights()}
/>
{isTtsPlaying && (
<Button
variant="ghost"
size="icon"
className="absolute top-0 right-0 opacity-25 hover:opacity-100 z-10"
onClick={() => vscode.postMessage({ type: "stopTts" })}>
<VolumeX className="size-4" />
</Button>
)}
}}>
{showContextMenu && (
<div
ref={contextMenuContainerRef}
className={cn(
"absolute",
"bottom-full",
"left-0",
"right-0",
"z-[1000]",
"mb-2",
"filter",
"drop-shadow-md",
)}>
<ContextMenu
onSelect={handleMentionSelect}
searchQuery={searchQuery}
onMouseDown={handleMenuMouseDown}
selectedIndex={selectedMenuIndex}
setSelectedIndex={setSelectedMenuIndex}
selectedType={selectedType}
queryItems={queryItems}
modes={getAllModes(customModes)}
loading={searchLoading}
dynamicSearchResults={fileSearchResults}
/>
</div>
)}
<div
className={cn(
"relative",
"flex-1",
"flex",
"flex-col-reverse",
"min-h-0",
"overflow-hidden",
"rounded",
)}>
<div
ref={highlightLayerRef}
className={cn(
"absolute",
"inset-0",
"pointer-events-none",
"whitespace-pre-wrap",
"break-words",
"text-transparent",
"overflow-hidden",
"font-vscode-font-family",
"text-vscode-editor-font-size",
"leading-vscode-editor-line-height",
"py-2",
"px-[9px]",
"z-[1000]",
)}
style={{
color: "transparent",
}}
/>
<DynamicTextArea
ref={(el) => {
if (typeof ref === "function") {
ref(el)
} else if (ref) {
ref.current = el
}
textAreaRef.current = el
}}
value={inputValue}
disabled={textAreaDisabled}
onChange={(e) => {
handleInputChange(e)
updateHighlights()
}}
onFocus={() => setIsFocused(true)}
onKeyDown={handleKeyDown}
onKeyUp={handleKeyUp}
onBlur={handleBlur}
onPaste={handlePaste}
onSelect={updateCursorPosition}
onMouseUp={updateCursorPosition}
onHeightChange={(height) => {
if (textAreaBaseHeight === undefined || height < textAreaBaseHeight) {
setTextAreaBaseHeight(height)
}
onHeightChange?.(height)
}}
placeholder={placeholderText}
minRows={3}
maxRows={15}
autoFocus={true}
className={cn(
"w-full",
"text-vscode-input-foreground",
"font-vscode-font-family",
"text-vscode-editor-font-size",
"leading-vscode-editor-line-height",
textAreaDisabled ? "cursor-not-allowed" : "cursor-text",
"py-1.5 px-2",
isFocused
? "border border-vscode-focusBorder outline outline-vscode-focusBorder"
: isDraggingOver
? "border-2 border-dashed border-vscode-focusBorder"
: "border border-transparent",
textAreaDisabled ? "opacity-50" : "opacity-100",
isDraggingOver
? "bg-[color-mix(in_srgb,var(--vscode-input-background)_95%,var(--vscode-focusBorder))]"
: "bg-vscode-input-background",
"transition-background-color duration-150 ease-in-out",
"will-change-background-color",
"h-[100px]",
"[@media(min-width:150px)]:min-h-[80px]",
"[@media(min-width:425px)]:min-h-[60px]",
"box-border",
"rounded",
"resize-none",
"overflow-x-hidden",
"overflow-y-auto",
"pr-2",
"flex-none flex-grow",
"z-[2]",
"scrollbar-none",
)}
onScroll={() => updateHighlights()}
/>
{isTtsPlaying && (
<Button
variant="ghost"
size="icon"
className="absolute top-0 right-0 opacity-25 hover:opacity-100 z-10"
onClick={() => vscode.postMessage({ type: "stopTts" })}>
<VolumeX className="size-4" />
</Button>
)}
{!inputValue && (
<div
className={cn(
"absolute",
"left-2",
"flex",
"gap-2",
"text-xs",
"text-descriptionForeground",
"pointer-events-none",
"z-25",
"bottom-1.5",
"pr-2",
"transition-opacity",
"duration-200",
"ease-in-out",
textAreaDisabled ? "opacity-35" : "opacity-70",
)}>
{placeholderBottomText}
</div>
)}
</div>
</div>
</div>
{selectedImages.length > 0 && (
<Thumbnails
images={selectedImages}
setImages={setSelectedImages}
onHeightChange={handleThumbnailsHeightChange}
style={{
position: "absolute",
bottom: "36px",
left: "16px",
zIndex: 2,
marginBottom: "4px",
marginBottom: 0,
}}
/>
)}
<div
style={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
marginTop: "auto",
paddingTop: "2px",
}}>
{/* Left side - dropdowns container */}
<div
style={{
display: "flex",
alignItems: "center",
gap: "4px",
overflow: "hidden",
minWidth: 0,
}}>
<div className={cn("flex", "justify-between", "items-center", "mt-auto", "pt-0.5")}>
<div className={cn("flex", "items-center", "gap-1", "min-w-0")}>
{/* Mode selector - fixed width */}
<div style={{ flexShrink: 0 }}>
<div className="shrink-0">
<SelectDropdown
value={mode}
disabled={textAreaDisabled}
title={t("chat:selectMode")}
options={[
{
@ -886,12 +953,7 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
</div>
{/* API configuration selector - flexible width */}
<div
style={{
flex: "1 1 auto",
minWidth: 0,
overflow: "hidden",
}}>
<div className={cn("flex-1", "min-w-0", "overflow-hidden")}>
<SelectDropdown
value={currentApiConfigName || ""}
disabled={textAreaDisabled}
@ -921,51 +983,25 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
</div>
{/* Right side - action buttons */}
<div
style={{
display: "flex",
alignItems: "center",
gap: "8px",
flexShrink: 0,
}}>
<div style={{ display: "flex", alignItems: "center" }}>
{isEnhancingPrompt ? (
<span
className="codicon codicon-loading codicon-modifier-spin"
style={{
color: "var(--vscode-input-foreground)",
opacity: 0.5,
fontSize: 16.5,
marginRight: 6,
}}
/>
) : (
<span
role="button"
aria-label="enhance prompt"
data-testid="enhance-prompt-button"
title={t("chat:enhancePrompt")}
className={`input-icon-button ${
textAreaDisabled ? "disabled" : ""
} codicon codicon-sparkle`}
onClick={() => !textAreaDisabled && handleEnhancePrompt()}
style={{ fontSize: 16.5 }}
/>
)}
</div>
<span
className={`input-icon-button ${
shouldDisableImages ? "disabled" : ""
} codicon codicon-device-camera`}
title={t("chat:addImages")}
onClick={() => !shouldDisableImages && onSelectImages()}
style={{ fontSize: 16.5 }}
<div className={cn("flex", "items-center", "gap-0.5", "shrink-0")}>
<IconButton
iconClass={isEnhancingPrompt ? "codicon-loading" : "codicon-sparkle"}
title={t("chat:enhancePrompt")}
disabled={textAreaDisabled}
isLoading={isEnhancingPrompt}
onClick={handleEnhancePrompt}
/>
<span
className={`input-icon-button ${textAreaDisabled ? "disabled" : ""} codicon codicon-send`}
<IconButton
iconClass="codicon-device-camera"
title={t("chat:addImages")}
disabled={shouldDisableImages}
onClick={onSelectImages}
/>
<IconButton
iconClass="codicon-send"
title={t("chat:sendMessage")}
onClick={() => !textAreaDisabled && onSend()}
style={{ fontSize: 15 }}
disabled={textAreaDisabled}
onClick={onSend}
/>
</div>
</div>

View file

@ -55,7 +55,9 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie
mcpServers,
alwaysAllowBrowser,
alwaysAllowReadOnly,
alwaysAllowReadOnlyOutsideWorkspace,
alwaysAllowWrite,
alwaysAllowWriteOutsideWorkspace,
alwaysAllowExecute,
alwaysAllowMcp,
allowedCommands,
@ -649,26 +651,69 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie
(message: ClineMessage | undefined) => {
if (!autoApprovalEnabled || !message || message.type !== "ask") return false
return (
(alwaysAllowBrowser && message.ask === "browser_action_launch") ||
(alwaysAllowReadOnly && message.ask === "tool" && isReadOnlyToolAction(message)) ||
(alwaysAllowWrite && message.ask === "tool" && isWriteToolAction(message)) ||
(alwaysAllowExecute && message.ask === "command" && isAllowedCommand(message)) ||
(alwaysAllowMcp && message.ask === "use_mcp_server" && isMcpToolAlwaysAllowed(message)) ||
(alwaysAllowModeSwitch &&
message.ask === "tool" &&
JSON.parse(message.text || "{}")?.tool === "switchMode") ||
(alwaysAllowSubtasks &&
message.ask === "tool" &&
["newTask", "finishTask"].includes(JSON.parse(message.text || "{}")?.tool))
)
if (message.ask === "browser_action_launch") {
return alwaysAllowBrowser
}
if (message.ask === "use_mcp_server") {
return alwaysAllowMcp && isMcpToolAlwaysAllowed(message)
}
if (message.ask === "command") {
return alwaysAllowExecute && isAllowedCommand(message)
}
// For read/write operations, check if it's outside workspace and if we have permission for that
if (message.ask === "tool") {
let tool: any = {}
try {
tool = JSON.parse(message.text || "{}")
} catch (error) {
console.error("Failed to parse tool:", error)
}
if (!tool) {
return false
}
if (tool?.tool === "fetchInstructions") {
if (tool.content === "create_mode") {
return alwaysAllowModeSwitch
}
if (tool.content === "create_mcp_server") {
return alwaysAllowMcp
}
}
if (tool?.tool === "switchMode") {
return alwaysAllowModeSwitch
}
if (["newTask", "finishTask"].includes(tool?.tool)) {
return alwaysAllowSubtasks
}
const isOutsideWorkspace = !!tool.isOutsideWorkspace
if (isReadOnlyToolAction(message)) {
return alwaysAllowReadOnly && (!isOutsideWorkspace || alwaysAllowReadOnlyOutsideWorkspace)
}
if (isWriteToolAction(message)) {
return alwaysAllowWrite && (!isOutsideWorkspace || alwaysAllowWriteOutsideWorkspace)
}
}
return false
},
[
autoApprovalEnabled,
alwaysAllowBrowser,
alwaysAllowReadOnly,
alwaysAllowReadOnlyOutsideWorkspace,
isReadOnlyToolAction,
alwaysAllowWrite,
alwaysAllowWriteOutsideWorkspace,
isWriteToolAction,
alwaysAllowExecute,
isAllowedCommand,
@ -974,10 +1019,7 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie
[],
)
const baseText = task ? t("chat:typeMessage") : t("chat:typeTask")
const placeholderText =
baseText +
`\n(${t("chat:addContext")}${shouldDisableImages ? `, ${t("chat:dragFiles")}` : `, ${t("chat:dragFilesImages")}`})`
const placeholderText = task ? t("chat:typeMessage") : t("chat:typeTask")
const itemContent = useCallback(
(index: number, messageOrGroup: ClineMessage | ClineMessage[]) => {
@ -1050,7 +1092,9 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie
handlePrimaryButtonClick,
alwaysAllowBrowser,
alwaysAllowReadOnly,
alwaysAllowReadOnlyOutsideWorkspace,
alwaysAllowWrite,
alwaysAllowWriteOutsideWorkspace,
alwaysAllowExecute,
alwaysAllowMcp,
messages,

View file

@ -0,0 +1,48 @@
import { cn } from "@/lib/utils"
interface IconButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
iconClass: string
title: string
disabled?: boolean
isLoading?: boolean
style?: React.CSSProperties
}
export const IconButton: React.FC<IconButtonProps> = ({
iconClass,
title,
className,
disabled,
isLoading,
onClick,
style,
...props
}) => {
const buttonClasses = cn(
"relative inline-flex items-center justify-center",
"bg-transparent border-none p-1.5",
"rounded-md min-w-[28px] min-h-[28px]",
"text-vscode-foreground opacity-85",
"transition-all duration-150",
"hover:opacity-100 hover:bg-[rgba(255,255,255,0.03)] hover:border-[rgba(255,255,255,0.15)]",
"focus:outline-none focus-visible:ring-1 focus-visible:ring-vscode-focusBorder",
"active:bg-[rgba(255,255,255,0.1)]",
disabled &&
"opacity-40 cursor-not-allowed grayscale-[30%] hover:bg-transparent hover:border-[rgba(255,255,255,0.08)] active:bg-transparent",
className,
)
const iconClasses = cn("codicon", iconClass, isLoading && "codicon-modifier-spin")
return (
<button
aria-label={title}
title={title}
className={buttonClasses}
onClick={!disabled ? onClick : undefined}
style={{ fontSize: 16.5, ...style }}
{...props}>
<span className={iconClasses} />
</button>
)
}

View file

@ -31,6 +31,16 @@ const mockConvertToMentionPath = pathMentions.convertToMentionPath as jest.Mock
// Mock ExtensionStateContext
jest.mock("../../../context/ExtensionStateContext")
// Custom query function to get the enhance prompt button
const getEnhancePromptButton = () => {
return screen.getByRole("button", {
name: (_, element) => {
// Find the button with the sparkle icon
return element.querySelector(".codicon-sparkle") !== null
},
})
}
describe("ChatTextArea", () => {
const defaultProps = {
inputValue: "",
@ -66,10 +76,9 @@ describe("ChatTextArea", () => {
filePaths: [],
openedTabs: [],
})
render(<ChatTextArea {...defaultProps} textAreaDisabled={true} />)
const enhanceButton = screen.getByRole("button", { name: /enhance prompt/i })
expect(enhanceButton).toHaveClass("disabled")
const enhanceButton = getEnhancePromptButton()
expect(enhanceButton).toHaveClass("cursor-not-allowed")
})
})
@ -88,7 +97,7 @@ describe("ChatTextArea", () => {
render(<ChatTextArea {...defaultProps} inputValue="Test prompt" />)
const enhanceButton = screen.getByRole("button", { name: /enhance prompt/i })
const enhanceButton = getEnhancePromptButton()
fireEvent.click(enhanceButton)
expect(mockPostMessage).toHaveBeenCalledWith({
@ -108,7 +117,7 @@ describe("ChatTextArea", () => {
render(<ChatTextArea {...defaultProps} inputValue="" />)
const enhanceButton = screen.getByRole("button", { name: /enhance prompt/i })
const enhanceButton = getEnhancePromptButton()
fireEvent.click(enhanceButton)
expect(mockPostMessage).not.toHaveBeenCalled()
@ -125,7 +134,7 @@ describe("ChatTextArea", () => {
render(<ChatTextArea {...defaultProps} inputValue="Test prompt" />)
const enhanceButton = screen.getByRole("button", { name: /enhance prompt/i })
const enhanceButton = getEnhancePromptButton()
fireEvent.click(enhanceButton)
const loadingSpinner = screen.getByText("", { selector: ".codicon-loading" })
@ -150,7 +159,7 @@ describe("ChatTextArea", () => {
rerender(<ChatTextArea {...defaultProps} />)
// Verify the enhance button appears after apiConfiguration changes
expect(screen.getByRole("button", { name: /enhance prompt/i })).toBeInTheDocument()
expect(getEnhancePromptButton()).toBeInTheDocument()
})
})

View file

@ -146,6 +146,156 @@ describe("ChatView - Auto Approval Tests", () => {
})
})
it("auto-approves outside workspace read operations when enabled", async () => {
render(
<ExtensionStateContextProvider>
<ChatView
isHidden={false}
showAnnouncement={false}
hideAnnouncement={() => {}}
showHistoryView={() => {}}
/>
</ExtensionStateContextProvider>,
)
// First hydrate state with initial task
mockPostMessage({
alwaysAllowReadOnly: true,
alwaysAllowReadOnlyOutsideWorkspace: true,
autoApprovalEnabled: true,
clineMessages: [
{
type: "say",
say: "task",
ts: Date.now() - 2000,
text: "Initial task",
},
],
})
// Then send the read tool ask message with an absolute path (outside workspace)
mockPostMessage({
alwaysAllowReadOnly: true,
alwaysAllowReadOnlyOutsideWorkspace: true,
autoApprovalEnabled: true,
clineMessages: [
{
type: "say",
say: "task",
ts: Date.now() - 2000,
text: "Initial task",
},
{
type: "ask",
ask: "tool",
ts: Date.now(),
text: JSON.stringify({
tool: "readFile",
path: "/absolute/path/test.txt",
// Use an absolute path that's clearly outside workspace
}),
partial: false,
},
],
})
// Also mock the filePaths for workspace detection
mockPostMessage({
alwaysAllowReadOnly: true,
alwaysAllowReadOnlyOutsideWorkspace: true,
autoApprovalEnabled: true,
filePaths: ["/workspace/root", "/another/workspace"],
clineMessages: [
{
type: "say",
say: "task",
ts: Date.now() - 2000,
text: "Initial task",
},
{
type: "ask",
ask: "tool",
ts: Date.now(),
text: JSON.stringify({
tool: "readFile",
path: "/absolute/path/test.txt",
}),
partial: false,
},
],
})
// Wait for the auto-approval message
await waitFor(() => {
expect(vscode.postMessage).toHaveBeenCalledWith({
type: "askResponse",
askResponse: "yesButtonClicked",
})
})
})
it("does not auto-approve outside workspace read operations without permission", async () => {
render(
<ExtensionStateContextProvider>
<ChatView
isHidden={false}
showAnnouncement={false}
hideAnnouncement={() => {}}
showHistoryView={() => {}}
/>
</ExtensionStateContextProvider>,
)
// First hydrate state with initial task
mockPostMessage({
alwaysAllowReadOnly: true,
alwaysAllowReadOnlyOutsideWorkspace: false, // No permission for outside workspace
autoApprovalEnabled: true,
filePaths: ["/workspace/root", "/another/workspace"], // Same workspace paths as before
clineMessages: [
{
type: "say",
say: "task",
ts: Date.now() - 2000,
text: "Initial task",
},
],
})
// Then send the read tool ask message with an absolute path (outside workspace)
mockPostMessage({
alwaysAllowReadOnly: true,
alwaysAllowReadOnlyOutsideWorkspace: false,
autoApprovalEnabled: true,
clineMessages: [
{
type: "say",
say: "task",
ts: Date.now() - 2000,
text: "Initial task",
},
{
type: "ask",
ask: "tool",
ts: Date.now(),
text: JSON.stringify({
tool: "readFile",
path: "/absolute/path/test.txt",
isOutsideWorkspace: true, // Explicitly indicate this is outside workspace
}),
partial: false,
},
],
})
// Wait a short time and verify no auto-approval message was sent
await new Promise((resolve) => setTimeout(resolve, 100))
expect(vscode.postMessage).not.toHaveBeenCalledWith({
type: "askResponse",
askResponse: "yesButtonClicked",
})
})
it("does not auto-approve when autoApprovalEnabled is false", async () => {
render(
<ExtensionStateContextProvider>
@ -258,6 +408,136 @@ describe("ChatView - Auto Approval Tests", () => {
})
})
it("auto-approves outside workspace write operations when enabled", async () => {
render(
<ExtensionStateContextProvider>
<ChatView
isHidden={false}
showAnnouncement={false}
hideAnnouncement={() => {}}
showHistoryView={() => {}}
/>
</ExtensionStateContextProvider>,
)
// First hydrate state with initial task
mockPostMessage({
alwaysAllowWrite: true,
alwaysAllowWriteOutsideWorkspace: true,
autoApprovalEnabled: true,
writeDelayMs: 0, // Set to 0 for testing
filePaths: ["/workspace/root", "/another/workspace"], // Define workspace paths for testing
clineMessages: [
{
type: "say",
say: "task",
ts: Date.now() - 2000,
text: "Initial task",
},
],
})
// Then send the write tool ask message with an absolute path (outside workspace)
mockPostMessage({
alwaysAllowWrite: true,
alwaysAllowWriteOutsideWorkspace: true,
autoApprovalEnabled: true,
writeDelayMs: 0,
clineMessages: [
{
type: "say",
say: "task",
ts: Date.now() - 2000,
text: "Initial task",
},
{
type: "ask",
ask: "tool",
ts: Date.now(),
text: JSON.stringify({
tool: "editedExistingFile",
path: "/absolute/path/test.txt",
content: "Test content",
}),
partial: false,
},
],
})
// Wait for the auto-approval message
await waitFor(() => {
expect(vscode.postMessage).toHaveBeenCalledWith({
type: "askResponse",
askResponse: "yesButtonClicked",
})
})
})
it("does not auto-approve outside workspace write operations without permission", async () => {
render(
<ExtensionStateContextProvider>
<ChatView
isHidden={false}
showAnnouncement={false}
hideAnnouncement={() => {}}
showHistoryView={() => {}}
/>
</ExtensionStateContextProvider>,
)
// First hydrate state with initial task
mockPostMessage({
alwaysAllowWrite: true,
alwaysAllowWriteOutsideWorkspace: false, // No permission for outside workspace
autoApprovalEnabled: true,
writeDelayMs: 0,
filePaths: ["/workspace/root", "/another/workspace"], // Define workspace paths for testing
clineMessages: [
{
type: "say",
say: "task",
ts: Date.now() - 2000,
text: "Initial task",
},
],
})
// Then send the write tool ask message with an absolute path (outside workspace)
mockPostMessage({
alwaysAllowWrite: true,
alwaysAllowWriteOutsideWorkspace: false,
autoApprovalEnabled: true,
writeDelayMs: 0,
clineMessages: [
{
type: "say",
say: "task",
ts: Date.now() - 2000,
text: "Initial task",
},
{
type: "ask",
ask: "tool",
ts: Date.now(),
text: JSON.stringify({
tool: "editedExistingFile",
path: "/absolute/path/test.txt",
content: "Test content",
isOutsideWorkspace: true, // Explicitly indicate this is outside workspace
}),
partial: false,
},
],
})
// Wait a short time and verify no auto-approval message was sent
await new Promise((resolve) => setTimeout(resolve, 100))
expect(vscode.postMessage).not.toHaveBeenCalledWith({
type: "askResponse",
askResponse: "yesButtonClicked",
})
})
it("auto-approves browser actions when enabled", async () => {
render(
<ExtensionStateContextProvider>

View file

@ -57,8 +57,6 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
customInstructions,
setCustomInstructions,
customModes,
enableCustomModeCreation,
setEnableCustomModeCreation,
} = useExtensionState()
// Memoize modes to preserve array order
@ -326,17 +324,6 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
return () => document.removeEventListener("click", handleClickOutside)
}, [showConfigMenu])
// Add effect to sync enableCustomModeCreation with backend
useEffect(() => {
if (enableCustomModeCreation !== undefined) {
// Send the value to the extension's global state
vscode.postMessage({
type: "enableCustomModeCreation", // Using dedicated message type
bool: enableCustomModeCreation,
})
}
}, [enableCustomModeCreation])
useEffect(() => {
const handler = (event: MessageEvent) => {
const message = event.data
@ -462,8 +449,6 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
values: {
create: true,
content: JSON.stringify({ customModes: [] }, null, 2),
searchParents: true,
startFromWorkspace: true,
},
})
setShowConfigMenu(false)
@ -810,8 +795,6 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
values: {
create: true,
content: "",
searchParents: true,
startFromWorkspace: true,
},
})
}}
@ -861,32 +844,6 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
</VSCodeButton>
</div>
{/*
NOTE: This setting is placed in PromptsView rather than SettingsView since it
directly affects the functionality related to modes and custom mode creation,
which are managed in this component. This is an intentional deviation from
the standard pattern described in cline_docs/settings.md.
*/}
<div className="mt-12">
<VSCodeCheckbox
checked={enableCustomModeCreation ?? true}
onChange={(e: any) => {
// Just update the local state through React context
// The React context will update the global state
setEnableCustomModeCreation(e.target.checked)
}}>
<span style={{ fontWeight: "500" }}>{t("prompts:customModeCreation.enableTitle")}</span>
</VSCodeCheckbox>
<p
style={{
fontSize: "12px",
marginTop: "5px",
color: "var(--vscode-descriptionForeground)",
}}>
{t("prompts:customModeCreation.description")}
</p>
</div>
{/* Custom System Prompt Disclosure */}
<div className="mt-12">
<button
@ -919,8 +876,6 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
values: {
create: true,
content: "",
searchParents: true,
startFromWorkspace: true,
},
})
}}
@ -976,8 +931,6 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
values: {
create: true,
content: "",
searchParents: true,
startFromWorkspace: true,
},
})
}

View file

@ -46,7 +46,7 @@ import {
useOpenRouterModelProviders,
OPENROUTER_DEFAULT_PROVIDER_NAME,
} from "@/components/ui/hooks/useOpenRouterModelProviders"
import { useOpenRouterKeyInfo } from "@/components/ui/hooks/useOpenRouterKeyInfo"
import { MODELS_BY_PROVIDER, PROVIDERS, AWS_REGIONS, VERTEX_REGIONS } from "./constants"
import { VSCodeButtonLink } from "../common/VSCodeButtonLink"
import { ModelInfoView } from "./ModelInfoView"
@ -57,6 +57,23 @@ import { ApiErrorMessage } from "./ApiErrorMessage"
import { ThinkingBudget } from "./ThinkingBudget"
import { R1FormatSetting } from "./R1FormatSetting"
// Component to display OpenRouter API key balance
const OpenRouterBalanceDisplay = ({ apiKey, baseUrl }: { apiKey: string; baseUrl?: string }) => {
const { data: keyInfo } = useOpenRouterKeyInfo(apiKey, baseUrl)
if (!keyInfo || !keyInfo.limit) {
return null
}
const formattedBalance = (keyInfo.limit - keyInfo.usage).toFixed(2)
return (
<VSCodeLink href="https://openrouter.ai/settings/keys" className="text-vscode-foreground hover:underline">
${formattedBalance}
</VSCodeLink>
)
}
interface ApiOptionsProps {
uriScheme: string | undefined
apiConfiguration: ApiConfiguration
@ -274,7 +291,15 @@ const ApiOptions = ({
onInput={handleInputChange("openRouterApiKey")}
placeholder={t("settings:placeholders.apiKey")}
className="w-full">
<label className="block font-medium mb-1">{t("settings:providers.openRouterApiKey")}</label>
<div className="flex justify-between items-center mb-1">
<label className="block font-medium">{t("settings:providers.openRouterApiKey")}</label>
{apiConfiguration?.openRouterApiKey && (
<OpenRouterBalanceDisplay
apiKey={apiConfiguration.openRouterApiKey}
baseUrl={apiConfiguration.openRouterBaseUrl}
/>
)}
</div>
</VSCodeTextField>
<div className="text-sm text-vscode-descriptionForeground -mt-2">
{t("settings:providers.apiKeyStorageNotice")}

View file

@ -12,7 +12,9 @@ import { Section } from "./Section"
type AutoApproveSettingsProps = HTMLAttributes<HTMLDivElement> & {
alwaysAllowReadOnly?: boolean
alwaysAllowReadOnlyOutsideWorkspace?: boolean
alwaysAllowWrite?: boolean
alwaysAllowWriteOutsideWorkspace?: boolean
writeDelayMs: number
alwaysAllowBrowser?: boolean
alwaysApproveResubmit?: boolean
@ -24,7 +26,9 @@ type AutoApproveSettingsProps = HTMLAttributes<HTMLDivElement> & {
allowedCommands?: string[]
setCachedStateField: SetCachedStateField<
| "alwaysAllowReadOnly"
| "alwaysAllowReadOnlyOutsideWorkspace"
| "alwaysAllowWrite"
| "alwaysAllowWriteOutsideWorkspace"
| "writeDelayMs"
| "alwaysAllowBrowser"
| "alwaysApproveResubmit"
@ -39,7 +43,9 @@ type AutoApproveSettingsProps = HTMLAttributes<HTMLDivElement> & {
export const AutoApproveSettings = ({
alwaysAllowReadOnly,
alwaysAllowReadOnlyOutsideWorkspace,
alwaysAllowWrite,
alwaysAllowWriteOutsideWorkspace,
writeDelayMs,
alwaysAllowBrowser,
alwaysApproveResubmit,
@ -88,6 +94,26 @@ export const AutoApproveSettings = ({
</div>
</div>
{alwaysAllowReadOnly && (
<div className="flex flex-col gap-3 pl-3 border-l-2 border-vscode-button-background">
<div>
<VSCodeCheckbox
checked={alwaysAllowReadOnlyOutsideWorkspace}
onChange={(e: any) =>
setCachedStateField("alwaysAllowReadOnlyOutsideWorkspace", e.target.checked)
}
data-testid="always-allow-readonly-outside-workspace-checkbox">
<span className="font-medium">
{t("settings:autoApprove.readOnly.outsideWorkspace.label")}
</span>
</VSCodeCheckbox>
<div className="text-vscode-descriptionForeground text-sm mt-1">
{t("settings:autoApprove.readOnly.outsideWorkspace.description")}
</div>
</div>
</div>
)}
<div>
<VSCodeCheckbox
checked={alwaysAllowWrite}
@ -102,6 +128,21 @@ export const AutoApproveSettings = ({
{alwaysAllowWrite && (
<div className="flex flex-col gap-3 pl-3 border-l-2 border-vscode-button-background">
<div>
<VSCodeCheckbox
checked={alwaysAllowWriteOutsideWorkspace}
onChange={(e: any) =>
setCachedStateField("alwaysAllowWriteOutsideWorkspace", e.target.checked)
}
data-testid="always-allow-write-outside-workspace-checkbox">
<span className="font-medium">
{t("settings:autoApprove.write.outsideWorkspace.label")}
</span>
</VSCodeCheckbox>
<div className="text-vscode-descriptionForeground text-sm mt-1 mb-3">
{t("settings:autoApprove.write.outsideWorkspace.description")}
</div>
</div>
<div>
<div className="flex items-center gap-2">
<Slider

View file

@ -98,6 +98,7 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone },
const {
alwaysAllowReadOnly,
alwaysAllowReadOnlyOutsideWorkspace,
allowedCommands,
language,
alwaysAllowBrowser,
@ -106,6 +107,7 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone },
alwaysAllowModeSwitch,
alwaysAllowSubtasks,
alwaysAllowWrite,
alwaysAllowWriteOutsideWorkspace,
alwaysApproveResubmit,
browserToolEnabled,
browserViewportSize,
@ -209,7 +211,12 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone },
if (isSettingValid) {
vscode.postMessage({ type: "language", text: language })
vscode.postMessage({ type: "alwaysAllowReadOnly", bool: alwaysAllowReadOnly })
vscode.postMessage({
type: "alwaysAllowReadOnlyOutsideWorkspace",
bool: alwaysAllowReadOnlyOutsideWorkspace,
})
vscode.postMessage({ type: "alwaysAllowWrite", bool: alwaysAllowWrite })
vscode.postMessage({ type: "alwaysAllowWriteOutsideWorkspace", bool: alwaysAllowWriteOutsideWorkspace })
vscode.postMessage({ type: "alwaysAllowExecute", bool: alwaysAllowExecute })
vscode.postMessage({ type: "alwaysAllowBrowser", bool: alwaysAllowBrowser })
vscode.postMessage({ type: "alwaysAllowMcp", bool: alwaysAllowMcp })
@ -405,7 +412,9 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone },
<div ref={autoApproveRef}>
<AutoApproveSettings
alwaysAllowReadOnly={alwaysAllowReadOnly}
alwaysAllowReadOnlyOutsideWorkspace={alwaysAllowReadOnlyOutsideWorkspace}
alwaysAllowWrite={alwaysAllowWrite}
alwaysAllowWriteOutsideWorkspace={alwaysAllowWriteOutsideWorkspace}
writeDelayMs={writeDelayMs}
alwaysAllowBrowser={alwaysAllowBrowser}
alwaysApproveResubmit={alwaysApproveResubmit}

View file

@ -0,0 +1,59 @@
import axios from "axios"
import { z } from "zod"
import { useQuery, UseQueryOptions } from "@tanstack/react-query"
// Define schema for OpenRouter key response
const openRouterKeyInfoSchema = z.object({
data: z.object({
label: z.string(),
usage: z.number(),
is_free_tier: z.boolean(),
is_provisioning_key: z.boolean(),
rate_limit: z.object({
requests: z.number(),
interval: z.string(),
}),
limit: z.number().nullable(),
}),
})
export type OpenRouterKeyInfo = z.infer<typeof openRouterKeyInfoSchema>["data"]
async function getOpenRouterKeyInfo(apiKey?: string, baseUrl?: string) {
if (!apiKey) return null
try {
// Use the provided base URL or default to OpenRouter's API URL
const apiBaseUrl = baseUrl || "https://openrouter.ai/api/v1"
const keyEndpoint = `${apiBaseUrl}/key`
const response = await axios.get(keyEndpoint, {
headers: {
Authorization: `Bearer ${apiKey}`,
},
})
const result = openRouterKeyInfoSchema.safeParse(response.data)
if (!result.success) {
console.error("OpenRouter API key info validation failed:", result.error)
return null
}
return result.data.data
} catch (error) {
console.error("Error fetching OpenRouter key info:", error)
return null
}
}
type UseOpenRouterKeyInfoOptions = Omit<UseQueryOptions<OpenRouterKeyInfo | null>, "queryKey" | "queryFn">
export const useOpenRouterKeyInfo = (apiKey?: string, baseUrl?: string, options?: UseOpenRouterKeyInfoOptions) => {
return useQuery<OpenRouterKeyInfo | null>({
queryKey: ["openrouter-key-info", apiKey, baseUrl],
queryFn: () => getOpenRouterKeyInfo(apiKey, baseUrl),
staleTime: 30 * 1000, // 30 seconds
enabled: !!apiKey,
...options,
})
}

View file

@ -82,9 +82,12 @@ export const SelectDropdown = React.forwardRef<React.ElementRef<typeof DropdownM
disabled={disabled}
title={title}
className={cn(
"w-full min-w-0 max-w-full inline-flex items-center gap-1 relative whitespace-nowrap pr-1.5 py-1.5 text-xs outline-none",
"bg-transparent border-none text-vscode-foreground w-auto",
disabled ? "opacity-50 cursor-not-allowed" : "opacity-80 hover:opacity-100 cursor-pointer",
"w-full min-w-0 max-w-full inline-flex items-center gap-1.5 relative whitespace-nowrap px-1.5 py-1 text-xs",
"bg-transparent border border-[rgba(255,255,255,0.08)] rounded-md text-vscode-foreground w-auto",
"transition-all duration-150 focus:outline-none focus-visible:ring-1 focus-visible:ring-vscode-focusBorder",
disabled
? "opacity-50 cursor-not-allowed"
: "opacity-90 hover:opacity-100 hover:bg-[rgba(255,255,255,0.03)] hover:border-[rgba(255,255,255,0.15)] cursor-pointer",
triggerClassName,
)}>
<CaretUpIcon className="pointer-events-none opacity-80 flex-shrink-0 size-3" />

View file

@ -23,7 +23,9 @@ export interface ExtensionStateContextType extends ExtensionState {
setApiConfiguration: (config: ApiConfiguration) => void
setCustomInstructions: (value?: string) => void
setAlwaysAllowReadOnly: (value: boolean) => void
setAlwaysAllowReadOnlyOutsideWorkspace: (value: boolean) => void
setAlwaysAllowWrite: (value: boolean) => void
setAlwaysAllowWriteOutsideWorkspace: (value: boolean) => void
setAlwaysAllowExecute: (value: boolean) => void
setAlwaysAllowBrowser: (value: boolean) => void
setAlwaysAllowMcp: (value: boolean) => void
@ -52,8 +54,6 @@ export interface ExtensionStateContextType extends ExtensionState {
setMcpEnabled: (value: boolean) => void
enableMcpServerCreation: boolean
setEnableMcpServerCreation: (value: boolean) => void
enableCustomModeCreation?: boolean
setEnableCustomModeCreation: (value: boolean) => void
alwaysApproveResubmit?: boolean
setAlwaysApproveResubmit: (value: boolean) => void
requestDelaySeconds: number
@ -128,7 +128,6 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
checkpointStorage: "task",
fuzzyMatchThreshold: 1.0,
language: "en", // Default language code
enableCustomModeCreation: true,
writeDelayMs: 1000,
browserViewportSize: "900x600",
screenshotQuality: 75,
@ -254,7 +253,11 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
setState((prevState) => ({ ...prevState, apiConfiguration: { ...prevState.apiConfiguration, ...value } })),
setCustomInstructions: (value) => setState((prevState) => ({ ...prevState, customInstructions: value })),
setAlwaysAllowReadOnly: (value) => setState((prevState) => ({ ...prevState, alwaysAllowReadOnly: value })),
setAlwaysAllowReadOnlyOutsideWorkspace: (value) =>
setState((prevState) => ({ ...prevState, alwaysAllowReadOnlyOutsideWorkspace: value })),
setAlwaysAllowWrite: (value) => setState((prevState) => ({ ...prevState, alwaysAllowWrite: value })),
setAlwaysAllowWriteOutsideWorkspace: (value) =>
setState((prevState) => ({ ...prevState, alwaysAllowWriteOutsideWorkspace: value })),
setAlwaysAllowExecute: (value) => setState((prevState) => ({ ...prevState, alwaysAllowExecute: value })),
setAlwaysAllowBrowser: (value) => setState((prevState) => ({ ...prevState, alwaysAllowBrowser: value })),
setAlwaysAllowMcp: (value) => setState((prevState) => ({ ...prevState, alwaysAllowMcp: value })),
@ -290,8 +293,6 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
setCustomSupportPrompts: (value) => setState((prevState) => ({ ...prevState, customSupportPrompts: value })),
setEnhancementApiConfigId: (value) =>
setState((prevState) => ({ ...prevState, enhancementApiConfigId: value })),
setEnableCustomModeCreation: (value) =>
setState((prevState) => ({ ...prevState, enableCustomModeCreation: value })),
setAutoApprovalEnabled: (value) => setState((prevState) => ({ ...prevState, autoApprovalEnabled: value })),
setCustomModes: (value) => setState((prevState) => ({ ...prevState, customModes: value })),
setMaxOpenTabsContext: (value) => setState((prevState) => ({ ...prevState, maxOpenTabsContext: value })),

View file

@ -105,10 +105,15 @@
},
"current": "Actual"
},
"instructions": {
"wantsToFetch": "Roo vol obtenir instruccions detallades per ajudar amb la tasca actual."
},
"fileOperations": {
"wantsToRead": "Roo vol llegir aquest fitxer:",
"wantsToReadOutsideWorkspace": "Roo vol llegir aquest fitxer fora de l'espai de treball:",
"didRead": "Roo ha llegit aquest fitxer:",
"wantsToEdit": "Roo vol editar aquest fitxer:",
"wantsToEditOutsideWorkspace": "Roo vol editar aquest fitxer fora de l'espai de treball:",
"wantsToCreate": "Roo vol crear un nou fitxer:"
},
"directoryOperations": {

View file

@ -91,13 +91,13 @@
"TERMINAL_EXPLAIN": {
"label": "Explicar comanda del terminal",
"description": "Obtingueu explicacions detallades de les comandes del terminal i les seves sortides. Disponible al menú contextual del terminal (clic dret al contingut seleccionat del terminal)."
},
"NEW_TASK": {
"label": "Iniciar nova tasca",
"description": "Inicieu una nova tasca amb l'entrada proporcionada. Disponible a la paleta de comandes."
}
}
},
"customModeCreation": {
"enableTitle": "Habilitar la creació de modes personalitzats a través de prompts",
"description": "Quan està habilitat, Roo us permet crear modes personalitzats utilitzant prompts com 'Crea'm un mode personalitzat que...'. Desactivar-ho redueix el vostre prompt del sistema en aproximadament 700 tokens quan aquesta funcionalitat no és necessària. Quan està desactivat, encara podeu crear modes personalitzats manualment utilitzant el botó + de dalt o editant el JSON de configuració relacionat."
},
"advancedSystemPrompt": {
"title": "Avançat: Sobreescriure prompt del sistema",
"description": "Podeu reemplaçar completament el prompt del sistema per a aquest mode (a part de la definició de rol i instruccions personalitzades) creant un fitxer a <span>.roo/system-prompt-{{slug}}</span> al vostre espai de treball. Aquesta és una funcionalitat molt avançada que eludeix les salvaguardes integrades i les comprovacions de consistència (especialment al voltant de l'ús d'eines), així que aneu amb compte!"

View file

@ -35,12 +35,20 @@
"description": "Permet que Roo realitzi operacions automàticament sense requerir aprovació. Activeu aquesta configuració només si confieu plenament en la IA i enteneu els riscos de seguretat associats.",
"readOnly": {
"label": "Aprovar sempre operacions de només lectura",
"description": "Quan està activat, Roo veurà automàticament el contingut del directori i llegirà fitxers sense que calgui fer clic al botó Aprovar."
"description": "Quan està activat, Roo veurà automàticament el contingut del directori i llegirà fitxers sense que calgui fer clic al botó Aprovar.",
"outsideWorkspace": {
"label": "Incloure fitxers fora de l'espai de treball",
"description": "Permetre a Roo llegir fitxers fora de l'espai de treball actual sense requerir aprovació."
}
},
"write": {
"label": "Aprovar sempre operacions d'escriptura",
"description": "Crear i editar fitxers automàticament sense requerir aprovació",
"delayLabel": "Retard després d'escriptura per permetre que els diagnòstics detectin possibles problemes"
"delayLabel": "Retard després d'escriptura per permetre que els diagnòstics detectin possibles problemes",
"outsideWorkspace": {
"label": "Incloure fitxers fora de l'espai de treball",
"description": "Permetre a Roo crear i editar fitxers fora de l'espai de treball actual sense requerir aprovació."
}
},
"browser": {
"label": "Aprovar sempre accions del navegador",

View file

@ -105,10 +105,15 @@
},
"current": "Aktuell"
},
"instructions": {
"wantsToFetch": "Roo möchte detaillierte Anweisungen abrufen, um bei der aktuellen Aufgabe zu helfen"
},
"fileOperations": {
"wantsToRead": "Roo möchte diese Datei lesen:",
"wantsToReadOutsideWorkspace": "Roo möchte diese Datei außerhalb des Arbeitsbereichs lesen:",
"didRead": "Roo hat diese Datei gelesen:",
"wantsToEdit": "Roo möchte diese Datei bearbeiten:",
"wantsToEditOutsideWorkspace": "Roo möchte diese Datei außerhalb des Arbeitsbereichs bearbeiten:",
"wantsToCreate": "Roo möchte eine neue Datei erstellen:"
},
"directoryOperations": {

View file

@ -91,13 +91,13 @@
"TERMINAL_EXPLAIN": {
"label": "Terminal-Befehl erklären",
"description": "Erhalten Sie detaillierte Erklärungen zu Terminal-Befehlen und deren Ausgaben. Verfügbar im Kontextmenü des Terminals (Rechtsklick auf ausgewählten Terminal-Inhalt)."
},
"NEW_TASK": {
"label": "Neue Aufgabe starten",
"description": "Starte eine neue Aufgabe mit deiner Eingabe. Verfügbar in der Befehlspalette."
}
}
},
"customModeCreation": {
"enableTitle": "Erstellung benutzerdefinierter Modi über Prompts aktivieren",
"description": "Wenn aktiviert, ermöglicht Roo dir, benutzerdefinierte Modi mit Prompts wie 'Erstelle mir einen benutzerdefinierten Modus, der...' zu erstellen. Die Deaktivierung reduziert deinen System-Prompt um etwa 700 Tokens, wenn diese Funktion nicht benötigt wird. Bei Deaktivierung kannst du immer noch manuell benutzerdefinierte Modi mit der +-Schaltfläche oben erstellen oder durch Bearbeiten der zugehörigen Konfigurations-JSON."
},
"advancedSystemPrompt": {
"title": "Erweitert: System-Prompt überschreiben",
"description": "Du kannst den System-Prompt für diesen Modus vollständig ersetzen (abgesehen von der Rollendefinition und benutzerdefinierten Anweisungen), indem du eine Datei unter <span>.roo/system-prompt-{{slug}}</span> in deinem Arbeitsbereich erstellst. Dies ist eine sehr fortgeschrittene Funktion, die eingebaute Schutzmaßnahmen und Konsistenzprüfungen umgeht (besonders bei der Werkzeugnutzung), also sei vorsichtig!"

View file

@ -35,12 +35,20 @@
"description": "Erlaubt Roo, Operationen automatisch ohne Genehmigung durchzuführen. Aktiviere diese Einstellungen nur, wenn du der KI vollständig vertraust und die damit verbundenen Sicherheitsrisiken verstehst.",
"readOnly": {
"label": "Schreibgeschützte Operationen immer genehmigen",
"description": "Wenn aktiviert, wird Roo automatisch Verzeichnisinhalte anzeigen und Dateien lesen, ohne dass du auf die Genehmigen-Schaltfläche klicken musst."
"description": "Wenn aktiviert, wird Roo automatisch Verzeichnisinhalte anzeigen und Dateien lesen, ohne dass du auf die Genehmigen-Schaltfläche klicken musst.",
"outsideWorkspace": {
"label": "Dateien außerhalb des Arbeitsbereichs einbeziehen",
"description": "Roo erlauben, Dateien außerhalb des aktuellen Arbeitsbereichs ohne Genehmigung zu lesen."
}
},
"write": {
"label": "Schreiboperationen immer genehmigen",
"description": "Dateien automatisch erstellen und bearbeiten ohne Genehmigung",
"delayLabel": "Verzögerung nach Schreibvorgängen, damit Diagnosefunktionen potenzielle Probleme erkennen können"
"delayLabel": "Verzögerung nach Schreibvorgängen, damit Diagnosefunktionen potenzielle Probleme erkennen können",
"outsideWorkspace": {
"label": "Dateien außerhalb des Arbeitsbereichs einbeziehen",
"description": "Roo erlauben, Dateien außerhalb des aktuellen Arbeitsbereichs ohne Genehmigung zu erstellen und zu bearbeiten."
}
},
"browser": {
"label": "Browser-Aktionen immer genehmigen",

View file

@ -103,10 +103,15 @@
},
"current": "Current"
},
"instructions": {
"wantsToFetch": "Roo wants to fetch detailed instructions to assist with the current task"
},
"fileOperations": {
"wantsToRead": "Roo wants to read this file:",
"wantsToReadOutsideWorkspace": "Roo wants to read this file outside of the workspace:",
"didRead": "Roo read this file:",
"wantsToEdit": "Roo wants to edit this file:",
"wantsToEditOutsideWorkspace": "Roo wants to edit this file outside of the workspace:",
"wantsToCreate": "Roo wants to create a new file:"
},
"directoryOperations": {

View file

@ -91,13 +91,13 @@
"TERMINAL_EXPLAIN": {
"label": "Explain Terminal Command",
"description": "Get detailed explanations of terminal commands and their outputs. Available in the terminal context menu (right-click on selected terminal content)."
},
"NEW_TASK": {
"label": "Start New Task",
"description": "Start a new task with user input. Available in the Command Palette."
}
}
},
"customModeCreation": {
"enableTitle": "Enable Custom Mode Creation Through Prompts",
"description": "When enabled, Roo allows you to create custom modes using prompts like 'Make me a custom mode that…'. Disabling this reduces your system prompt by about 700 tokens when this feature isn't needed. When disabled you can still manually create custom modes using the + button above or by editing the related config JSON."
},
"advancedSystemPrompt": {
"title": "Advanced: Override System Prompt",
"description": "You can completely replace the system prompt for this mode (aside from the role definition and custom instructions) by creating a file at <span>.roo/system-prompt-{{slug}}</span> in your workspace. This is a very advanced feature that bypasses built-in safeguards and consistency checks (especially around tool usage), so be careful!"

View file

@ -35,12 +35,20 @@
"description": "Allow Roo to automatically perform operations without requiring approval. Enable these settings only if you fully trust the AI and understand the associated security risks.",
"readOnly": {
"label": "Always approve read-only operations",
"description": "When enabled, Roo will automatically view directory contents and read files without requiring you to click the Approve button."
"description": "When enabled, Roo will automatically view directory contents and read files without requiring you to click the Approve button.",
"outsideWorkspace": {
"label": "Include files outside workspace",
"description": "Allow Roo to read files outside the current workspace without requiring approval."
}
},
"write": {
"label": "Always approve write operations",
"description": "Automatically create and edit files without requiring approval",
"delayLabel": "Delay after writes to allow diagnostics to detect potential problems"
"delayLabel": "Delay after writes to allow diagnostics to detect potential problems",
"outsideWorkspace": {
"label": "Include files outside workspace",
"description": "Allow Roo to create and edit files outside the current workspace without requiring approval."
}
},
"browser": {
"label": "Always approve browser actions",

View file

@ -105,10 +105,15 @@
},
"current": "Actual"
},
"instructions": {
"wantsToFetch": "Roo quiere obtener instrucciones detalladas para ayudar con la tarea actual"
},
"fileOperations": {
"wantsToRead": "Roo quiere leer este archivo:",
"wantsToReadOutsideWorkspace": "Roo quiere leer este archivo fuera del espacio de trabajo:",
"didRead": "Roo leyó este archivo:",
"wantsToEdit": "Roo quiere editar este archivo:",
"wantsToEditOutsideWorkspace": "Roo quiere editar este archivo fuera del espacio de trabajo:",
"wantsToCreate": "Roo quiere crear un nuevo archivo:"
},
"directoryOperations": {

View file

@ -91,13 +91,13 @@
"TERMINAL_EXPLAIN": {
"label": "Explicar comando de terminal",
"description": "Obtén explicaciones detalladas de comandos de terminal y sus salidas. Disponible en el menú contextual de la terminal (clic derecho en el contenido seleccionado de la terminal)."
},
"NEW_TASK": {
"label": "Iniciar nueva tarea",
"description": "Inicia una nueva tarea con entrada del usuario. Disponible en la Paleta de comandos."
}
}
},
"customModeCreation": {
"enableTitle": "Habilitar la creación de modos personalizados a través de solicitudes",
"description": "Cuando está habilitado, Roo te permite crear modos personalizados usando solicitudes como 'Hazme un modo personalizado que...'. Deshabilitarlo reduce tu solicitud del sistema en aproximadamente 700 tokens cuando esta función no es necesaria. Cuando está deshabilitado, aún puedes crear modos personalizados manualmente usando el botón + de arriba o editando el JSON de configuración relacionado."
},
"advancedSystemPrompt": {
"title": "Avanzado: Anular solicitud del sistema",
"description": "Puedes reemplazar completamente la solicitud del sistema para este modo (aparte de la definición de rol e instrucciones personalizadas) creando un archivo en <span>.roo/system-prompt-{{slug}}</span> en tu espacio de trabajo. ¡Esta es una función muy avanzada que omite las salvaguardas integradas y las verificaciones de consistencia (especialmente en torno al uso de herramientas), así que ten cuidado!"

View file

@ -35,12 +35,20 @@
"description": "Permitir que Roo realice operaciones automáticamente sin requerir aprobación. Habilite esta configuración solo si confía plenamente en la IA y comprende los riesgos de seguridad asociados.",
"readOnly": {
"label": "Aprobar siempre operaciones de solo lectura",
"description": "Cuando está habilitado, Roo verá automáticamente el contenido del directorio y leerá archivos sin que necesite hacer clic en el botón Aprobar."
"description": "Cuando está habilitado, Roo verá automáticamente el contenido del directorio y leerá archivos sin que necesite hacer clic en el botón Aprobar.",
"outsideWorkspace": {
"label": "Incluir archivos fuera del espacio de trabajo",
"description": "Permitir a Roo leer archivos fuera del espacio de trabajo actual sin requerir aprobación."
}
},
"write": {
"label": "Aprobar siempre operaciones de escritura",
"description": "Crear y editar archivos automáticamente sin requerir aprobación",
"delayLabel": "Retraso después de escritura para permitir que los diagnósticos detecten posibles problemas"
"delayLabel": "Retraso después de escritura para permitir que los diagnósticos detecten posibles problemas",
"outsideWorkspace": {
"label": "Incluir archivos fuera del espacio de trabajo",
"description": "Permitir a Roo crear y editar archivos fuera del espacio de trabajo actual sin requerir aprobación."
}
},
"browser": {
"label": "Aprobar siempre acciones del navegador",

View file

@ -107,10 +107,15 @@
},
"fileOperations": {
"wantsToRead": "Roo veut lire ce fichier :",
"wantsToReadOutsideWorkspace": "Roo veut lire ce fichier en dehors de l'espace de travail :",
"didRead": "Roo a lu ce fichier :",
"wantsToEdit": "Roo veut éditer ce fichier :",
"wantsToEditOutsideWorkspace": "Roo veut éditer ce fichier en dehors de l'espace de travail :",
"wantsToCreate": "Roo veut créer un nouveau fichier :"
},
"instructions": {
"wantsToFetch": "Roo veut récupérer des instructions détaillées pour aider à la tâche actuelle"
},
"directoryOperations": {
"wantsToViewTopLevel": "Roo veut voir les fichiers de premier niveau dans ce répertoire :",
"didViewTopLevel": "Roo a vu les fichiers de premier niveau dans ce répertoire :",

View file

@ -91,13 +91,13 @@
"TERMINAL_EXPLAIN": {
"label": "Expliquer la commande du terminal",
"description": "Obtenez des explications détaillées sur les commandes du terminal et leurs sorties. Disponible dans le menu contextuel du terminal (clic droit sur le contenu sélectionné du terminal)."
},
"NEW_TASK": {
"label": "Démarrer une nouvelle tâche",
"description": "Démarre une nouvelle tâche avec ton entrée. Disponible dans la palette de commandes."
}
}
},
"customModeCreation": {
"enableTitle": "Activer la création de modes personnalisés via les prompts",
"description": "Lorsque cette option est activée, Roo vous permet de créer des modes personnalisés en utilisant des prompts comme 'Crée-moi un mode personnalisé qui...'. La désactivation réduit votre prompt système d'environ 700 tokens lorsque cette fonctionnalité n'est pas nécessaire. Lorsqu'elle est désactivée, vous pouvez toujours créer manuellement des modes personnalisés en utilisant le bouton + ci-dessus ou en modifiant le JSON de configuration associé."
},
"advancedSystemPrompt": {
"title": "Avancé : Remplacer le prompt système",
"description": "Vous pouvez complètement remplacer le prompt système pour ce mode (en dehors de la définition du rôle et des instructions personnalisées) en créant un fichier à <span>.roo/system-prompt-{{slug}}</span> dans votre espace de travail. Il s'agit d'une fonctionnalité très avancée qui contourne les garanties intégrées et les vérifications de cohérence (notamment concernant l'utilisation des outils), alors soyez prudent !"

View file

@ -35,12 +35,20 @@
"description": "Permettre à Roo d'effectuer automatiquement des opérations sans requérir d'approbation. Activez ces paramètres uniquement si vous faites entièrement confiance à l'IA et que vous comprenez les risques de sécurité associés.",
"readOnly": {
"label": "Toujours approuver les opérations en lecture seule",
"description": "Lorsque cette option est activée, Roo affichera automatiquement le contenu des répertoires et lira les fichiers sans que vous ayez à cliquer sur le bouton Approuver."
"description": "Lorsque cette option est activée, Roo affichera automatiquement le contenu des répertoires et lira les fichiers sans que vous ayez à cliquer sur le bouton Approuver.",
"outsideWorkspace": {
"label": "Inclure les fichiers en dehors de l'espace de travail",
"description": "Permettre à Roo de lire des fichiers en dehors de l'espace de travail actuel sans nécessiter d'approbation."
}
},
"write": {
"label": "Toujours approuver les opérations d'écriture",
"description": "Créer et modifier automatiquement des fichiers sans nécessiter d'approbation",
"delayLabel": "Délai après les écritures pour permettre aux diagnostics de détecter les problèmes potentiels"
"delayLabel": "Délai après les écritures pour permettre aux diagnostics de détecter les problèmes potentiels",
"outsideWorkspace": {
"label": "Inclure les fichiers en dehors de l'espace de travail",
"description": "Permettre à Roo de créer et modifier des fichiers en dehors de l'espace de travail actuel sans nécessiter d'approbation."
}
},
"browser": {
"label": "Toujours approuver les actions du navigateur",

View file

@ -105,10 +105,15 @@
},
"current": "वर्तमान"
},
"instructions": {
"wantsToFetch": "Roo को वर्तमान कार्य में सहायता के लिए विस्तृत निर्देश प्राप्त करना है"
},
"fileOperations": {
"wantsToRead": "Roo इस फ़ाइल को पढ़ना चाहता है:",
"wantsToReadOutsideWorkspace": "Roo कार्यक्षेत्र के बाहर इस फ़ाइल को पढ़ना चाहता है:",
"didRead": "Roo ने इस फ़ाइल को पढ़ा:",
"wantsToEdit": "Roo इस फ़ाइल को संपादित करना चाहता है:",
"wantsToEditOutsideWorkspace": "Roo कार्यक्षेत्र के बाहर इस फ़ाइल को संपादित करना चाहता है:",
"wantsToCreate": "Roo एक नई फ़ाइल बनाना चाहता है:"
},
"directoryOperations": {

View file

@ -91,13 +91,13 @@
"TERMINAL_EXPLAIN": {
"label": "टर्मिनल कमांड समझाएँ",
"description": "टर्मिनल कमांड और उनके आउटपुट के विस्तृत स्पष्टीकरण प्राप्त करें। टर्मिनल के कंटेक्स्ट मेनू (चयनित टर्मिनल सामग्री पर राइट-क्लिक) में उपलब्ध है।"
},
"NEW_TASK": {
"label": "नया कार्य शुरू करें",
"description": "इनपुट के साथ नया कार्य शुरू करें। कमांड पैलेट में उपलब्ध है।"
}
}
},
"customModeCreation": {
"enableTitle": "प्रॉम्प्ट्स के माध्यम से कस्टम मोड निर्माण सक्षम करें",
"description": "सक्षम होने पर, Roo आपको 'मेरे लिए एक कस्टम मोड बनाएँ जो...' जैसे प्रॉम्प्ट्स का उपयोग करके कस्टम मोड बनाने की अनुमति देता है। इस सुविधा की आवश्यकता न होने पर इसे अक्षम करने से आपका सिस्टम प्रॉम्प्ट लगभग 700 token कम हो जाता है। अक्षम होने पर भी आप ऊपर दिए गए + बटन का उपयोग करके या संबंधित कॉन्फिग JSON को संपादित करके मैन्युअल रूप से कस्टम मोड बना सकते हैं।"
},
"advancedSystemPrompt": {
"title": "उन्नत: सिस्टम प्रॉम्प्ट ओवरराइड करें",
"description": "आप अपने वर्कस्पेस में <span>.roo/system-prompt-{{slug}}</span> पर एक फाइल बनाकर इस मोड के लिए सिस्टम प्रॉम्प्ट को पूरी तरह से बदल सकते हैं (भूमिका परिभाषा और कस्टम निर्देशों को छोड़कर)। यह एक बहुत उन्नत सुविधा है जो अंतर्निहित सुरक्षा उपायों और सामंजस्यता जांचों को बायपास करती है (विशेष रूप से टूल उपयोग के आसपास), इसलिए सावधान रहें!"

View file

@ -35,12 +35,20 @@
"description": "Roo को अनुमोदन की आवश्यकता के बिना स्वचालित रूप से ऑपरेशन करने की अनुमति दें। इन सेटिंग्स को केवल तभी सक्षम करें जब आप AI पर पूरी तरह से भरोसा करते हों और संबंधित सुरक्षा जोखिमों को समझते हों।",
"readOnly": {
"label": "केवल पढ़ने वाले ऑपरेशन हमेशा अनुमोदित करें",
"description": "जब सक्षम होता है, तो Roo आपके अनुमोदित बटन पर क्लिक किए बिना स्वचालित रूप से निर्देशिका सामग्री देखेगा और फाइलें पढ़ेगा।"
"description": "जब सक्षम होता है, तो Roo आपके अनुमोदित बटन पर क्लिक किए बिना स्वचालित रूप से निर्देशिका सामग्री देखेगा और फाइलें पढ़ेगा।",
"outsideWorkspace": {
"label": "वर्कस्पेस के बाहर की फाइलें शामिल करें",
"description": "Roo को अनुमोदन की आवश्यकता के बिना वर्तमान वर्कस्पेस के बाहर की फाइलें पढ़ने की अनुमति दें।"
}
},
"write": {
"label": "लिखने वाले ऑपरेशन हमेशा अनुमोदित करें",
"description": "अनुमोदन की आवश्यकता के बिना स्वचालित रूप से फाइलें बनाएँ और संपादित करें",
"delayLabel": "लिखने के बाद विलंब ताकि डायग्नोस्टिक संभावित समस्याओं का पता लगा सकें"
"delayLabel": "लिखने के बाद विलंब ताकि डायग्नोस्टिक संभावित समस्याओं का पता लगा सकें",
"outsideWorkspace": {
"label": "वर्कस्पेस के बाहर की फाइलें शामिल करें",
"description": "Roo को अनुमोदन की आवश्यकता के बिना वर्तमान वर्कस्पेस के बाहर फाइलें बनाने और संपादित करने की अनुमति दें।"
}
},
"browser": {
"label": "ब्राउज़र क्रियाएँ हमेशा अनुमोदित करें",

View file

@ -80,6 +80,9 @@
"separator": "Separatore",
"edit": "Modifica...",
"forNextMode": "per la prossima modalità",
"instructions": {
"wantsToFetch": "Roo vuole recuperare istruzioni dettagliate per aiutare con l'attività corrente"
},
"error": "Errore",
"troubleMessage": "Roo sta avendo problemi...",
"apiRequest": {
@ -107,8 +110,10 @@
},
"fileOperations": {
"wantsToRead": "Roo vuole leggere questo file:",
"wantsToReadOutsideWorkspace": "Roo vuole leggere questo file al di fuori dell'area di lavoro:",
"didRead": "Roo ha letto questo file:",
"wantsToEdit": "Roo vuole modificare questo file:",
"wantsToEditOutsideWorkspace": "Roo vuole modificare questo file al di fuori dell'area di lavoro:",
"wantsToCreate": "Roo vuole creare un nuovo file:"
},
"directoryOperations": {

View file

@ -91,13 +91,13 @@
"TERMINAL_EXPLAIN": {
"label": "Spiega comando del terminale",
"description": "Ottieni spiegazioni dettagliate sui comandi del terminale e sui loro output. Disponibile nel menu contestuale del terminale (clic destro sul contenuto selezionato del terminale)."
},
"NEW_TASK": {
"label": "Avvia nuova attività",
"description": "Avvia una nuova attività con il tuo input. Disponibile nella palette dei comandi."
}
}
},
"customModeCreation": {
"enableTitle": "Abilita creazione di modalità personalizzate tramite prompt",
"description": "Quando abilitato, Roo ti permette di creare modalità personalizzate usando prompt come 'Crea una modalità personalizzata che...'. Disabilitarlo riduce il tuo prompt di sistema di circa 700 token quando questa funzionalità non è necessaria. Quando disabilitato, puoi comunque creare manualmente modalità personalizzate usando il pulsante + sopra o modificando il JSON di configurazione correlato."
},
"advancedSystemPrompt": {
"title": "Avanzato: Sovrascrivi prompt di sistema",
"description": "Puoi sostituire completamente il prompt di sistema per questa modalità (a parte la definizione del ruolo e le istruzioni personalizzate) creando un file in <span>.roo/system-prompt-{{slug}}</span> nel tuo spazio di lavoro. Questa è una funzionalità molto avanzata che bypassa le protezioni integrate e i controlli di coerenza (specialmente riguardo all'uso degli strumenti), quindi fai attenzione!"

View file

@ -35,12 +35,20 @@
"description": "Permetti a Roo di eseguire automaticamente operazioni senza richiedere approvazione. Abilita queste impostazioni solo se ti fidi completamente dell'IA e comprendi i rischi di sicurezza associati.",
"readOnly": {
"label": "Approva sempre operazioni di sola lettura",
"description": "Quando abilitato, Roo visualizzerà automaticamente i contenuti della directory e leggerà i file senza richiedere di cliccare sul pulsante Approva."
"description": "Quando abilitato, Roo visualizzerà automaticamente i contenuti della directory e leggerà i file senza richiedere di cliccare sul pulsante Approva.",
"outsideWorkspace": {
"label": "Includi file al di fuori dell'area di lavoro",
"description": "Permetti a Roo di leggere file al di fuori dell'area di lavoro attuale senza richiedere approvazione."
}
},
"write": {
"label": "Approva sempre operazioni di scrittura",
"description": "Crea e modifica automaticamente i file senza richiedere approvazione",
"delayLabel": "Ritardo dopo le scritture per consentire alla diagnostica di rilevare potenziali problemi"
"delayLabel": "Ritardo dopo le scritture per consentire alla diagnostica di rilevare potenziali problemi",
"outsideWorkspace": {
"label": "Includi file al di fuori dell'area di lavoro",
"description": "Permetti a Roo di creare e modificare file al di fuori dell'area di lavoro attuale senza richiedere approvazione."
}
},
"browser": {
"label": "Approva sempre azioni del browser",

View file

@ -105,10 +105,15 @@
},
"current": "現在"
},
"instructions": {
"wantsToFetch": "Rooは現在のタスクを支援するための詳細な指示を取得したい"
},
"fileOperations": {
"wantsToRead": "Rooはこのファイルを読みたい:",
"wantsToReadOutsideWorkspace": "Rooはワークスペース外のこのファイルを読みたい:",
"didRead": "Rooはこのファイルを読みました:",
"wantsToEdit": "Rooはこのファイルを編集したい:",
"wantsToEditOutsideWorkspace": "Rooはワークスペース外のこのファイルを編集したい:",
"wantsToCreate": "Rooは新しいファイルを作成したい:"
},
"directoryOperations": {

View file

@ -91,13 +91,13 @@
"TERMINAL_EXPLAIN": {
"label": "ターミナルコマンドを説明",
"description": "ターミナルコマンドとその出力の詳細な説明を得ることができます。ターミナルのコンテキストメニュー(選択したターミナルの内容で右クリック)から利用できます。"
},
"NEW_TASK": {
"label": "新しいタスクを開始",
"description": "入力内容で新しいタスクを開始できます。コマンドパレットから利用できます。"
}
}
},
"customModeCreation": {
"enableTitle": "プロンプトを通じたカスタムモード作成を有効にする",
"description": "有効にすると、Rooは「するカスタムモードを作成して」のようなプロンプトを使用してカスタムモードを作成できます。無効にすると、この機能が不要な場合、システムプロンプトが約700トークン削減されます。無効にしても、上記の+ボタンを使用するか、関連する設定JSONを編集して手動でカスタムモードを作成できます。"
},
"advancedSystemPrompt": {
"title": "詳細設定:システムプロンプトの上書き",
"description": "ワークスペースの<span>.roo/system-prompt-{{slug}}</span>にファイルを作成することで、このモードのシステムプロンプト(役割定義とカスタム指示以外)を完全に置き換えることができます。これは組み込みの安全対策と一貫性チェック(特にツールの使用に関して)をバイパスする非常に高度な機能なので、注意して使用してください!"

View file

@ -35,12 +35,20 @@
"description": "Rooが承認なしで自動的に操作を実行できるようにします。AIを完全に信頼し、関連するセキュリティリスクを理解している場合にのみ、これらの設定を有効にしてください。",
"readOnly": {
"label": "読み取り専用操作を常に承認",
"description": "有効にすると、Rooは承認ボタンをクリックすることなく、自動的にディレクトリの内容を表示してファイルを読み取ります。"
"description": "有効にすると、Rooは承認ボタンをクリックすることなく、自動的にディレクトリの内容を表示してファイルを読み取ります。",
"outsideWorkspace": {
"label": "ワークスペース外のファイルを含める",
"description": "Rooが承認なしで現在のワークスペース外のファイルを読み取ることを許可します。"
}
},
"write": {
"label": "書き込み操作を常に承認",
"description": "承認なしで自動的にファイルを作成・編集",
"delayLabel": "診断が潜在的な問題を検出できるよう、書き込み後に遅延を設ける"
"delayLabel": "診断が潜在的な問題を検出できるよう、書き込み後に遅延を設ける",
"outsideWorkspace": {
"label": "ワークスペース外のファイルを含める",
"description": "Rooが承認なしで現在のワークスペース外のファイルを作成・編集することを許可します。"
}
},
"browser": {
"label": "ブラウザアクションを常に承認",

View file

@ -105,10 +105,15 @@
},
"current": "현재"
},
"instructions": {
"wantsToFetch": "Roo는 현재 작업을 지원하기 위해 자세한 지침을 가져오려고 합니다"
},
"fileOperations": {
"wantsToRead": "Roo가 이 파일을 읽고 싶어합니다:",
"wantsToReadOutsideWorkspace": "Roo가 워크스페이스 외부의 이 파일을 읽고 싶어합니다:",
"didRead": "Roo가 이 파일을 읽었습니다:",
"wantsToEdit": "Roo가 이 파일을 편집하고 싶어합니다:",
"wantsToEditOutsideWorkspace": "Roo가 워크스페이스 외부의 이 파일을 편집하고 싶어합니다:",
"wantsToCreate": "Roo가 새 파일을 만들고 싶어합니다:"
},
"directoryOperations": {

View file

@ -91,13 +91,13 @@
"TERMINAL_EXPLAIN": {
"label": "터미널 명령 설명",
"description": "터미널 명령과 그 출력에 대한 상세한 설명을 얻을 수 있습니다. 터미널 컨텍스트 메뉴(선택한 터미널 콘텐츠에서 우클릭)에서 이용 가능합니다."
},
"NEW_TASK": {
"label": "새 작업 시작",
"description": "입력한 내용으로 새 작업을 시작할 수 있습니다. 명령 팔레트에서 이용 가능합니다."
}
}
},
"customModeCreation": {
"enableTitle": "프롬프트를 통한 커스텀 모드 생성 활성화",
"description": "활성화하면 Roo를 통해 '...하는 커스텀 모드를 만들어줘'와 같은 프롬프트를 사용하여 커스텀 모드를 생성할 수 있습니다. 이 기능이 필요하지 않을 때 비활성화하면 시스템 프롬프트가 약 700 token 감소합니다. 비활성화해도 위의 + 버튼을 사용하거나 관련 구성 JSON을 편집하여 수동으로 커스텀 모드를 생성할 수 있습니다."
},
"advancedSystemPrompt": {
"title": "고급: 시스템 프롬프트 재정의",
"description": "작업 공간의 <span>.roo/system-prompt-{{slug}}</span>에 파일을 생성하여 이 모드의 시스템 프롬프트(역할 정의 및 사용자 지정 지침 제외)를 완전히 대체할 수 있습니다. 이는 내장된 안전 장치와 일관성 검사(특히 도구 사용 관련)를 우회하는 매우 고급 기능이므로 주의하세요!"

View file

@ -35,12 +35,20 @@
"description": "Roo가 승인 없이 자동으로 작업을 수행할 수 있도록 허용합니다. AI를 완전히 신뢰하고 관련 보안 위험을 이해하는 경우에만 이러한 설정을 활성화하세요.",
"readOnly": {
"label": "읽기 전용 작업 항상 승인",
"description": "활성화되면 Roo는 승인 버튼을 클릭하지 않고도 자동으로 디렉토리 내용을 보고 파일을 읽습니다."
"description": "활성화되면 Roo는 승인 버튼을 클릭하지 않고도 자동으로 디렉토리 내용을 보고 파일을 읽습니다.",
"outsideWorkspace": {
"label": "워크스페이스 외부 파일 포함",
"description": "Roo가 승인 없이 현재 워크스페이스 외부의 파일을 읽을 수 있도록 허용합니다."
}
},
"write": {
"label": "쓰기 작업 항상 승인",
"description": "승인 없이 자동으로 파일 생성 및 편집",
"delayLabel": "진단이 잠재적 문제를 감지할 수 있도록 쓰기 후 지연"
"delayLabel": "진단이 잠재적 문제를 감지할 수 있도록 쓰기 후 지연",
"outsideWorkspace": {
"label": "워크스페이스 외부 파일 포함",
"description": "Roo가 승인 없이 현재 워크스페이스 외부의 파일을 생성하고 편집할 수 있도록 허용합니다."
}
},
"browser": {
"label": "브라우저 작업 항상 승인",

View file

@ -105,10 +105,15 @@
},
"current": "Bieżący"
},
"instructions": {
"wantsToFetch": "Roo chce pobrać szczegółowe instrukcje, aby pomóc w bieżącym zadaniu"
},
"fileOperations": {
"wantsToRead": "Roo chce przeczytać ten plik:",
"wantsToReadOutsideWorkspace": "Roo chce przeczytać ten plik poza obszarem roboczym:",
"didRead": "Roo przeczytał ten plik:",
"wantsToEdit": "Roo chce edytować ten plik:",
"wantsToEditOutsideWorkspace": "Roo chce edytować ten plik poza obszarem roboczym:",
"wantsToCreate": "Roo chce utworzyć nowy plik:"
},
"directoryOperations": {

View file

@ -91,13 +91,13 @@
"TERMINAL_EXPLAIN": {
"label": "Wyjaśnij polecenie terminala",
"description": "Uzyskaj szczegółowe wyjaśnienia poleceń terminala i ich wyników. Dostępne w menu kontekstowym terminala (prawy przycisk myszy na wybranej zawartości terminala)."
},
"NEW_TASK": {
"label": "Rozpocznij nowe zadanie",
"description": "Rozpocznij nowe zadanie z wprowadzonymi danymi. Dostępne w palecie poleceń."
}
}
},
"customModeCreation": {
"enableTitle": "Włącz tworzenie niestandardowych trybów przez podpowiedzi",
"description": "Gdy włączone, Roo pozwala na tworzenie niestandardowych trybów za pomocą podpowiedzi takich jak 'Stwórz dla mnie niestandardowy tryb, który...'. Wyłączenie tej opcji zmniejsza podpowiedź systemową o około 700 tokenów, gdy ta funkcja nie jest potrzebna. Po wyłączeniu nadal możesz ręcznie tworzyć niestandardowe tryby za pomocą przycisku + powyżej lub edytując powiązany plik konfiguracyjny JSON."
},
"advancedSystemPrompt": {
"title": "Zaawansowane: Zastąp podpowiedź systemową",
"description": "Możesz całkowicie zastąpić podpowiedź systemową dla tego trybu (oprócz definicji roli i niestandardowych instrukcji) poprzez utworzenie pliku w .roo/system-prompt-{{modeSlug}} w swoim obszarze roboczym. Jest to bardzo zaawansowana funkcja, która omija wbudowane zabezpieczenia i kontrole spójności (szczególnie wokół używania narzędzi), więc bądź ostrożny!"

View file

@ -35,12 +35,20 @@
"description": "Pozwól Roo na automatyczne wykonywanie operacji bez wymagania zatwierdzenia. Włącz te ustawienia tylko jeśli w pełni ufasz AI i rozumiesz związane z tym zagrożenia bezpieczeństwa.",
"readOnly": {
"label": "Zawsze zatwierdzaj operacje tylko do odczytu",
"description": "Gdy włączone, Roo automatycznie będzie wyświetlać zawartość katalogów i czytać pliki bez konieczności klikania przycisku Zatwierdź."
"description": "Gdy włączone, Roo automatycznie będzie wyświetlać zawartość katalogów i czytać pliki bez konieczności klikania przycisku Zatwierdź.",
"outsideWorkspace": {
"label": "Uwzględnij pliki poza obszarem roboczym",
"description": "Pozwól Roo na odczyt plików poza bieżącym obszarem roboczym bez konieczności zatwierdzania."
}
},
"write": {
"label": "Zawsze zatwierdzaj operacje zapisu",
"description": "Automatycznie twórz i edytuj pliki bez konieczności zatwierdzania",
"delayLabel": "Opóźnienie po zapisach, aby umożliwić diagnostyce wykrycie potencjalnych problemów"
"delayLabel": "Opóźnienie po zapisach, aby umożliwić diagnostyce wykrycie potencjalnych problemów",
"outsideWorkspace": {
"label": "Uwzględnij pliki poza obszarem roboczym",
"description": "Pozwól Roo na tworzenie i edycję plików poza bieżącym obszarem roboczym bez konieczności zatwierdzania."
}
},
"browser": {
"label": "Zawsze zatwierdzaj akcje przeglądarki",

View file

@ -105,10 +105,15 @@
},
"current": "Atual"
},
"instructions": {
"wantsToFetch": "Roo quer buscar instruções detalhadas para ajudar com a tarefa atual"
},
"fileOperations": {
"wantsToRead": "Roo quer ler este arquivo:",
"wantsToReadOutsideWorkspace": "Roo quer ler este arquivo fora do espaço de trabalho:",
"didRead": "Roo leu este arquivo:",
"wantsToEdit": "Roo quer editar este arquivo:",
"wantsToEditOutsideWorkspace": "Roo quer editar este arquivo fora do espaço de trabalho:",
"wantsToCreate": "Roo quer criar um novo arquivo:"
},
"directoryOperations": {

View file

@ -91,13 +91,13 @@
"TERMINAL_EXPLAIN": {
"label": "Explicar Comando do Terminal",
"description": "Obtenha explicações detalhadas de comandos de terminal e suas saídas. Available in the terminal context menu (right-click on selected terminal content)."
},
"NEW_TASK": {
"label": "Iniciar Nova Tarefa",
"description": "Inicie uma nova tarefa com a entrada fornecida. Disponível na paleta de comandos."
}
}
},
"customModeCreation": {
"enableTitle": "Ativar criação de modo personalizado através de prompts",
"description": "Quando ativado, o Roo permite que você crie modos personalizados usando prompts como 'Crie para mim um modo personalizado que...'. Desativar isto reduz seu prompt de sistema em cerca de 700 tokens quando esta funcionalidade não é necessária. Quando desativado, você ainda pode criar modos personalizados manualmente usando o botão + acima ou editando o JSON de configuração relacionado."
},
"advancedSystemPrompt": {
"title": "Avançado: Substituir prompt do sistema",
"description": "Você pode substituir completamente o prompt do sistema para este modo (além da definição de função e instruções personalizadas) criando um arquivo em .roo/system-prompt-{{modeSlug}} no seu espaço de trabalho. Esta é uma funcionalidade muito avançada que contorna as salvaguardas integradas e verificações de consistência (especialmente em torno do uso de ferramentas), então tenha cuidado!"

View file

@ -35,12 +35,20 @@
"description": "Permitir que o Roo realize operações automaticamente sem exigir aprovação. Ative essas configurações apenas se confiar totalmente na IA e compreender os riscos de segurança associados.",
"readOnly": {
"label": "Aprovar sempre operações somente de leitura",
"description": "Quando ativado, o Roo visualizará automaticamente o conteúdo do diretório e lerá arquivos sem que você precise clicar no botão Aprovar."
"description": "Quando ativado, o Roo visualizará automaticamente o conteúdo do diretório e lerá arquivos sem que você precise clicar no botão Aprovar.",
"outsideWorkspace": {
"label": "Incluir arquivos fora do espaço de trabalho",
"description": "Permitir que o Roo leia arquivos fora do espaço de trabalho atual sem exigir aprovação."
}
},
"write": {
"label": "Aprovar sempre operações de escrita",
"description": "Criar e editar arquivos automaticamente sem exigir aprovação",
"delayLabel": "Atraso após escritas para permitir que diagnósticos detectem problemas potenciais"
"delayLabel": "Atraso após escritas para permitir que diagnósticos detectem problemas potenciais",
"outsideWorkspace": {
"label": "Incluir arquivos fora do espaço de trabalho",
"description": "Permitir que o Roo crie e edite arquivos fora do espaço de trabalho atual sem exigir aprovação."
}
},
"browser": {
"label": "Aprovar sempre ações do navegador",

View file

@ -105,10 +105,15 @@
},
"current": "Mevcut"
},
"instructions": {
"wantsToFetch": "Roo mevcut göreve yardımcı olmak için ayrıntılı talimatlar almak istiyor"
},
"fileOperations": {
"wantsToRead": "Roo bu dosyayı okumak istiyor:",
"wantsToReadOutsideWorkspace": "Roo çalışma alanı dışındaki bu dosyayı okumak istiyor:",
"didRead": "Roo bu dosyayı okudu:",
"wantsToEdit": "Roo bu dosyayı düzenlemek istiyor:",
"wantsToEditOutsideWorkspace": "Roo çalışma alanı dışındaki bu dosyayı düzenlemek istiyor:",
"wantsToCreate": "Roo yeni bir dosya oluşturmak istiyor:"
},
"directoryOperations": {

View file

@ -91,13 +91,13 @@
"TERMINAL_EXPLAIN": {
"label": "Terminal Komutunu Açıkla",
"description": "Terminal komutları ve çıktıları hakkında ayrıntılııklamalar alın. Terminal bağlam menüsünde (seçili terminal içeriğine sağ tıklayın) kullanılabilir."
},
"NEW_TASK": {
"label": "Yeni Görev Başlat",
"description": "Girdiyle yeni bir görev başlat. Komut paletinde kullanılabilir."
}
}
},
"customModeCreation": {
"enableTitle": "Promptlar aracılığıyla özel mod oluşturmayı etkinleştir",
"description": "Etkinleştirildiğinde, Roo 'Bana ... yapabilen özel bir mod oluştur' gibi promptlar kullanarak özel modlar oluşturmanıza olanak tanır. Bu özelliğe ihtiyaç duyulmadığında devre dışı bırakmak, sistem promptunuzu yaklaşık 700 token azaltır. Devre dışı bırakıldığında, yukarıdaki + düğmesini kullanarak veya ilgili yapılandırma JSON'ını düzenleyerek manuel olarak özel modlar oluşturabilirsiniz."
},
"advancedSystemPrompt": {
"title": "Gelişmiş: Sistem Promptunu Geçersiz Kıl",
"description": "Çalışma alanınızda <span>.roo/system-prompt-{{slug}}</span> adresinde bir dosya oluşturarak bu mod için sistem istemini tamamen değiştirebilirsiniz (rol tanımı ve özel talimatlar hariç). Bu, yerleşik güvenlik önlemlerini ve tutarlılık kontrollerini (özellikle araç kullanımıyla ilgili) aşan çok gelişmiş bir özelliktir, bu yüzden dikkatli olun!"

Some files were not shown because too many files have changed in this diff Show more