mirror of
https://github.com/cline/cline.git
synced 2026-09-09 06:45:53 +08:00
Compare commits
7
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2ed8fa0a5f | ||
|
|
5cd958d424 | ||
|
|
0e5cc8ec45 | ||
|
|
542cf70c92 | ||
|
|
bb4fd5555b | ||
|
|
d601261467 | ||
|
|
7040ecc442 |
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"claude-dev": minor
|
||||
---
|
||||
|
||||
Put all the navigation state and message handling and navigation functions in the extension state context instead of the app.tsx
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"claude-dev": minor
|
||||
---
|
||||
|
||||
Migrate setActiveQuote to protobus
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"claude-dev": patch
|
||||
---
|
||||
|
||||
Fix edge case of changing language in settings
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"claude-dev": patch
|
||||
---
|
||||
|
||||
feat: added gemini flash 05-20
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"claude-dev": minor
|
||||
---
|
||||
|
||||
Migrate toggleToolAutoApprove to protobus
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"claude-dev": patch
|
||||
---
|
||||
|
||||
Use identify to enhance distinct user segmentation
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"claude-dev": patch
|
||||
---
|
||||
|
||||
togglePlanActMode protobus migration
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"claude-dev": minor
|
||||
---
|
||||
|
||||
Migrate toggleClineRule to protobus
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"claude-dev": patch
|
||||
---
|
||||
|
||||
taskCompletionViewChanges protobus migration
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"claude-dev": patch
|
||||
---
|
||||
|
||||
copyToClipboard protobus migration
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"claude-dev": minor
|
||||
---
|
||||
|
||||
Migrate updateTerminalConnectionTimeout to protobus
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"claude-dev": patch
|
||||
---
|
||||
|
||||
Fixing bug in toggle plan and act
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"claude-dev": patch
|
||||
---
|
||||
|
||||
scrollToSetting protobus migration
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"claude-dev": minor
|
||||
---
|
||||
|
||||
migrate accountLogoutClicked to protobus
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"claude-dev": patch
|
||||
---
|
||||
|
||||
openMention protobus migration
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"claude-dev": minor
|
||||
---
|
||||
|
||||
Migrate toggleWindsurfRule to protobus
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"claude-dev": minor
|
||||
---
|
||||
|
||||
Migrate restartMcpServer to protobus
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"claude-dev": minor
|
||||
---
|
||||
|
||||
Migrate silentlyRefreshMcpMarketplace to protobus
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"claude-dev": minor
|
||||
---
|
||||
|
||||
Migrate toggleCursorRule to protobus
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"claude-dev": patch
|
||||
---
|
||||
|
||||
browserRelaunchResult protobus migration
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"claude-dev": patch
|
||||
---
|
||||
|
||||
Remind VS code users of clines existence(Open Cline on AutoUpdate+KeyboardShortcuts+Lightbulb icons)
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"claude-dev": minor
|
||||
---
|
||||
|
||||
Migrate the deleteMcpServer message to protobus
|
||||
@@ -20,11 +20,3 @@ coverage
|
||||
!.github/scripts/coverage/
|
||||
|
||||
*evals.env
|
||||
|
||||
# Generated proto files
|
||||
src/shared/proto/*.ts
|
||||
src/core/controller/*/methods.ts
|
||||
src/core/controller/*/index.ts
|
||||
src/core/controller/grpc-service-config.ts
|
||||
webview-ui/src/services/grpc-client.ts
|
||||
src/standalone/server-setup.ts
|
||||
|
||||
@@ -1,53 +1,5 @@
|
||||
# Changelog
|
||||
|
||||
## [3.17.5]
|
||||
|
||||
- Fix issue with Claude 4 models where after several conversation turns, it would start making invalid diff edits
|
||||
|
||||
## [3.17.4]
|
||||
|
||||
- Fix thinking budget slider for Claude 4
|
||||
|
||||
## [3.17.3]
|
||||
|
||||
- Fix diff edit errors with Claude 4 models
|
||||
|
||||
## [3.17.2]
|
||||
|
||||
- Add support for Claude 4 models (Sonnet 4 and Opus 4) in AWS Bedrock and Vertex AI providers
|
||||
- Add support for global workflows, allowing workflows to be shared across workspaces with local workflows taking precedence
|
||||
- Fix settings page z-index UI issues that caused display problems
|
||||
- Fix AWS Bedrock environment variable handling to properly restore process.env after API calls (Thanks @DaveFres!)
|
||||
|
||||
## [3.17.1]
|
||||
|
||||
- Add prompt caching for Claude 4 models on Cline and OpenRouter providers
|
||||
- Increase max tokens for Claude Opus 4 from 4096 to 8192
|
||||
|
||||
## [3.17.0]
|
||||
|
||||
- Add support for Anthropic Claude Sonnet 4 and Claude Opus 4 in both Anthropic and Vertex providers
|
||||
- Add integration with Nebius AI Studio as a new provider (Thanks @Aktsvigun!)
|
||||
- Add custom highlight and hotkey suggestion when the assistant prompts to switch to Act mode
|
||||
- Update settings page design, now split into tabs for easier navigation (Thanks Yellow Bat @dlab-anton, and Roo Team!)
|
||||
- Fix MCP Server configuration bug
|
||||
- Fix model listing for Requesty provider
|
||||
- Move all advanced settings to settings page
|
||||
|
||||
## [3.16.3]
|
||||
|
||||
- Add devstral-small-2505 to the Mistral model list, a new specialized coding model from Mistral AI (Thanks @BarreiroT!)
|
||||
- Add documentation links to rules & workflows UI
|
||||
- Add support for Streameable HTTP Transport for MCPs (Thanks @alejandropta!)
|
||||
- Improve error handling for Mistral SDK API
|
||||
|
||||
## [3.16.2]
|
||||
|
||||
- Add support for Gemini 2.5 Flash Preview 05-20 model to Vertex AI provider with massive 1M token context window (Thanks @omercelik!)
|
||||
- Add keyboard shortcut (Cmd+') to quickly focus Cline from anywhere in VS Code
|
||||
- Add lightbulb actions for selected text with options to "Add to Cline", "Explain with Cline", and "Improve with Cline"
|
||||
- Automatically focus Cline window after extension updates
|
||||
|
||||
## [3.16.1]
|
||||
|
||||
- Add Enable auto approve toggle switch, allowing users to easily turn auto-approve functionality on or off without losing their action settings
|
||||
|
||||
@@ -134,6 +134,14 @@ const baseConfig = {
|
||||
aliasResolverPlugin,
|
||||
/* add to the end of plugins array */
|
||||
esbuildProblemMatcherPlugin,
|
||||
{
|
||||
name: "alias-plugin",
|
||||
setup(build) {
|
||||
build.onResolve({ filter: /^pkce-challenge$/ }, (args) => {
|
||||
return { path: require.resolve("pkce-challenge/dist/index.browser.js") }
|
||||
})
|
||||
},
|
||||
},
|
||||
],
|
||||
format: "cjs",
|
||||
sourcesContent: false,
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
|
||||
التقى Cline، مساعد الذكاء الاصطناعي الذي يمكنه استخدام **سطر الأوامر** و **محرر النصوص** الخاص بك.
|
||||
|
||||
بفضل [قدرات Claude 4 Sonnet على التعليمات البرمجية الوكيلة](https://www.anthropic.com/claude/sonnet)، يمكن لـ Cline التعامل مع مهام تطوير البرامج المعقدة خطوة بخطوة. مع الأدوات التي تسمح له بإنشاء وتعديل الملفات، واستكشاف المشاريع الكبيرة، واستخدام المتصفح، وتنفيذ أوامر الطرفية (بعد منحك الإذن)، يمكنه مساعدتك بطرق تتجاوز إكمال الكود أو الدعم الفني. يمكن لـ Cline أيضًا استخدام بروتوكول سياق النموذج (MCP) لإنشاء أدوات جديدة وتوسيع قدراته الخاصة. في حين تعمل النصوص البرمجية الآلية المستقلة تقليديًا في بيئات محاصرة، توفر هذه الإضافة واجهة رسومية لموافقة المستخدم على كل تغيير في الملف وأمر طرفية، مما يوفر طريقة آمنة وسهلة الاستخدام لاستكشاف إمكانات الذكاء الاصطناعي الوكيل.
|
||||
بفضل [قدرات Claude 3.7 Sonnet على التعليمات البرمجية الوكيلة](https://www.anthropic.com/claude/sonnet)، يمكن لـ Cline التعامل مع مهام تطوير البرامج المعقدة خطوة بخطوة. مع الأدوات التي تسمح له بإنشاء وتعديل الملفات، واستكشاف المشاريع الكبيرة، واستخدام المتصفح، وتنفيذ أوامر الطرفية (بعد منحك الإذن)، يمكنه مساعدتك بطرق تتجاوز إكمال الكود أو الدعم الفني. يمكن لـ Cline أيضًا استخدام بروتوكول سياق النموذج (MCP) لإنشاء أدوات جديدة وتوسيع قدراته الخاصة. في حين تعمل النصوص البرمجية الآلية المستقلة تقليديًا في بيئات محاصرة، توفر هذه الإضافة واجهة رسومية لموافقة المستخدم على كل تغيير في الملف وأمر طرفية، مما يوفر طريقة آمنة وسهلة الاستخدام لاستكشاف إمكانات الذكاء الاصطناعي الوكيل.
|
||||
|
||||
1. أدخل مهمتك وأضف الصور لتحويل المحاكاة إلى تطبيقات وظيفية أو إصلاح الأخطاء مع لقطات الشاشة.
|
||||
2. يبدأ Cline بتحليل هيكل الملفات الخاصة بك وشجرة التعريف المصدرية، وإجراء عمليات بحث regex، وقراءة الملفات ذات الصلة للاطلاع على المشاريع الحالية. من خلال إدارة المعلومات التي يتم إضافتها إلى السياق بعناية، يمكن لـ Cline تقديم مساعدة قيمة حتى للمشاريع الكبيرة والمعقدة دون إرهاق نافذة السياق.
|
||||
@@ -87,7 +87,7 @@
|
||||
|
||||
### استخدم المتصفح
|
||||
|
||||
مع قدرة [استخدام الكمبيوتر](https://www.anthropic.com/news/3-5-models-and-computer-use) الجديدة لـ Claude 4 Sonnet، يمكن لـ Cline إطلاق متصفح، والنقر على العناصر، وكتابة النص، والتمرير، والتقاط لقطات الشاشة وسجلات وحدة التحكم في كل خطوة. يسمح له هذا بالتصحيح التفاعلي، واختبار نهاية إلى نهاية، وحتى الاستخدام العام للويب! يمنحه هذا الاستقلالية لإصلاح الأخطاء البصرية وأخطاء وقت التشغيل دون الحاجة إلى نسخ ولصق سجلات الأخطاء بنفسك.
|
||||
مع قدرة [استخدام الكمبيوتر](https://www.anthropic.com/news/3-5-models-and-computer-use) الجديدة لـ Claude 3.5 Sonnet، يمكن لـ Cline إطلاق متصفح، والنقر على العناصر، وكتابة النص، والتمرير، والتقاط لقطات الشاشة وسجلات وحدة التحكم في كل خطوة. يسمح له هذا بالتصحيح التفاعلي، واختبار نهاية إلى نهاية، وحتى الاستخدام العام للويب! يمنحه هذا الاستقلالية لإصلاح الأخطاء البصرية وأخطاء وقت التشغيل دون الحاجة إلى نسخ ولصق سجلات الأخطاء بنفسك.
|
||||
|
||||
حاول طلب من Cline "اختبار التطبيق"، وشاهده يشغل أمرًا مثل `npm run dev`، ويطلق خادم التطوير المحلي في متصفح، ويجري سلسلة من الاختبارات للتأكد من أن كل شيء يعمل. [شاهد عرضًا توضيحيًا هنا.](https://x.com/sdrzn/status/1850880547825823989)
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
|
||||
Lernen Sie Cline kennen, einen KI-Assistenten, der Ihre **CLI** u**N**d **E**ditor nutzen kann.
|
||||
|
||||
Dank der [agentischen Codierungsfähigkeiten von Claude 4 Sonnet](https://www.anthropic.com/claude/sonnet) kann Cline komplexe Softwareentwicklungsaufgaben Schritt für Schritt bewältigen. Mit Werkzeugen, die ihm das Erstellen und Bearbeiten von Dateien, das Erkunden großer Projekte, die Nutzung des Browsers und das Ausführen von Terminalbefehlen (nach Ihrer Genehmigung) ermöglichen, kann er Ihnen auf eine Weise helfen, die über die Codevervollständigung oder technischen Support hinausgeht. Cline kann sogar das Model Context Protocol (MCP) verwenden, um neue Werkzeuge zu erstellen und seine eigenen Fähigkeiten zu erweitern. Während autonome KI-Skripte traditionell in sandboxed Umgebungen laufen, bietet diese Erweiterung eine Mensch-in-der-Schleife-GUI, um jede Dateiänderung und jeden Terminalbefehl zu genehmigen, was eine sichere und zugängliche Möglichkeit bietet, das Potenzial agentischer KI zu erkunden.
|
||||
Dank der [agentischen Codierungsfähigkeiten von Claude 3.7 Sonnet](https://www.anthropic.com/claude/sonnet) kann Cline komplexe Softwareentwicklungsaufgaben Schritt für Schritt bewältigen. Mit Werkzeugen, die ihm das Erstellen und Bearbeiten von Dateien, das Erkunden großer Projekte, die Nutzung des Browsers und das Ausführen von Terminalbefehlen (nach Ihrer Genehmigung) ermöglichen, kann er Ihnen auf eine Weise helfen, die über die Codevervollständigung oder technischen Support hinausgeht. Cline kann sogar das Model Context Protocol (MCP) verwenden, um neue Werkzeuge zu erstellen und seine eigenen Fähigkeiten zu erweitern. Während autonome KI-Skripte traditionell in sandboxed Umgebungen laufen, bietet diese Erweiterung eine Mensch-in-der-Schleife-GUI, um jede Dateiänderung und jeden Terminalbefehl zu genehmigen, was eine sichere und zugängliche Möglichkeit bietet, das Potenzial agentischer KI zu erkunden.
|
||||
|
||||
1. Geben Sie Ihre Aufgabe ein und fügen Sie Bilder hinzu, um Mockups in funktionale Apps zu konvertieren oder Fehler mit Screenshots zu beheben.
|
||||
2. Cline beginnt mit der Analyse Ihrer Dateistruktur und Quellcode-ASTs, führt Regex-Suchen durch und liest relevante Dateien, um sich in bestehenden Projekten zurechtzufinden. Durch sorgfältiges Management der hinzugefügten Informationen kann Cline wertvolle Unterstützung auch bei großen, komplexen Projekten bieten, ohne das Kontextfenster zu überladen.
|
||||
@@ -83,7 +83,7 @@ Alle von Cline vorgenommenen Änderungen werden in der Timeline Ihrer Datei aufg
|
||||
|
||||
### Den Browser verwenden
|
||||
|
||||
Mit der neuen [Computer Use](https://www.anthropic.com/news/3-5-models-and-computer-use) Fähigkeit von Claude 4 Sonnet kann Cline einen Browser starten, Elemente anklicken, Text eingeben und scrollen, dabei Screenshots und Konsolenprotokolle bei jedem Schritt erfassen. Dies ermöglicht interaktives Debugging, End-to-End-Tests und sogar allgemeine Webnutzung! Dies gibt ihm die Autonomie, visuelle Fehler und Laufzeitprobleme zu beheben, ohne dass Sie selbst Fehlerprotokolle kopieren und einfügen müssen.
|
||||
Mit der neuen [Computer Use](https://www.anthropic.com/news/3-5-models-and-computer-use) Fähigkeit von Claude 3.5 Sonnet kann Cline einen Browser starten, Elemente anklicken, Text eingeben und scrollen, dabei Screenshots und Konsolenprotokolle bei jedem Schritt erfassen. Dies ermöglicht interaktives Debugging, End-to-End-Tests und sogar allgemeine Webnutzung! Dies gibt ihm die Autonomie, visuelle Fehler und Laufzeitprobleme zu beheben, ohne dass Sie selbst Fehlerprotokolle kopieren und einfügen müssen.
|
||||
|
||||
Versuchen Sie, Cline zu bitten, "die App zu testen", und sehen Sie zu, wie er einen Befehl wie `npm run dev` ausführt, Ihren lokal laufenden Dev-Server in einem Browser startet und eine Reihe von Tests durchführt, um zu bestätigen, dass alles funktioniert. [Sehen Sie sich hier eine Demo an.](https://x.com/sdrzn/status/1850880547825823989)
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
|
||||
Conozca a Cline, un asistente de IA que puede usar su **CLI** y **E**ditor.
|
||||
|
||||
Gracias a las [habilidades de codificación agencial de Claude 4 Sonnet](https://www.anthropic.com/claude/sonnet), Cline puede abordar tareas complejas de desarrollo de software paso a paso. Con herramientas que le permiten crear y editar archivos, explorar grandes proyectos, usar el navegador y ejecutar comandos de terminal (con su aprobación), puede ayudarle de una manera que va más allá de la autocompletación de código o el soporte técnico. Cline incluso puede usar el Model Context Protocol (MCP) para crear nuevas herramientas y expandir sus propias capacidades. Mientras que los scripts de IA autónomos tradicionalmente se ejecutan en entornos aislados, esta extensión ofrece una GUI con un humano en el bucle para aprobar cada cambio de archivo y comando de terminal, proporcionando una forma segura y accesible de explorar el potencial de la IA agencial.
|
||||
Gracias a las [habilidades de codificación agencial de Claude 3.7 Sonnet](https://www.anthropic.com/claude/sonnet), Cline puede abordar tareas complejas de desarrollo de software paso a paso. Con herramientas que le permiten crear y editar archivos, explorar grandes proyectos, usar el navegador y ejecutar comandos de terminal (con su aprobación), puede ayudarle de una manera que va más allá de la autocompletación de código o el soporte técnico. Cline incluso puede usar el Model Context Protocol (MCP) para crear nuevas herramientas y expandir sus propias capacidades. Mientras que los scripts de IA autónomos tradicionalmente se ejecutan en entornos aislados, esta extensión ofrece una GUI con un humano en el bucle para aprobar cada cambio de archivo y comando de terminal, proporcionando una forma segura y accesible de explorar el potencial de la IA agencial.
|
||||
|
||||
1. Ingrese su tarea y agregue imágenes para convertir maquetas en aplicaciones funcionales o solucionar errores con capturas de pantalla.
|
||||
2. Cline comenzará analizando su estructura de archivos y ASTs de código fuente, realizando búsquedas Regex y leyendo archivos relevantes para orientarse en proyectos existentes. Al gestionar cuidadosamente la información agregada, Cline puede proporcionar asistencia valiosa incluso en proyectos grandes y complejos sin sobrecargar la ventana de contexto.
|
||||
@@ -83,7 +83,7 @@ Todos los cambios realizados por Cline se registran en la línea de tiempo de su
|
||||
|
||||
### Usar el navegador
|
||||
|
||||
Con la nueva [habilidad de uso de computadora](https://www.anthropic.com/news/3-5-models-and-computer-use) de Claude 4 Sonnet, Cline puede iniciar un navegador, hacer clic en elementos, escribir texto y desplazarse, capturando capturas de pantalla y registros de consola. Esto permite la depuración interactiva, pruebas de extremo a extremo e incluso el uso general de la web. Esto le da la autonomía para solucionar errores visuales y problemas de tiempo de ejecución sin que tenga que copiar y pegar registros de errores.
|
||||
Con la nueva [habilidad de uso de computadora](https://www.anthropic.com/news/3-5-models-and-computer-use) de Claude 3.5 Sonnet, Cline puede iniciar un navegador, hacer clic en elementos, escribir texto y desplazarse, capturando capturas de pantalla y registros de consola. Esto permite la depuración interactiva, pruebas de extremo a extremo e incluso el uso general de la web. Esto le da la autonomía para solucionar errores visuales y problemas de tiempo de ejecución sin que tenga que copiar y pegar registros de errores.
|
||||
|
||||
Intente pedirle a Cline que "pruebe la aplicación" y observe cómo ejecuta un comando como `npm run dev`, inicia su servidor de desarrollo local en un navegador y realiza una serie de pruebas para confirmar que todo funciona. [Vea una demostración aquí.](https://x.com/sdrzn/status/1850880547825823989)
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
|
||||
Clineは、**CLI**と**エディター**を使用できるAIアシスタントです。
|
||||
|
||||
[Claude 4 Sonnetのエージェント的コーディング機能](https://www.anthropic.com/claude/sonnet)のおかげで、Clineは複雑なソフトウェア開発タスクをステップバイステップで処理できます。ファイルの作成と編集、大規模プロジェクトの探索、ブラウザの使用、ターミナルコマンドの実行(許可後)などのツールを使用して、コード補完や技術サポートを超えた支援を提供します。Clineは、Model Context Protocol (MCP)を使用して新しいツールを作成し、自身の機能を拡張することもできます。自律的なAIスクリプトは通常サンドボックス環境で実行されますが、この拡張機能はファイル変更やターミナルコマンドを承認するための人間インターフェースを提供し、エージェント的AIの可能性を安全かつアクセスしやすい方法で探求できます。
|
||||
[Claude 3.7 Sonnetのエージェント的コーディング機能](https://www.anthropic.com/claude/sonnet)のおかげで、Clineは複雑なソフトウェア開発タスクをステップバイステップで処理できます。ファイルの作成と編集、大規模プロジェクトの探索、ブラウザの使用、ターミナルコマンドの実行(許可後)などのツールを使用して、コード補完や技術サポートを超えた支援を提供します。Clineは、Model Context Protocol (MCP)を使用して新しいツールを作成し、自身の機能を拡張することもできます。自律的なAIスクリプトは通常サンドボックス環境で実行されますが、この拡張機能はファイル変更やターミナルコマンドを承認するための人間インターフェースを提供し、エージェント的AIの可能性を安全かつアクセスしやすい方法で探求できます。
|
||||
|
||||
1. タスクを入力し、モックアップを機能するアプリに変換したり、スクリーンショットでバグを修正したりします。
|
||||
2. Clineは、ファイル構造とソースコードASTの分析、正規表現検索の実行、関連ファイルの読み取りから始め、既存プロジェクトに精通します。コンテキストに追加される情報を慎重に管理することで、大規模で複雑なプロジェクトでもコンテキストウィンドウを圧倒することなく貴重な支援を提供できます。
|
||||
@@ -83,7 +83,7 @@ Clineによるすべての変更はファイルのタイムラインに記録さ
|
||||
|
||||
### ブラウザの使用
|
||||
|
||||
Claude 4 Sonnetの新しい[コンピュータ使用](https://www.anthropic.com/news/3-5-models-and-computer-use)機能により、Clineはブラウザを起動し、要素をクリック、テキストを入力、スクロールし、各ステップでスクリーンショットとコンソールログをキャプチャできます。これにより、インタラクティブなデバッグ、エンドツーエンドテスト、さらには一般的なウェブ使用が可能になります。これにより、エラーログを手動でコピー&ペーストすることなく、視覚的なバグやランタイムの問題を自律的に修正できます。
|
||||
Claude 3.5 Sonnetの新しい[コンピュータ使用](https://www.anthropic.com/news/3-5-models-and-computer-use)機能により、Clineはブラウザを起動し、要素をクリック、テキストを入力、スクロールし、各ステップでスクリーンショットとコンソールログをキャプチャできます。これにより、インタラクティブなデバッグ、エンドツーエンドテスト、さらには一般的なウェブ使用が可能になります。これにより、エラーログを手動でコピー&ペーストすることなく、視覚的なバグやランタイムの問題を自律的に修正できます。
|
||||
|
||||
Clineに「アプリをテストして」と頼んでみてください。彼は`npm run dev`のようなコマンドを実行し、ローカルで実行中の開発サーバーをブラウザで起動し、一連のテストを実行してすべてが正常に動作することを確認します。[デモはこちら。](https://x.com/sdrzn/status/1850880547825823989)
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
|
||||
Cline을 만나보세요, **CLI** 및 **에디터**를 활용할 수 있는 AI 어시스턴트입니다.
|
||||
|
||||
[Claude 4 Sonnet의 에이전트형 코딩 기능](https://www.anthropic.com/claude/sonnet) 덕분에, Cline은 복잡한 소프트웨어 개발 작업을 단계별로 처리할 수 있습니다. 파일 생성과 편집, 대규모 프로젝트 탐색, 브라우저 사용, 터미널 명령 실행(권한 허가 필요) 등의 도구를 사용하여 단순 코드 완성이나 기술 지원을 넘어서는 도움을 제공합니다. Cline은 Model Context Protocol(MCP)를 사용하여 새로운 도구를 만들고 자신의 기능을 확장할 수도 있습니다. 자율적인 AI 스크립트는 일반적으로 샌드박스 환경에서 실행되지만, 이 확장 프로그램은 모든 파일 변경 및 터미널 명령을 승인할 수 있는 사람이 개입가능한 GUI를 제공하여, 에이전트형 AI의 잠재력을 보다 안전하고 쉽게 탐색할 수 있도록 합니다.
|
||||
[Claude 3.7 Sonnet의 에이전트형 코딩 기능](https://www.anthropic.com/claude/sonnet) 덕분에, Cline은 복잡한 소프트웨어 개발 작업을 단계별로 처리할 수 있습니다. 파일 생성과 편집, 대규모 프로젝트 탐색, 브라우저 사용, 터미널 명령 실행(권한 허가 필요) 등의 도구를 사용하여 단순 코드 완성이나 기술 지원을 넘어서는 도움을 제공합니다. Cline은 Model Context Protocol(MCP)를 사용하여 새로운 도구를 만들고 자신의 기능을 확장할 수도 있습니다. 자율적인 AI 스크립트는 일반적으로 샌드박스 환경에서 실행되지만, 이 확장 프로그램은 모든 파일 변경 및 터미널 명령을 승인할 수 있는 사람이 개입가능한 GUI를 제공하여, 에이전트형 AI의 잠재력을 보다 안전하고 쉽게 탐색할 수 있도록 합니다.
|
||||
|
||||
1. 작업을 입력하고, 목업을 기능하는 앱으로 변환하거나 스크린샷으로 버그를 수정합니다.
|
||||
2. Cline은 파일 구조와 소스코드 AST의 분석, 정규식 검색 실행, 관련 파일 읽기부터 시작하여 기존 프로젝트를 파악합니다. 또한, 어떤 정보를 컨텍스트에 추가할지를 신중하게 관리하여, 대규모 복잡한 프로젝트에서도 컨텍스트 윈도우를 과부하시키지 않으면서도 효과적인 지원을 제공합니다.
|
||||
@@ -78,7 +78,7 @@ Cline에 의한 모든 변경은 파일의 타임라인에 기록되어 필요
|
||||
|
||||
### 브라우저 사용
|
||||
|
||||
Claude 4 Sonnet의 새로운 [컴퓨터 사용](https://www.anthropic.com/news/3-5-models-and-computer-use) 기능으로 인해, Cline은 브라우저를 실행하고 요소를 클릭하고 텍스트를 입력하고 스크롤하며 각 단계에서 스크린샷과 콘솔 로그를 캡처할 수 있습니다. 이를 통해 인터랙티브한 디버깅, 엔드투엔드 테스트, 심지어 일반적인 웹 탐색까지 가능해집니다. 이로 인해 오류 로그를 수동으로 복사 & 붙여넣기 할 필요 없이 시각적 버그나 런타임 문제를 자율적으로 수정할 수 있습니다.
|
||||
Claude 3.5 Sonnet의 새로운 [컴퓨터 사용](https://www.anthropic.com/news/3-5-models-and-computer-use) 기능으로 인해, Cline은 브라우저를 실행하고 요소를 클릭하고 텍스트를 입력하고 스크롤하며 각 단계에서 스크린샷과 콘솔 로그를 캡처할 수 있습니다. 이를 통해 인터랙티브한 디버깅, 엔드투엔드 테스트, 심지어 일반적인 웹 탐색까지 가능해집니다. 이로 인해 오류 로그를 수동으로 복사 & 붙여넣기 할 필요 없이 시각적 버그나 런타임 문제를 자율적으로 수정할 수 있습니다.
|
||||
|
||||
Cline에게 "앱을 테스트해줘"라고 요청하면, `npm run dev`와 같은 명령을 실행하고 로컬에서 실행 중인 개발 서버를 브라우저에서 실행하여 일련의 테스트를 수행하고 모든 것이 정상적으로 작동하는지 확인합니다. [데모는 여기를 참조하세요.](https://x.com/sdrzn/status/1850880547825823989)
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
|
||||
Conheça o Cline: um assistente de IA que pode usar seu **CLI** e **Editor**.
|
||||
|
||||
Graças às [habilidades avançadas do Claude 4 Sonnet](https://www.anthropic.com/claude/sonnet), o Cline pode lidar com tarefas complexas de desenvolvimento de software passo a passo. Com ferramentas que permitem criar e editar arquivos, explorar grandes projetos, usar o navegador e executar comandos no terminal (com sua aprovação), ele pode ajudar você de maneiras que vão além da inclusão de código ou suporte técnico. O Cline pode é capaz inclusive de usar o Model Context Protocol (MCP) para criar novas ferramentas e expandir seus próprios recursos. Embora os scripts de IA autônomas tradicionalmente sejam executados em ambientes isolados, esta extensão oferece uma GUI com um humano no circuito para aprovar cada alteração de arquivo e comando de terminal, fornecendo uma maneira segura e acessível de explorar todo o potencial da IA.
|
||||
Graças às [habilidades avançadas do Claude 3.7 Sonnet](https://www.anthropic.com/claude/sonnet), o Cline pode lidar com tarefas complexas de desenvolvimento de software passo a passo. Com ferramentas que permitem criar e editar arquivos, explorar grandes projetos, usar o navegador e executar comandos no terminal (com sua aprovação), ele pode ajudar você de maneiras que vão além da inclusão de código ou suporte técnico. O Cline pode é capaz inclusive de usar o Model Context Protocol (MCP) para criar novas ferramentas e expandir seus próprios recursos. Embora os scripts de IA autônomas tradicionalmente sejam executados em ambientes isolados, esta extensão oferece uma GUI com um humano no circuito para aprovar cada alteração de arquivo e comando de terminal, fornecendo uma maneira segura e acessível de explorar todo o potencial da IA.
|
||||
|
||||
1. Insira sua tarefa e adicione imagens para transformar mockups em aplicativos funcionais ou corrigir erros através de capturas de tela.
|
||||
|
||||
@@ -83,7 +83,7 @@ Todas as alterações feitas pelo Cline são registradas na Linha do tempo do ar
|
||||
|
||||
### Uso do navegador
|
||||
|
||||
Com a nova habilidade de [uso de computador](https://www.anthropic.com/news/3-5-models-and-computer-use) do Claude Sonnet 4, Cline pode abrir um navegador, clicar em elementos, digitar texto e rolar, capturando a tela e logs de console. Isso permite depurar de maneira interativa, testes end-to-end e até mesmo uso geral da web. Isso lhe dá autonomia para solucionar erros visuais e problemas em tempo de execução sem precisar copiar e colar logs dos erros.
|
||||
Com a nova habilidade de [uso de computador](https://www.anthropic.com/news/3-5-models-and-computer-use) do Claude Sonnet 3.5, Cline pode abrir um navegador, clicar em elementos, digitar texto e rolar, capturando a tela e logs de console. Isso permite depurar de maneira interativa, testes end-to-end e até mesmo uso geral da web. Isso lhe dá autonomia para solucionar erros visuais e problemas em tempo de execução sem precisar copiar e colar logs dos erros.
|
||||
|
||||
Tente pedir a Cline para "testar o aplicativo" e observe enquanto o Cline executa um comando como `npm run dev`, inicia seu servidor de desenvolvimento local em um navegador e executa uma série de testes para confirmar se tudo funciona. [Veja uma demonstração aqui.](https://x.com/sdrzn/status/1850880547825823989)
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
|
||||
认识 Cline —— 一个可以使用你的 **终端** 和 **编辑器** 的 AI 助手。
|
||||
|
||||
得益于 [Claude 4 Sonnet 的代理式编码能力](https://www.anthropic.com/claude/sonnet),Cline 能够逐步处理复杂的软件开发任务。借助于一系列工具,他可以创建和编辑文件、浏览大型项目、使用浏览器,并在你授权后执行终端命令,从而在代码补全或技术支持之外提供更深入的帮助。Cline 甚至还能使用 Model Context Protocol(MCP)来创建新工具,并扩展自身的能力。虽然传统的自动化 AI 脚本通常运行在沙盒环境中,但这个扩展提供了一个人类参与审核的图形界面(GUI),用于审批每一次文件变更和终端命令,从而为探索代理式 AI 的潜力提供了一种安全且易于使用的方式。
|
||||
得益于 [Claude 3.7 Sonnet 的代理式编码能力](https://www.anthropic.com/claude/sonnet),Cline 能够逐步处理复杂的软件开发任务。借助于一系列工具,他可以创建和编辑文件、浏览大型项目、使用浏览器,并在你授权后执行终端命令,从而在代码补全或技术支持之外提供更深入的帮助。Cline 甚至还能使用 Model Context Protocol(MCP)来创建新工具,并扩展自身的能力。虽然传统的自动化 AI 脚本通常运行在沙盒环境中,但这个扩展提供了一个人类参与审核的图形界面(GUI),用于审批每一次文件变更和终端命令,从而为探索代理式 AI 的潜力提供了一种安全且易于使用的方式。
|
||||
|
||||
1. 输入你的任务,并添加图片,以将界面原型(mockup)转换为功能应用,或通过截图修复 bug。
|
||||
2. Cline 会从分析你的文件结构和源代码的抽象语法树(AST)开始,同时执行正则搜索并读取相关文件,以便尽快熟悉项目上下文。通过精细地管理上下文中引入的信息,即使面对大型复杂项目,Cline 也能在不超出上下文窗口限制的前提下提供有效协助。
|
||||
@@ -83,7 +83,7 @@ Cline 所做的所有更改都会记录在你的文件时间轴中,提供了
|
||||
|
||||
### 使用浏览器
|
||||
|
||||
借助 Claude 4 Sonnet 的新 [计算机使用](https://www.anthropic.com/news/3-5-models-and-computer-use) 功能,Cline 可以启动浏览器,点击元素,输入文本和滚动,在每一步捕获截图和控制台日志。这允许进行交互式调试、端到端测试,甚至是一般的网页使用!这使他能够自主修复视觉错误和运行时问题,而无需你亲自操作和复制粘贴错误日志。
|
||||
借助 Claude 3.5 Sonnet 的新 [计算机使用](https://www.anthropic.com/news/3-5-models-and-computer-use) 功能,Cline 可以启动浏览器,点击元素,输入文本和滚动,在每一步捕获截图和控制台日志。这允许进行交互式调试、端到端测试,甚至是一般的网页使用!这使他能够自主修复视觉错误和运行时问题,而无需你亲自操作和复制粘贴错误日志。
|
||||
|
||||
试试让 Cline “测试应用程序”,看看他如何运行 `npm run dev` 命令,在浏览器中启动你本地运行的开发服务器,并执行一系列测试以确认一切正常。[在这里查看演示。](https://x.com/sdrzn/status/1850880547825823989)
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
|
||||
認識 Cline,一個可以使用您的**命令列介面** (CLI) 和**程式編輯器** (Editor) 的 AI 助理。
|
||||
|
||||
感謝 [Claude 4 Sonnet 的代理式程式設計能力](https://www.anthropic.com/claude/sonnet),Cline 能夠逐步處理複雜的軟體開發任務。透過能讓他建立和編輯檔案、探索大型專案、使用瀏覽器,以及執行終端機指令(在您授權後)的工具,從而在程式碼補全或技術支援之外提供更深入的協助。Cline 甚至能使用模型上下文協定(Model Context Protocol,MCP)來建立新工具並擴展自己的功能。雖然自主 AI 腳本傳統上會在沙箱環境中執行,但這個擴充套件提供了人機互動的圖形介面,讓您可以核准每個檔案變更和終端機指令,提供一個安全且容易使用的方式來探索代理式 AI 的潛力。
|
||||
感謝 [Claude 3.7 Sonnet 的代理式程式設計能力](https://www.anthropic.com/claude/sonnet),Cline 能夠逐步處理複雜的軟體開發任務。透過能讓他建立和編輯檔案、探索大型專案、使用瀏覽器,以及執行終端機指令(在您授權後)的工具,從而在程式碼補全或技術支援之外提供更深入的協助。Cline 甚至能使用模型上下文協定(Model Context Protocol,MCP)來建立新工具並擴展自己的功能。雖然自主 AI 腳本傳統上會在沙箱環境中執行,但這個擴充套件提供了人機互動的圖形介面,讓您可以核准每個檔案變更和終端機指令,提供一個安全且容易使用的方式來探索代理式 AI 的潛力。
|
||||
|
||||
1. 輸入您的任務,並可以加入圖片來將設計稿轉換成功能性應用程式,或使用截圖來修正錯誤。
|
||||
2. Cline 會先分析您的檔案結構和程式碼 AST、執行正規表達式搜尋,並讀取相關檔案,以便在現有專案中快速掌握狀況。透過仔細管理加入上下文的資訊,Cline 可以在不超過上下文視窗的情況下,為大型且複雜的專案提供有價值的協助。
|
||||
@@ -84,7 +84,7 @@ Cline 可以直接在您的編輯器中建立和編輯檔案,並顯示變更
|
||||
|
||||
### 使用瀏覽器
|
||||
|
||||
透過 Claude 4 Sonnet 的新[電腦使用](https://www.anthropic.com/news/3-5-models-and-computer-use)功能,Cline 可以啟動瀏覽器、點選元素、輸入文字和捲動,在每個步驟擷取螢幕截圖和主控台記錄。這讓互動式除錯、端對端測試,甚至一般網頁使用成為可能!這讓他能獨立修正視覺問題和執行時錯誤,而不需要您手動複製錯誤記錄。
|
||||
透過 Claude 3.5 Sonnet 的新[電腦使用](https://www.anthropic.com/news/3-5-models-and-computer-use)功能,Cline 可以啟動瀏覽器、點選元素、輸入文字和捲動,在每個步驟擷取螢幕截圖和主控台記錄。這讓互動式除錯、端對端測試,甚至一般網頁使用成為可能!這讓他能獨立修正視覺問題和執行時錯誤,而不需要您手動複製錯誤記錄。
|
||||
|
||||
試著請 Cline 「測試應用程式」,觀察他如何執行 `npm run dev`、在瀏覽器中啟動您的本機開發伺服器,並執行一系列測試來確認一切正常運作。[點此觀看示範](https://x.com/sdrzn/status/1850880547825823989)。
|
||||
|
||||
@@ -108,13 +108,10 @@ Cline 可以直接在您的編輯器中建立和編輯檔案,並顯示變更
|
||||
|
||||
### 新增上下文
|
||||
|
||||
**`@url`**:貼上網址讓擴充套件擷取並轉換為 Markdown,當您想給 Cline 最新文件時很有用
|
||||
|
||||
**`@problems`**:新增工作區的錯誤和警告(「問題」面板)給 Cline 修正
|
||||
|
||||
**`@file`**:新增檔案內容,讓您不必浪費 API 請求來核准讀取檔案(+ 輸入以搜尋檔案)
|
||||
|
||||
**`@folder`**:一次新增整個資料夾的檔案,讓您的工作流程更快速
|
||||
**`@url`:**貼上網址讓擴充套件擷取並轉換為 Markdown,當您想給 Cline 最新文件時很有用
|
||||
**`@problems`:**新增工作區的錯誤和警告(「問題」面板)給 Cline 修正
|
||||
**`@file`:**新增檔案內容,讓您不必浪費 API 請求來核准讀取檔案(+ 輸入以搜尋檔案)
|
||||
**`@folder`:**一次新增整個資料夾的檔案,讓您的工作流程更快速
|
||||
|
||||
<!-- 透明像素用於浮動圖片後的換行 -->
|
||||
<img width="2000" height="0" src="https://github.com/user-attachments/assets/ee14e6f7-20b8-4391-9091-8e8e25561929"><br>
|
||||
|
||||
Generated
+19
-19
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "claude-dev",
|
||||
"version": "3.17.2",
|
||||
"version": "3.16.1",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "claude-dev",
|
||||
"version": "3.17.2",
|
||||
"version": "3.16.1",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@anthropic-ai/bedrock-sdk": "^0.12.4",
|
||||
@@ -19,7 +19,7 @@
|
||||
"@grpc/grpc-js": "^1.9.15",
|
||||
"@grpc/reflection": "^1.0.4",
|
||||
"@mistralai/mistralai": "^1.5.0",
|
||||
"@modelcontextprotocol/sdk": "^1.11.1",
|
||||
"@modelcontextprotocol/sdk": "^1.7.0",
|
||||
"@opentelemetry/api": "^1.4.1",
|
||||
"@opentelemetry/exporter-trace-otlp-http": "^0.39.1",
|
||||
"@opentelemetry/resources": "^1.30.1",
|
||||
@@ -7600,17 +7600,17 @@
|
||||
"license": "BSD-2-Clause"
|
||||
},
|
||||
"node_modules/@modelcontextprotocol/sdk": {
|
||||
"version": "1.11.1",
|
||||
"resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.11.1.tgz",
|
||||
"integrity": "sha512-9LfmxKTb1v+vUS1/emSk1f5ePmTLkb9Le9AxOB5T0XM59EUumwcS45z05h7aiZx3GI0Bl7mjb3FMEglYj+acuQ==",
|
||||
"version": "1.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.7.0.tgz",
|
||||
"integrity": "sha512-IYPe/FLpvF3IZrd/f5p5ffmWhMc3aEMuM2wGJASDqC2Ge7qatVCdbfPx3n/5xFeb19xN0j/911M2AaFuircsWA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"content-type": "^1.0.5",
|
||||
"cors": "^2.8.5",
|
||||
"cross-spawn": "^7.0.3",
|
||||
"eventsource": "^3.0.2",
|
||||
"express": "^5.0.1",
|
||||
"express-rate-limit": "^7.5.0",
|
||||
"pkce-challenge": "^5.0.0",
|
||||
"pkce-challenge": "^4.1.0",
|
||||
"raw-body": "^3.0.0",
|
||||
"zod": "^3.23.8",
|
||||
"zod-to-json-schema": "^3.24.1"
|
||||
@@ -21465,9 +21465,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/pkce-challenge": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.0.tgz",
|
||||
"integrity": "sha512-ueGLflrrnvwB3xuo/uGob5pd5FN7l0MsLf0Z87o/UQmRtwjvfylfc9MurIxRAWywCYTgrvpXBcqjV4OfCYGCIQ==",
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-4.1.0.tgz",
|
||||
"integrity": "sha512-ZBmhE1C9LcPoH9XZSdwiPtbPHZROwAnMy+kIFQVrnMCxY4Cudlz3gBOpzilgc0jOgRaiT3sIWfpMomW2ar2orQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=16.20.0"
|
||||
}
|
||||
@@ -31508,17 +31509,16 @@
|
||||
"integrity": "sha512-Y28PR25bHXUg88kCV7nivXrP2Nj2RueZ3/l/jdx6J9f8J4nsEGcgX0Qe6lt7Pa+J79+kPiJU3LguR6O/6zrLOw=="
|
||||
},
|
||||
"@modelcontextprotocol/sdk": {
|
||||
"version": "1.11.1",
|
||||
"resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.11.1.tgz",
|
||||
"integrity": "sha512-9LfmxKTb1v+vUS1/emSk1f5ePmTLkb9Le9AxOB5T0XM59EUumwcS45z05h7aiZx3GI0Bl7mjb3FMEglYj+acuQ==",
|
||||
"version": "1.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.7.0.tgz",
|
||||
"integrity": "sha512-IYPe/FLpvF3IZrd/f5p5ffmWhMc3aEMuM2wGJASDqC2Ge7qatVCdbfPx3n/5xFeb19xN0j/911M2AaFuircsWA==",
|
||||
"requires": {
|
||||
"content-type": "^1.0.5",
|
||||
"cors": "^2.8.5",
|
||||
"cross-spawn": "^7.0.3",
|
||||
"eventsource": "^3.0.2",
|
||||
"express": "^5.0.1",
|
||||
"express-rate-limit": "^7.5.0",
|
||||
"pkce-challenge": "^5.0.0",
|
||||
"pkce-challenge": "^4.1.0",
|
||||
"raw-body": "^3.0.0",
|
||||
"zod": "^3.23.8",
|
||||
"zod-to-json-schema": "^3.24.1"
|
||||
@@ -41248,9 +41248,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"pkce-challenge": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.0.tgz",
|
||||
"integrity": "sha512-ueGLflrrnvwB3xuo/uGob5pd5FN7l0MsLf0Z87o/UQmRtwjvfylfc9MurIxRAWywCYTgrvpXBcqjV4OfCYGCIQ=="
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-4.1.0.tgz",
|
||||
"integrity": "sha512-ZBmhE1C9LcPoH9XZSdwiPtbPHZROwAnMy+kIFQVrnMCxY4Cudlz3gBOpzilgc0jOgRaiT3sIWfpMomW2ar2orQ=="
|
||||
},
|
||||
"pony-cause": {
|
||||
"version": "1.1.1",
|
||||
|
||||
+7
-20
@@ -2,7 +2,7 @@
|
||||
"name": "claude-dev",
|
||||
"displayName": "Cline",
|
||||
"description": "Autonomous coding agent right in your IDE, capable of creating/editing files, running commands, using the browser, and more with your permission every step of the way.",
|
||||
"version": "3.17.5",
|
||||
"version": "3.16.1",
|
||||
"icon": "assets/icons/icon.png",
|
||||
"engines": {
|
||||
"vscode": "^1.84.0"
|
||||
@@ -50,21 +50,8 @@
|
||||
"activitybar": [
|
||||
{
|
||||
"id": "claude-dev-ActivityBar",
|
||||
"title": "Cline (⌘+')",
|
||||
"icon": "assets/icons/icon.svg",
|
||||
"when": "isMac"
|
||||
},
|
||||
{
|
||||
"id": "claude-dev-ActivityBar",
|
||||
"title": "Cline (Ctrl+')",
|
||||
"icon": "assets/icons/icon.svg",
|
||||
"when": "isWindows"
|
||||
},
|
||||
{
|
||||
"id": "claude-dev-ActivityBar",
|
||||
"title": "Cline (Ctrl+')",
|
||||
"icon": "assets/icons/icon.svg",
|
||||
"when": "isLinux || !isMac && !isWindows"
|
||||
"title": "Cline",
|
||||
"icon": "assets/icons/icon.svg"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -273,18 +260,18 @@
|
||||
"scripts": {
|
||||
"vscode:prepublish": "npm run package",
|
||||
"compile": "npm run check-types && npm run lint && node esbuild.js",
|
||||
"compile-standalone": "npm run check-types && npm run lint && node esbuild.js --standalone",
|
||||
"compile-standalone": "npm run protos && npm run check-types && npm run lint && node esbuild.js --standalone",
|
||||
"postcompile-standalone": "node scripts/package-standalone.mjs",
|
||||
"watch": "npm-run-all -p watch:*",
|
||||
"watch:esbuild": "node esbuild.js --watch",
|
||||
"watch:tsc": "tsc --noEmit --watch --project tsconfig.json",
|
||||
"package": "npm run check-types && npm run build:webview && npm run lint && node esbuild.js --production",
|
||||
"package": "npm run build:webview && npm run check-types && npm run lint && node esbuild.js --production",
|
||||
"protos": "node proto/build-proto.js && node scripts/generate-server-setup.mjs",
|
||||
"postprotos": "prettier src/shared/proto src/core/controller webview-ui/src/services src/standalone/server-setup.ts --write --log-level silent",
|
||||
"compile-tests": "node ./scripts/build-tests.js",
|
||||
"watch-tests": "tsc -p . -w --outDir out",
|
||||
"pretest": "npm run compile-tests && npm run compile && npm run lint",
|
||||
"check-types": "npm run protos && tsc --noEmit",
|
||||
"check-types": "tsc --noEmit",
|
||||
"lint": "eslint src --ext ts && eslint webview-ui/src --ext ts",
|
||||
"format": "prettier . --check",
|
||||
"format:fix": "prettier . --write",
|
||||
@@ -354,7 +341,7 @@
|
||||
"@grpc/grpc-js": "^1.9.15",
|
||||
"@grpc/reflection": "^1.0.4",
|
||||
"@mistralai/mistralai": "^1.5.0",
|
||||
"@modelcontextprotocol/sdk": "^1.11.1",
|
||||
"@modelcontextprotocol/sdk": "^1.7.0",
|
||||
"@opentelemetry/api": "^1.4.1",
|
||||
"@opentelemetry/exporter-trace-otlp-http": "^0.39.1",
|
||||
"@opentelemetry/resources": "^1.30.1",
|
||||
|
||||
@@ -46,19 +46,6 @@ service FileService {
|
||||
|
||||
// Toggle a Windsurf rule (enable or disable)
|
||||
rpc toggleWindsurfRule(ToggleWindsurfRuleRequest) returns (ClineRulesToggles);
|
||||
|
||||
// Refreshes all rule toggles (Cline, External, and Workflows)
|
||||
rpc refreshRules(EmptyRequest) returns (RefreshedRules);
|
||||
}
|
||||
|
||||
// Response for refreshRules operation
|
||||
message RefreshedRules {
|
||||
ClineRulesToggles global_cline_rules_toggles = 1;
|
||||
ClineRulesToggles local_cline_rules_toggles = 2;
|
||||
ClineRulesToggles local_cursor_rules_toggles = 3;
|
||||
ClineRulesToggles local_windsurf_rules_toggles = 4;
|
||||
ClineRulesToggles local_workflow_toggles = 5;
|
||||
ClineRulesToggles global_workflow_toggles = 6;
|
||||
}
|
||||
|
||||
// Request to toggle a Windsurf rule
|
||||
|
||||
@@ -10,7 +10,6 @@ service StateService {
|
||||
rpc resetState(EmptyRequest) returns (Empty);
|
||||
rpc togglePlanActMode(TogglePlanActModeRequest) returns (Empty);
|
||||
rpc updateTerminalConnectionTimeout(Int64Request) returns (Int64);
|
||||
rpc updateAutoApprovalSettings(AutoApprovalSettingsRequest) returns (Empty);
|
||||
}
|
||||
|
||||
message State {
|
||||
@@ -38,26 +37,3 @@ message ChatContent {
|
||||
optional string message = 1;
|
||||
repeated string images = 2;
|
||||
}
|
||||
|
||||
// Message for auto approval settings
|
||||
message AutoApprovalSettingsRequest {
|
||||
Metadata metadata = 1;
|
||||
|
||||
message Actions {
|
||||
bool read_files = 1;
|
||||
bool read_files_externally = 2;
|
||||
bool edit_files = 3;
|
||||
bool edit_files_externally = 4;
|
||||
bool execute_safe_commands = 5;
|
||||
bool execute_all_commands = 6;
|
||||
bool use_browser = 7;
|
||||
bool use_mcp = 8;
|
||||
}
|
||||
|
||||
int32 version = 2;
|
||||
bool enabled = 3;
|
||||
Actions actions = 4;
|
||||
int32 max_requests = 5;
|
||||
bool enable_notifications = 6;
|
||||
repeated string favorites = 7;
|
||||
}
|
||||
|
||||
+1
-3
@@ -10,7 +10,5 @@ import "common.proto";
|
||||
service UiService {
|
||||
// Scrolls to a specific settings section in the settings view
|
||||
rpc scrollToSettings(StringRequest) returns (Empty);
|
||||
|
||||
// Marks the current announcement as shown and returns whether an announcement should still be shown
|
||||
rpc onDidShowAnnouncement(EmptyRequest) returns (Boolean);
|
||||
}
|
||||
|
||||
|
||||
@@ -3,8 +3,7 @@ set -eu
|
||||
|
||||
DIR=${1:-src/}
|
||||
DEST_DIR=dist-standalone
|
||||
SDK_DEST=$DEST_DIR/vscode-sdk-uses.txt
|
||||
CSS_DEST=$DEST_DIR/vscode-css-uses.txt
|
||||
DEST=dist-standalone/vscode-uses.txt
|
||||
mkdir -p $DEST_DIR
|
||||
|
||||
{
|
||||
@@ -12,16 +11,8 @@ git grep -h 'vscode\.' $DIR |
|
||||
grep -Ev '//.*vscode' | # remove commented out code
|
||||
sed 's|.*vscode\.|vscode.|'| # remove everything before vscode.
|
||||
sed 's/[^a-zA-Z0-9_.].*$//' | # remove everything after last identifier
|
||||
sort | uniq > $SDK_DEST
|
||||
sort | uniq > $DEST
|
||||
}
|
||||
echo Wrote uses of the vscode SDK to $(realpath $SDK_DEST)
|
||||
|
||||
{
|
||||
grep -rh -- --vscode- webview-ui/build/ |
|
||||
sed 's/--vscode/\n--vscode/g' | # One var per line
|
||||
grep -- --vscode | # Remove lines that don't have vars.
|
||||
sed 's/[),"\\].*$//' | # remove from the end of the var name to the end of the line.
|
||||
sort | uniq > $CSS_DEST
|
||||
}
|
||||
echo Wrote vscode vars used to $(realpath $CSS_DEST)
|
||||
echo Done, wrote uses of the vscode SDK to $(realpath $DEST)
|
||||
|
||||
|
||||
@@ -13,7 +13,6 @@ import { ApiStream, ApiStreamUsageChunk } from "./transform/stream"
|
||||
import { DeepSeekHandler } from "./providers/deepseek"
|
||||
import { RequestyHandler } from "./providers/requesty"
|
||||
import { TogetherHandler } from "./providers/together"
|
||||
import { NebiusHandler } from "./providers/nebius"
|
||||
import { QwenHandler } from "./providers/qwen"
|
||||
import { MistralHandler } from "./providers/mistral"
|
||||
import { DoubaoHandler } from "./providers/doubao"
|
||||
@@ -76,8 +75,6 @@ export function buildApiHandler(configuration: ApiConfiguration): ApiHandler {
|
||||
return new ClineHandler(options)
|
||||
case "litellm":
|
||||
return new LiteLlmHandler(options)
|
||||
case "nebius":
|
||||
return new NebiusHandler(options)
|
||||
case "asksage":
|
||||
return new AskSageHandler(options)
|
||||
case "xai":
|
||||
|
||||
@@ -1,145 +0,0 @@
|
||||
import "should"
|
||||
import { AwsBedrockHandler } from "../bedrock"
|
||||
import { ApiHandlerOptions } from "@shared/api"
|
||||
|
||||
describe("AwsBedrockHandler", () => {
|
||||
describe("withTempEnv", () => {
|
||||
// Store original env vars for cleanup
|
||||
const originalEnv: Record<string, string | undefined> = {}
|
||||
|
||||
beforeEach(() => {
|
||||
// Store original values before each test
|
||||
originalEnv.TEST_VAR = process.env.TEST_VAR
|
||||
originalEnv.ANOTHER_VAR = process.env.ANOTHER_VAR
|
||||
originalEnv.VAR1 = process.env.VAR1
|
||||
originalEnv.VAR2 = process.env.VAR2
|
||||
originalEnv.VAR3 = process.env.VAR3
|
||||
originalEnv.UNDEFINED_VAR = process.env.UNDEFINED_VAR
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
// Restore original values after each test
|
||||
Object.entries(originalEnv).forEach(([key, value]) => {
|
||||
if (value === undefined) {
|
||||
delete process.env[key]
|
||||
} else {
|
||||
process.env[key] = value
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
it("should restore original environment variables after operation", async () => {
|
||||
// Set initial environment
|
||||
process.env.TEST_VAR = "original"
|
||||
process.env.ANOTHER_VAR = "another"
|
||||
|
||||
// Store original values
|
||||
const originalTestVar = process.env.TEST_VAR
|
||||
const originalAnotherVar = process.env.ANOTHER_VAR
|
||||
|
||||
await AwsBedrockHandler["withTempEnv"](
|
||||
() => {
|
||||
process.env.TEST_VAR = "modified"
|
||||
delete process.env.ANOTHER_VAR
|
||||
},
|
||||
async () => {
|
||||
// Verify environment is modified
|
||||
process.env.TEST_VAR!.should.equal("modified")
|
||||
should.not.exist(process.env.ANOTHER_VAR)
|
||||
return "test"
|
||||
},
|
||||
)
|
||||
|
||||
// Verify environment is restored
|
||||
process.env.TEST_VAR!.should.equal(originalTestVar)
|
||||
process.env.ANOTHER_VAR!.should.equal(originalAnotherVar)
|
||||
})
|
||||
|
||||
it("should handle undefined environment variables", async () => {
|
||||
await AwsBedrockHandler["withTempEnv"](
|
||||
() => {
|
||||
delete process.env.UNDEFINED_VAR
|
||||
},
|
||||
async () => {
|
||||
should.not.exist(process.env.UNDEFINED_VAR)
|
||||
return "test"
|
||||
},
|
||||
)
|
||||
|
||||
// Verify undefined variable is not present
|
||||
should.not.exist(process.env.UNDEFINED_VAR)
|
||||
})
|
||||
|
||||
it("should handle errors and still restore environment", async () => {
|
||||
// Set initial environment
|
||||
process.env.TEST_VAR = "original"
|
||||
|
||||
try {
|
||||
await AwsBedrockHandler["withTempEnv"](
|
||||
() => {
|
||||
process.env.TEST_VAR = "modified"
|
||||
},
|
||||
async () => {
|
||||
throw new Error("Test error")
|
||||
},
|
||||
)
|
||||
should.fail(null, null, "Expected error was not thrown", "throw")
|
||||
} catch (error) {
|
||||
;(error as Error).message.should.equal("Test error")
|
||||
}
|
||||
|
||||
// Verify environment is restored even after error
|
||||
process.env.TEST_VAR!.should.equal("original")
|
||||
})
|
||||
|
||||
it("should handle multiple environment variable changes", async () => {
|
||||
// Set initial environment
|
||||
process.env.VAR1 = "original1"
|
||||
process.env.VAR2 = "original2"
|
||||
process.env.VAR3 = "original3"
|
||||
|
||||
// Store original values
|
||||
const originalVar1 = process.env.VAR1
|
||||
const originalVar2 = process.env.VAR2
|
||||
const originalVar3 = process.env.VAR3
|
||||
|
||||
await AwsBedrockHandler["withTempEnv"](
|
||||
() => {
|
||||
process.env.VAR1 = "modified1"
|
||||
process.env.VAR2 = "modified2"
|
||||
delete process.env.VAR3
|
||||
},
|
||||
async () => {
|
||||
// Verify environment is modified
|
||||
process.env.VAR1!.should.equal("modified1")
|
||||
process.env.VAR2!.should.equal("modified2")
|
||||
should.not.exist(process.env.VAR3)
|
||||
return "test"
|
||||
},
|
||||
)
|
||||
|
||||
// Verify environment is restored
|
||||
process.env.VAR1!.should.equal(originalVar1)
|
||||
process.env.VAR2!.should.equal(originalVar2)
|
||||
process.env.VAR3!.should.equal(originalVar3)
|
||||
})
|
||||
|
||||
it("should work with AWS_PROFILE", async () => {
|
||||
process.env["AWS_PROFILE"] = "test-profile"
|
||||
|
||||
const preAWSProfile = process.env["AWS_PROFILE"]
|
||||
|
||||
await AwsBedrockHandler["withTempEnv"](
|
||||
() => {
|
||||
delete process.env["AWS_PROFILE"]
|
||||
},
|
||||
async () => {
|
||||
should.not.exist(process.env["AWS_PROFILE"])
|
||||
return "test"
|
||||
},
|
||||
)
|
||||
|
||||
process.env["AWS_PROFILE"]!.should.equal(preAWSProfile)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -24,15 +24,13 @@ export class AnthropicHandler implements ApiHandler {
|
||||
const modelId = model.id
|
||||
|
||||
const budget_tokens = this.options.thinkingBudgetTokens || 0
|
||||
const reasoningOn = (modelId.includes("3-7") || modelId.includes("4-")) && budget_tokens !== 0 ? true : false
|
||||
const reasoningOn = modelId.includes("3-7") && budget_tokens !== 0 ? true : false
|
||||
|
||||
switch (modelId) {
|
||||
// 'latest' alias does not support cache_control
|
||||
case "claude-sonnet-4-20250514":
|
||||
case "claude-3-7-sonnet-20250219":
|
||||
case "claude-3-5-sonnet-20241022":
|
||||
case "claude-3-5-haiku-20241022":
|
||||
case "claude-opus-4-20250514":
|
||||
case "claude-3-opus-20240229":
|
||||
case "claude-3-haiku-20240307": {
|
||||
/*
|
||||
@@ -98,8 +96,6 @@ export class AnthropicHandler implements ApiHandler {
|
||||
// https://github.com/anthropics/anthropic-sdk-typescript?tab=readme-ov-file#default-headers
|
||||
// https://github.com/anthropics/anthropic-sdk-typescript/commit/c920b77fc67bd839bfeb6716ceab9d7c9bbe7393
|
||||
switch (modelId) {
|
||||
case "claude-sonnet-4-20250514":
|
||||
case "claude-opus-4-20250514":
|
||||
case "claude-3-7-sonnet-20250219":
|
||||
case "claude-3-5-sonnet-20241022":
|
||||
case "claude-3-5-haiku-20241022":
|
||||
|
||||
@@ -47,11 +47,7 @@ export class AwsBedrockHandler implements ApiHandler {
|
||||
}
|
||||
|
||||
const budget_tokens = this.options.thinkingBudgetTokens || 0
|
||||
const reasoningOn =
|
||||
(baseModelId.includes("3-7") || baseModelId.includes("sonnet-4") || baseModelId.includes("opus-4")) &&
|
||||
budget_tokens !== 0
|
||||
? true
|
||||
: false
|
||||
const reasoningOn = baseModelId.includes("3-7") && budget_tokens !== 0 ? true : false
|
||||
|
||||
// Get model info and message indices for caching
|
||||
const userMsgIndices = messages.reduce((acc, msg, index) => (msg.role === "user" ? [...acc, index] : acc), [] as number[])
|
||||
@@ -62,62 +58,56 @@ export class AwsBedrockHandler implements ApiHandler {
|
||||
// initialization, and allowing for session renewal if necessary as well
|
||||
const client = await this.getAnthropicClient()
|
||||
|
||||
// Use withTempEnv to ensure environment variables are properly restored
|
||||
const stream = await AwsBedrockHandler.withTempEnv(
|
||||
() => {
|
||||
// AWS SDK prioritizes AWS_PROFILE over AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY pair
|
||||
// If this is set as an env variable already (ie. from ~/.zshrc) it will override credentials configured by Cline
|
||||
// Temporarily remove AWS_PROFILE to ensure our credentials are used
|
||||
delete process.env["AWS_PROFILE"]
|
||||
},
|
||||
async () => {
|
||||
return await client.messages.create({
|
||||
model: modelId,
|
||||
max_tokens: model.info.maxTokens || 8192,
|
||||
thinking: reasoningOn ? { type: "enabled", budget_tokens: budget_tokens } : undefined,
|
||||
temperature: reasoningOn ? undefined : 0,
|
||||
system: [
|
||||
{
|
||||
text: systemPrompt,
|
||||
type: "text",
|
||||
...(this.options.awsBedrockUsePromptCache === true && {
|
||||
cache_control: { type: "ephemeral" },
|
||||
}),
|
||||
},
|
||||
],
|
||||
messages: messages.map((message, index) => {
|
||||
if (index === lastUserMsgIndex || index === secondLastMsgUserIndex) {
|
||||
return {
|
||||
...message,
|
||||
content:
|
||||
typeof message.content === "string"
|
||||
? [
|
||||
{
|
||||
type: "text",
|
||||
text: message.content,
|
||||
// AWS SDK prioritizes AWS_PROFILE over AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY pair
|
||||
// If this is set as an env variable already (ie. from ~/.zshrc) it will override credentials configured by Cline
|
||||
const previousEnv = process.env
|
||||
delete process.env["AWS_PROFILE"]
|
||||
const stream = await client.messages.create({
|
||||
model: modelId,
|
||||
max_tokens: model.info.maxTokens || 8192,
|
||||
thinking: reasoningOn ? { type: "enabled", budget_tokens: budget_tokens } : undefined,
|
||||
temperature: reasoningOn ? undefined : 0,
|
||||
system: [
|
||||
{
|
||||
text: systemPrompt,
|
||||
type: "text",
|
||||
...(this.options.awsBedrockUsePromptCache === true && {
|
||||
cache_control: { type: "ephemeral" },
|
||||
}),
|
||||
},
|
||||
],
|
||||
messages: messages.map((message, index) => {
|
||||
if (index === lastUserMsgIndex || index === secondLastMsgUserIndex) {
|
||||
return {
|
||||
...message,
|
||||
content:
|
||||
typeof message.content === "string"
|
||||
? [
|
||||
{
|
||||
type: "text",
|
||||
text: message.content,
|
||||
...(this.options.awsBedrockUsePromptCache === true && {
|
||||
cache_control: { type: "ephemeral" },
|
||||
}),
|
||||
},
|
||||
]
|
||||
: message.content.map((content, contentIndex) =>
|
||||
contentIndex === message.content.length - 1
|
||||
? {
|
||||
...content,
|
||||
...(this.options.awsBedrockUsePromptCache === true && {
|
||||
cache_control: { type: "ephemeral" },
|
||||
}),
|
||||
},
|
||||
]
|
||||
: message.content.map((content, contentIndex) =>
|
||||
contentIndex === message.content.length - 1
|
||||
? {
|
||||
...content,
|
||||
...(this.options.awsBedrockUsePromptCache === true && {
|
||||
cache_control: { type: "ephemeral" },
|
||||
}),
|
||||
}
|
||||
: content,
|
||||
),
|
||||
}
|
||||
}
|
||||
return message
|
||||
}),
|
||||
stream: true,
|
||||
})
|
||||
},
|
||||
)
|
||||
}
|
||||
: content,
|
||||
),
|
||||
}
|
||||
}
|
||||
return message
|
||||
}),
|
||||
stream: true,
|
||||
})
|
||||
process.env = previousEnv
|
||||
|
||||
for await (const chunk of stream) {
|
||||
switch (chunk.type) {
|
||||
@@ -307,23 +297,13 @@ export class AwsBedrockHandler implements ApiHandler {
|
||||
}
|
||||
|
||||
private static async withTempEnv<R>(updateEnv: () => void, fn: () => Promise<R>): Promise<R> {
|
||||
const previousEnv = Object.assign({}, process.env)
|
||||
const previousEnv = { ...process.env }
|
||||
|
||||
try {
|
||||
updateEnv()
|
||||
return await fn()
|
||||
} finally {
|
||||
// Restore the previous environment
|
||||
// First clear any new variables that might have been added
|
||||
for (const key in process.env) {
|
||||
if (!(key in previousEnv)) {
|
||||
delete process.env[key]
|
||||
}
|
||||
}
|
||||
// Then restore all previous values
|
||||
for (const key in previousEnv) {
|
||||
process.env[key] = previousEnv[key]
|
||||
}
|
||||
process.env = previousEnv
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,23 +19,13 @@ export class MistralHandler implements ApiHandler {
|
||||
|
||||
@withRetry()
|
||||
async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream {
|
||||
const stream = await this.client.chat
|
||||
.stream({
|
||||
model: this.getModel().id,
|
||||
// max_completion_tokens: this.getModel().info.maxTokens,
|
||||
temperature: 0,
|
||||
messages: [{ role: "system", content: systemPrompt }, ...convertToMistralMessages(messages)],
|
||||
stream: true,
|
||||
})
|
||||
.catch((err) => {
|
||||
// The Mistal SDK uses statusCode instead of status
|
||||
// However, if they introduce status for something, I don't want to override it
|
||||
if ("statusCode" in err && !("status" in err)) {
|
||||
err.status = err.statusCode
|
||||
}
|
||||
|
||||
throw err
|
||||
})
|
||||
const stream = await this.client.chat.stream({
|
||||
model: this.getModel().id,
|
||||
// max_completion_tokens: this.getModel().info.maxTokens,
|
||||
temperature: 0,
|
||||
messages: [{ role: "system", content: systemPrompt }, ...convertToMistralMessages(messages)],
|
||||
stream: true,
|
||||
})
|
||||
|
||||
for await (const chunk of stream) {
|
||||
const delta = chunk.data.choices[0]?.delta
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
import { Anthropic } from "@anthropic-ai/sdk"
|
||||
import OpenAI from "openai"
|
||||
import { withRetry } from "../retry"
|
||||
import { ApiHandler } from "../index"
|
||||
import { convertToOpenAiMessages } from "../transform/openai-format"
|
||||
import { ApiStream } from "../transform/stream"
|
||||
import { convertToR1Format } from "../transform/r1-format"
|
||||
import { nebiusDefaultModelId, nebiusModels, type ModelInfo, type ApiHandlerOptions, type NebiusModelId } from "../../shared/api"
|
||||
|
||||
export class NebiusHandler implements ApiHandler {
|
||||
private client: OpenAI
|
||||
|
||||
constructor(private readonly options: ApiHandlerOptions) {
|
||||
this.client = new OpenAI({
|
||||
baseURL: "https://api.studio.nebius.ai/v1",
|
||||
apiKey: this.options.nebiusApiKey,
|
||||
})
|
||||
}
|
||||
|
||||
@withRetry()
|
||||
async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream {
|
||||
const model = this.getModel()
|
||||
|
||||
const openAiMessages: OpenAI.Chat.ChatCompletionMessageParam[] = model.id.includes("DeepSeek-R1")
|
||||
? convertToR1Format([{ role: "user", content: systemPrompt }, ...messages])
|
||||
: [{ role: "system", content: systemPrompt }, ...convertToOpenAiMessages(messages)]
|
||||
|
||||
const stream = await this.client.chat.completions.create({
|
||||
model: model.id,
|
||||
messages: openAiMessages,
|
||||
temperature: 0,
|
||||
stream: true,
|
||||
stream_options: { include_usage: true },
|
||||
})
|
||||
for await (const chunk of stream) {
|
||||
const delta = chunk.choices[0]?.delta
|
||||
if (delta?.content) {
|
||||
yield {
|
||||
type: "text",
|
||||
text: delta.content,
|
||||
}
|
||||
}
|
||||
|
||||
if (delta && "reasoning_content" in delta && delta.reasoning_content) {
|
||||
yield {
|
||||
type: "reasoning",
|
||||
reasoning: (delta.reasoning_content as string | undefined) || "",
|
||||
}
|
||||
}
|
||||
|
||||
if (chunk.usage) {
|
||||
yield {
|
||||
type: "usage",
|
||||
inputTokens: chunk.usage.prompt_tokens || 0,
|
||||
outputTokens: chunk.usage.completion_tokens || 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getModel(): { id: string; info: ModelInfo } {
|
||||
const modelId = this.options.apiModelId
|
||||
|
||||
if (modelId !== undefined && modelId in nebiusModels) {
|
||||
return { id: modelId, info: nebiusModels[modelId as NebiusModelId] }
|
||||
}
|
||||
return { id: nebiusDefaultModelId, info: nebiusModels[nebiusDefaultModelId] }
|
||||
}
|
||||
}
|
||||
@@ -7,16 +7,6 @@ import { convertToOpenAiMessages } from "@api/transform/openai-format"
|
||||
import { calculateApiCostOpenAI } from "@utils/cost"
|
||||
import { ApiStream } from "@api/transform/stream"
|
||||
|
||||
// Requesty usage includes an extra field for Anthropic use cases.
|
||||
// Safely cast the prompt token details section to the appropriate structure.
|
||||
interface RequestyUsage extends OpenAI.CompletionUsage {
|
||||
prompt_tokens_details?: {
|
||||
caching_tokens?: number
|
||||
cached_tokens?: number
|
||||
}
|
||||
total_cost?: number
|
||||
}
|
||||
|
||||
export class RequestyHandler implements ApiHandler {
|
||||
private options: ApiHandlerOptions
|
||||
private client: OpenAI
|
||||
@@ -51,10 +41,7 @@ export class RequestyHandler implements ApiHandler {
|
||||
thinkingBudget > 0
|
||||
? { thinking: { type: "enabled", budget_tokens: thinkingBudget } }
|
||||
: { thinking: { type: "disabled" } }
|
||||
const thinkingArgs =
|
||||
model.id.includes("claude-3-7-sonnet") || model.id.includes("claude-sonnet-4") || model.id.includes("claude-opus-4")
|
||||
? thinking
|
||||
: {}
|
||||
const thinkingArgs = model.id.includes("claude-3-7-sonnet") ? thinking : {}
|
||||
|
||||
// @ts-ignore-next-line
|
||||
const stream = await this.client.chat.completions.create({
|
||||
@@ -68,8 +55,6 @@ export class RequestyHandler implements ApiHandler {
|
||||
...thinkingArgs,
|
||||
})
|
||||
|
||||
let lastUsage: any = undefined
|
||||
|
||||
for await (const chunk of stream) {
|
||||
const delta = chunk.choices[0]?.delta
|
||||
if (delta?.content) {
|
||||
@@ -86,26 +71,32 @@ export class RequestyHandler implements ApiHandler {
|
||||
}
|
||||
}
|
||||
|
||||
if (chunk.usage) {
|
||||
lastUsage = chunk.usage
|
||||
// Requesty usage includes an extra field for Anthropic use cases.
|
||||
// Safely cast the prompt token details section to the appropriate structure.
|
||||
interface RequestyUsage extends OpenAI.CompletionUsage {
|
||||
prompt_tokens_details?: {
|
||||
caching_tokens?: number
|
||||
cached_tokens?: number
|
||||
}
|
||||
total_cost?: number
|
||||
}
|
||||
}
|
||||
|
||||
if (lastUsage) {
|
||||
const usage = lastUsage as RequestyUsage
|
||||
const inputTokens = usage.prompt_tokens || 0
|
||||
const outputTokens = usage.completion_tokens || 0
|
||||
const cacheWriteTokens = usage.prompt_tokens_details?.caching_tokens || undefined
|
||||
const cacheReadTokens = usage.prompt_tokens_details?.cached_tokens || undefined
|
||||
const totalCost = calculateApiCostOpenAI(model.info, inputTokens, outputTokens, cacheWriteTokens, cacheReadTokens)
|
||||
if (chunk.usage) {
|
||||
const usage = chunk.usage as RequestyUsage
|
||||
const inputTokens = usage.prompt_tokens || 0
|
||||
const outputTokens = usage.completion_tokens || 0
|
||||
const cacheWriteTokens = usage.prompt_tokens_details?.caching_tokens || undefined
|
||||
const cacheReadTokens = usage.prompt_tokens_details?.cached_tokens || undefined
|
||||
const totalCost = calculateApiCostOpenAI(model.info, inputTokens, outputTokens, cacheWriteTokens, cacheReadTokens)
|
||||
|
||||
yield {
|
||||
type: "usage",
|
||||
inputTokens: inputTokens,
|
||||
outputTokens: outputTokens,
|
||||
cacheWriteTokens: cacheWriteTokens,
|
||||
cacheReadTokens: cacheReadTokens,
|
||||
totalCost: totalCost,
|
||||
yield {
|
||||
type: "usage",
|
||||
inputTokens: inputTokens,
|
||||
outputTokens: outputTokens,
|
||||
cacheWriteTokens: cacheWriteTokens,
|
||||
cacheReadTokens: cacheReadTokens,
|
||||
totalCost: totalCost,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,15 +41,10 @@ export class VertexHandler implements ApiHandler {
|
||||
|
||||
// Claude implementation
|
||||
let budget_tokens = this.options.thinkingBudgetTokens || 0
|
||||
const reasoningOn =
|
||||
(modelId.includes("3-7") || modelId.includes("sonnet-4") || modelId.includes("opus-4")) && budget_tokens !== 0
|
||||
? true
|
||||
: false
|
||||
const reasoningOn = modelId.includes("3-7") && budget_tokens !== 0 ? true : false
|
||||
let stream
|
||||
|
||||
switch (modelId) {
|
||||
case "claude-sonnet-4@20250514":
|
||||
case "claude-opus-4@20250514":
|
||||
case "claude-3-7-sonnet@20250219":
|
||||
case "claude-3-5-sonnet-v2@20241022":
|
||||
case "claude-3-5-sonnet@20240620":
|
||||
|
||||
@@ -23,8 +23,6 @@ export async function createOpenRouterStream(
|
||||
// this was initially specifically for claude models (some models may 'support prompt caching' automatically without this)
|
||||
// handles direct model.id match logic
|
||||
switch (model.id) {
|
||||
case "anthropic/claude-sonnet-4":
|
||||
case "anthropic/claude-opus-4":
|
||||
case "anthropic/claude-3.7-sonnet":
|
||||
case "anthropic/claude-3.7-sonnet:beta":
|
||||
case "anthropic/claude-3.7-sonnet:thinking":
|
||||
@@ -81,8 +79,6 @@ export async function createOpenRouterStream(
|
||||
// (models usually default to max tokens allowed)
|
||||
let maxTokens: number | undefined
|
||||
switch (model.id) {
|
||||
case "anthropic/claude-sonnet-4":
|
||||
case "anthropic/claude-opus-4":
|
||||
case "anthropic/claude-3.7-sonnet":
|
||||
case "anthropic/claude-3.7-sonnet:beta":
|
||||
case "anthropic/claude-3.7-sonnet:thinking":
|
||||
@@ -116,8 +112,6 @@ export async function createOpenRouterStream(
|
||||
|
||||
let reasoning: { max_tokens: number } | undefined = undefined
|
||||
switch (model.id) {
|
||||
case "anthropic/claude-sonnet-4":
|
||||
case "anthropic/claude-opus-4":
|
||||
case "anthropic/claude-3.7-sonnet":
|
||||
case "anthropic/claude-3.7-sonnet:beta":
|
||||
case "anthropic/claude-3.7-sonnet:thinking":
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { fileExistsAtPath, isDirectory, readDirectory } from "@utils/fs"
|
||||
import { ensureRulesDirectoryExists, ensureWorkflowsDirectoryExists, GlobalFileNames } from "@core/storage/disk"
|
||||
import { ensureRulesDirectoryExists, GlobalFileNames } from "@core/storage/disk"
|
||||
import { getGlobalState, getWorkspaceState, updateGlobalState, updateWorkspaceState } from "@core/storage/state"
|
||||
import * as path from "path"
|
||||
import fs from "fs/promises"
|
||||
@@ -172,13 +172,9 @@ export const createRuleFile = async (isGlobal: boolean, filename: string, cwd: s
|
||||
try {
|
||||
let filePath: string
|
||||
if (isGlobal) {
|
||||
if (type === "workflow") {
|
||||
const globalClineWorkflowFilePath = await ensureWorkflowsDirectoryExists()
|
||||
filePath = path.join(globalClineWorkflowFilePath, filename)
|
||||
} else {
|
||||
const globalClineRulesFilePath = await ensureRulesDirectoryExists()
|
||||
filePath = path.join(globalClineRulesFilePath, filename)
|
||||
}
|
||||
// global means its implicitly clinerules
|
||||
const globalClineRulesFilePath = await ensureRulesDirectoryExists()
|
||||
filePath = path.join(globalClineRulesFilePath, filename)
|
||||
} else {
|
||||
const localClineRulesFilePath = path.resolve(cwd, GlobalFileNames.clineRules)
|
||||
|
||||
@@ -247,15 +243,9 @@ export async function deleteRuleFile(
|
||||
|
||||
// Update the appropriate toggles
|
||||
if (isGlobal) {
|
||||
if (type === "workflow") {
|
||||
const toggles = ((await getGlobalState(context, "globalWorkflowToggles")) as ClineRulesToggles) || {}
|
||||
delete toggles[rulePath]
|
||||
await updateGlobalState(context, "globalWorkflowToggles", toggles)
|
||||
} else {
|
||||
const toggles = ((await getGlobalState(context, "globalClineRulesToggles")) as ClineRulesToggles) || {}
|
||||
delete toggles[rulePath]
|
||||
await updateGlobalState(context, "globalClineRulesToggles", toggles)
|
||||
}
|
||||
const toggles = ((await getGlobalState(context, "globalClineRulesToggles")) as ClineRulesToggles) || {}
|
||||
delete toggles[rulePath]
|
||||
await updateGlobalState(context, "globalClineRulesToggles", toggles)
|
||||
} else {
|
||||
if (type === "workflow") {
|
||||
const toggles = ((await getWorkspaceState(context, "workflowToggles")) as ClineRulesToggles) || {}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import path from "path"
|
||||
import { GlobalFileNames, ensureWorkflowsDirectoryExists } from "@core/storage/disk"
|
||||
import { GlobalFileNames } from "@core/storage/disk"
|
||||
import { ClineRulesToggles } from "@shared/cline-rules"
|
||||
import { getWorkspaceState, updateWorkspaceState, getGlobalState, updateGlobalState } from "@core/storage/state"
|
||||
import { getWorkspaceState, updateWorkspaceState } from "@core/storage/state"
|
||||
import * as vscode from "vscode"
|
||||
import { synchronizeRuleToggles } from "@core/context/instructions/user-instructions/rule-helpers"
|
||||
|
||||
@@ -11,23 +11,10 @@ import { synchronizeRuleToggles } from "@core/context/instructions/user-instruct
|
||||
export async function refreshWorkflowToggles(
|
||||
context: vscode.ExtensionContext,
|
||||
workingDirectory: string,
|
||||
): Promise<{
|
||||
globalWorkflowToggles: ClineRulesToggles
|
||||
localWorkflowToggles: ClineRulesToggles
|
||||
}> {
|
||||
// Global workflows
|
||||
const globalWorkflowToggles = ((await getGlobalState(context, "globalWorkflowToggles")) as ClineRulesToggles) || {}
|
||||
const globalClineWorkflowsFilePath = await ensureWorkflowsDirectoryExists()
|
||||
const updatedGlobalWorkflowToggles = await synchronizeRuleToggles(globalClineWorkflowsFilePath, globalWorkflowToggles)
|
||||
await updateGlobalState(context, "globalWorkflowToggles", updatedGlobalWorkflowToggles)
|
||||
|
||||
): Promise<ClineRulesToggles> {
|
||||
const workflowRulesToggles = ((await getWorkspaceState(context, "workflowToggles")) as ClineRulesToggles) || {}
|
||||
const workflowsDirPath = path.resolve(workingDirectory, GlobalFileNames.workflows)
|
||||
const updatedWorkflowToggles = await synchronizeRuleToggles(workflowsDirPath, workflowRulesToggles)
|
||||
await updateWorkspaceState(context, "workflowToggles", updatedWorkflowToggles)
|
||||
|
||||
return {
|
||||
globalWorkflowToggles: updatedGlobalWorkflowToggles,
|
||||
localWorkflowToggles: updatedWorkflowToggles,
|
||||
}
|
||||
return updatedWorkflowToggles
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ import { getRelativePaths } from "./getRelativePaths"
|
||||
import { openFile } from "./openFile"
|
||||
import { openImage } from "./openImage"
|
||||
import { openMention } from "./openMention"
|
||||
import { refreshRules } from "./refreshRules"
|
||||
import { searchCommits } from "./searchCommits"
|
||||
import { searchFiles } from "./searchFiles"
|
||||
import { selectImages } from "./selectImages"
|
||||
@@ -28,7 +27,6 @@ export function registerAllMethods(): void {
|
||||
registerMethod("openFile", openFile)
|
||||
registerMethod("openImage", openImage)
|
||||
registerMethod("openMention", openMention)
|
||||
registerMethod("refreshRules", refreshRules)
|
||||
registerMethod("searchCommits", searchCommits)
|
||||
registerMethod("searchFiles", searchFiles)
|
||||
registerMethod("selectImages", selectImages)
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
import { EmptyRequest } from "@shared/proto/common"
|
||||
import { RefreshedRules } from "@shared/proto/file"
|
||||
import type { Controller } from "../index"
|
||||
import { refreshClineRulesToggles } from "@core/context/instructions/user-instructions/cline-rules"
|
||||
import { refreshExternalRulesToggles } from "@core/context/instructions/user-instructions/external-rules"
|
||||
import { refreshWorkflowToggles } from "@core/context/instructions/user-instructions/workflows"
|
||||
import { cwd } from "@core/task"
|
||||
|
||||
/**
|
||||
* Refreshes all rule toggles (Cline, External, and Workflows)
|
||||
* @param controller The controller instance
|
||||
* @param _request The empty request
|
||||
* @returns RefreshedRules containing updated toggles for all rule types
|
||||
*/
|
||||
export async function refreshRules(controller: Controller, _request: EmptyRequest): Promise<RefreshedRules> {
|
||||
try {
|
||||
const { globalToggles, localToggles } = await refreshClineRulesToggles(controller.context, cwd)
|
||||
const { cursorLocalToggles, windsurfLocalToggles } = await refreshExternalRulesToggles(controller.context, cwd)
|
||||
const { localWorkflowToggles, globalWorkflowToggles } = await refreshWorkflowToggles(controller.context, cwd)
|
||||
|
||||
return {
|
||||
globalClineRulesToggles: { toggles: globalToggles },
|
||||
localClineRulesToggles: { toggles: localToggles },
|
||||
localCursorRulesToggles: { toggles: cursorLocalToggles },
|
||||
localWindsurfRulesToggles: { toggles: windsurfLocalToggles },
|
||||
localWorkflowToggles: { toggles: localWorkflowToggles },
|
||||
globalWorkflowToggles: { toggles: globalWorkflowToggles },
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to refresh rules:", error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
@@ -32,12 +32,7 @@ import { fileExistsAtPath } from "@utils/fs"
|
||||
import { getWorkingState } from "@utils/git"
|
||||
import { extractCommitMessage } from "@integrations/git/commit-message-generator"
|
||||
import { getTotalTasksSize } from "@utils/storage"
|
||||
import {
|
||||
ensureMcpServersDirectoryExists,
|
||||
ensureSettingsDirectoryExists,
|
||||
GlobalFileNames,
|
||||
ensureWorkflowsDirectoryExists,
|
||||
} from "../storage/disk"
|
||||
import { ensureMcpServersDirectoryExists, ensureSettingsDirectoryExists, GlobalFileNames } from "../storage/disk"
|
||||
import {
|
||||
getAllExtensionState,
|
||||
getGlobalState,
|
||||
@@ -70,7 +65,7 @@ export class Controller {
|
||||
workspaceTracker: WorkspaceTracker
|
||||
mcpHub: McpHub
|
||||
accountService: ClineAccountService
|
||||
latestAnnouncementId = "may-22-2025_16:11:00" // update to some unique identifier when we add a new announcement
|
||||
private latestAnnouncementId = "may-16-2025_16:11:00" // update to some unique identifier when we add a new announcement
|
||||
|
||||
constructor(
|
||||
readonly context: vscode.ExtensionContext,
|
||||
@@ -261,6 +256,13 @@ export class Controller {
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// If user already opted in to telemetry, enable telemetry service
|
||||
this.getStateToPostToWebview().then((state) => {
|
||||
const { telemetrySetting } = state
|
||||
const isOptedIn = telemetrySetting !== "disabled"
|
||||
telemetryService.updateTelemetryState(isOptedIn)
|
||||
})
|
||||
break
|
||||
case "showChatView": {
|
||||
this.postMessageToWebview({
|
||||
@@ -289,6 +291,25 @@ export class Controller {
|
||||
}
|
||||
await this.postStateToWebview()
|
||||
break
|
||||
case "autoApprovalSettings":
|
||||
if (message.autoApprovalSettings) {
|
||||
const currentSettings = (await getAllExtensionState(this.context)).autoApprovalSettings
|
||||
const incomingVersion = message.autoApprovalSettings.version ?? 1
|
||||
const currentVersion = currentSettings?.version ?? 1
|
||||
if (incomingVersion > currentVersion) {
|
||||
await updateGlobalState(this.context, "autoApprovalSettings", message.autoApprovalSettings)
|
||||
if (this.task) {
|
||||
this.task.autoApprovalSettings = message.autoApprovalSettings
|
||||
}
|
||||
await this.postStateToWebview()
|
||||
}
|
||||
}
|
||||
break
|
||||
case "togglePlanActMode":
|
||||
if (message.chatSettings) {
|
||||
await this.togglePlanActModeWithChatSettings(message.chatSettings, message.chatContent)
|
||||
}
|
||||
break
|
||||
case "optionsResponse":
|
||||
await this.postMessageToWebview({
|
||||
type: "invoke",
|
||||
@@ -296,6 +317,16 @@ export class Controller {
|
||||
text: message.text,
|
||||
})
|
||||
break
|
||||
case "didShowAnnouncement":
|
||||
await updateGlobalState(this.context, "lastShownAnnouncementId", this.latestAnnouncementId)
|
||||
await this.postStateToWebview()
|
||||
break
|
||||
case "refreshClineRules":
|
||||
await refreshClineRulesToggles(this.context, cwd)
|
||||
await refreshExternalRulesToggles(this.context, cwd)
|
||||
await refreshWorkflowToggles(this.context, cwd)
|
||||
await this.postStateToWebview()
|
||||
break
|
||||
case "openInBrowser":
|
||||
if (message.url) {
|
||||
vscode.env.openExternal(vscode.Uri.parse(message.url))
|
||||
@@ -356,20 +387,12 @@ export class Controller {
|
||||
// break
|
||||
// }
|
||||
case "toggleWorkflow": {
|
||||
const { workflowPath, enabled, isGlobal } = message
|
||||
if (workflowPath && typeof enabled === "boolean" && typeof isGlobal === "boolean") {
|
||||
if (isGlobal) {
|
||||
const globalWorkflowToggles =
|
||||
((await getGlobalState(this.context, "globalWorkflowToggles")) as ClineRulesToggles) || {}
|
||||
globalWorkflowToggles[workflowPath] = enabled
|
||||
await updateGlobalState(this.context, "globalWorkflowToggles", globalWorkflowToggles)
|
||||
await this.postStateToWebview()
|
||||
} else {
|
||||
const toggles = ((await getWorkspaceState(this.context, "workflowToggles")) as ClineRulesToggles) || {}
|
||||
toggles[workflowPath] = enabled
|
||||
await updateWorkspaceState(this.context, "workflowToggles", toggles)
|
||||
await this.postStateToWebview()
|
||||
}
|
||||
const { workflowPath, enabled } = message
|
||||
if (workflowPath && typeof enabled === "boolean") {
|
||||
const toggles = ((await getWorkspaceState(this.context, "workflowToggles")) as ClineRulesToggles) || {}
|
||||
toggles[workflowPath] = enabled
|
||||
await updateWorkspaceState(this.context, "workflowToggles", toggles)
|
||||
await this.postStateToWebview()
|
||||
}
|
||||
break
|
||||
}
|
||||
@@ -400,6 +423,13 @@ export class Controller {
|
||||
break
|
||||
}
|
||||
// telemetry
|
||||
case "openSettings": {
|
||||
await this.postMessageToWebview({
|
||||
type: "action",
|
||||
action: "settingsButtonClicked",
|
||||
})
|
||||
break
|
||||
}
|
||||
case "telemetrySetting": {
|
||||
if (message.telemetrySetting) {
|
||||
await this.updateTelemetrySetting(message.telemetrySetting)
|
||||
@@ -803,7 +833,6 @@ export class Controller {
|
||||
const response = await axios.get("https://api.cline.bot/v1/mcp/marketplace", {
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"User-Agent": "cline-vscode-extension",
|
||||
},
|
||||
})
|
||||
|
||||
@@ -1243,7 +1272,6 @@ export class Controller {
|
||||
planActSeparateModelsSetting,
|
||||
enableCheckpointsSetting,
|
||||
globalClineRulesToggles,
|
||||
globalWorkflowToggles,
|
||||
shellIntegrationTimeout,
|
||||
isNewUser,
|
||||
} = await getAllExtensionState(this.context)
|
||||
@@ -1257,7 +1285,7 @@ export class Controller {
|
||||
const localCursorRulesToggles =
|
||||
((await getWorkspaceState(this.context, "localCursorRulesToggles")) as ClineRulesToggles) || {}
|
||||
|
||||
const localWorkflowToggles = ((await getWorkspaceState(this.context, "workflowToggles")) as ClineRulesToggles) || {}
|
||||
const workflowToggles = ((await getWorkspaceState(this.context, "workflowToggles")) as ClineRulesToggles) || {}
|
||||
|
||||
return {
|
||||
version: this.context.extension?.packageJSON?.version ?? "",
|
||||
@@ -1281,13 +1309,12 @@ export class Controller {
|
||||
telemetrySetting,
|
||||
planActSeparateModelsSetting,
|
||||
enableCheckpointsSetting: enableCheckpointsSetting ?? true,
|
||||
distinctId: telemetryService.distinctId,
|
||||
vscMachineId: vscode.env.machineId,
|
||||
globalClineRulesToggles: globalClineRulesToggles || {},
|
||||
localClineRulesToggles: localClineRulesToggles || {},
|
||||
localWindsurfRulesToggles: localWindsurfRulesToggles || {},
|
||||
localCursorRulesToggles: localCursorRulesToggles || {},
|
||||
localWorkflowToggles: localWorkflowToggles || {},
|
||||
globalWorkflowToggles: globalWorkflowToggles || {},
|
||||
workflowToggles: workflowToggles || {},
|
||||
shellIntegrationTimeout,
|
||||
isNewUser,
|
||||
}
|
||||
|
||||
@@ -43,8 +43,6 @@ export async function refreshOpenRouterModels(
|
||||
}
|
||||
|
||||
switch (rawModel.id) {
|
||||
case "anthropic/claude-sonnet-4":
|
||||
case "anthropic/claude-opus-4":
|
||||
case "anthropic/claude-3-7-sonnet":
|
||||
case "anthropic/claude-3-7-sonnet:beta":
|
||||
case "anthropic/claude-3.7-sonnet":
|
||||
|
||||
@@ -44,11 +44,6 @@ export async function refreshRequestyModels(
|
||||
models[model.id] = modelInfo
|
||||
}
|
||||
console.log("Requesty models fetched", models)
|
||||
|
||||
controller.postMessageToWebview({
|
||||
type: "requestyModels",
|
||||
requestyModels: models,
|
||||
})
|
||||
} else {
|
||||
console.error("Invalid response from Requesty API")
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ import { resetState } from "./resetState"
|
||||
import { subscribeToState } from "./subscribeToState"
|
||||
import { toggleFavoriteModel } from "./toggleFavoriteModel"
|
||||
import { togglePlanActMode } from "./togglePlanActMode"
|
||||
import { updateAutoApprovalSettings } from "./updateAutoApprovalSettings"
|
||||
import { updateTerminalConnectionTimeout } from "./updateTerminalConnectionTimeout"
|
||||
|
||||
// Streaming methods for this service
|
||||
@@ -22,6 +21,5 @@ export function registerAllMethods(): void {
|
||||
registerMethod("subscribeToState", subscribeToState, { isStreaming: true })
|
||||
registerMethod("toggleFavoriteModel", toggleFavoriteModel)
|
||||
registerMethod("togglePlanActMode", togglePlanActMode)
|
||||
registerMethod("updateAutoApprovalSettings", updateAutoApprovalSettings)
|
||||
registerMethod("updateTerminalConnectionTimeout", updateTerminalConnectionTimeout)
|
||||
}
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
import { Controller } from ".."
|
||||
import { AutoApprovalSettingsRequest } from "../../../shared/proto/state"
|
||||
import { Empty } from "../../../shared/proto/common"
|
||||
import { convertProtoToAutoApprovalSettings } from "../../../shared/proto-conversions/models/auto-approval-settings-conversion"
|
||||
import { updateGlobalState } from "../../../core/storage/state"
|
||||
|
||||
/**
|
||||
* Updates the auto approval settings
|
||||
* @param controller The controller instance
|
||||
* @param request The auto approval settings request
|
||||
* @returns Empty response
|
||||
*/
|
||||
export async function updateAutoApprovalSettings(controller: Controller, request: AutoApprovalSettingsRequest): Promise<Empty> {
|
||||
const currentSettings = (await controller.getStateToPostToWebview()).autoApprovalSettings
|
||||
const incomingVersion = request.version
|
||||
const currentVersion = currentSettings?.version ?? 1
|
||||
|
||||
// Only update if incoming version is higher
|
||||
if (incomingVersion > currentVersion) {
|
||||
const settings = convertProtoToAutoApprovalSettings(request)
|
||||
|
||||
await updateGlobalState(controller.context, "autoApprovalSettings", settings)
|
||||
|
||||
if (controller.task) {
|
||||
controller.task.autoApprovalSettings = settings
|
||||
}
|
||||
|
||||
await controller.postStateToWebview()
|
||||
}
|
||||
|
||||
return Empty.create()
|
||||
}
|
||||
@@ -3,12 +3,10 @@
|
||||
|
||||
// Import all method implementations
|
||||
import { registerMethod } from "./index"
|
||||
import { onDidShowAnnouncement } from "./onDidShowAnnouncement"
|
||||
import { scrollToSettings } from "./scrollToSettings"
|
||||
|
||||
// Register all ui service methods
|
||||
export function registerAllMethods(): void {
|
||||
// Register each method with the registry
|
||||
registerMethod("onDidShowAnnouncement", onDidShowAnnouncement)
|
||||
registerMethod("scrollToSettings", scrollToSettings)
|
||||
}
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
import type { EmptyRequest, Boolean } from "../../../shared/proto/common"
|
||||
import type { Controller } from "../index"
|
||||
import { getGlobalState, updateGlobalState } from "../../storage/state"
|
||||
|
||||
/**
|
||||
* Marks the current announcement as shown and returns the updated shouldShowAnnouncement value
|
||||
*
|
||||
* @param controller The controller instance
|
||||
* @param _request The empty request (not used)
|
||||
* @returns Boolean indicating whether an announcement should be shown
|
||||
*/
|
||||
export async function onDidShowAnnouncement(controller: Controller, _request: EmptyRequest): Promise<Boolean> {
|
||||
try {
|
||||
// Update the lastShownAnnouncementId to the current latestAnnouncementId
|
||||
await updateGlobalState(controller.context, "lastShownAnnouncementId", controller.latestAnnouncementId)
|
||||
|
||||
// Get the updated lastShownAnnouncementId value after the update
|
||||
const lastShownAnnouncementId = await getGlobalState(controller.context, "lastShownAnnouncementId")
|
||||
|
||||
// Calculate the new shouldShowAnnouncement value
|
||||
// This replicates the same logic used in getStateToPostToWebview()
|
||||
const shouldShowAnnouncement = lastShownAnnouncementId !== controller.latestAnnouncementId
|
||||
|
||||
return { value: shouldShowAnnouncement }
|
||||
} catch (error) {
|
||||
console.error("Failed to acknowledge announcement:", error)
|
||||
return { value: false }
|
||||
}
|
||||
}
|
||||
@@ -196,7 +196,7 @@ Otherwise, if you have not completed the task and do not need additional informa
|
||||
`${newProblemsMessage}`,
|
||||
|
||||
diffError: (relPath: string, originalContent: string | undefined) =>
|
||||
`This is likely because the SEARCH block content doesn't match exactly with what's in the file, or if you used multiple SEARCH/REPLACE blocks they may not have been in the order they appear in the file. (Please also ensure that when using the replace_in_file tool, Do NOT add extra characters to the markers (e.g., <<<<<<< SEARCH> is INVALID). Do NOT forget to use the closing >>>>>>> REPLACE marker. Do NOT modify the marker format in any way. Malformed XML will cause complete tool failure and break the entire editing process.)\n\n` +
|
||||
`This is likely because the SEARCH block content doesn't match exactly with what's in the file, or if you used multiple SEARCH/REPLACE blocks they may not have been in the order they appear in the file.\n\n` +
|
||||
`The file was reverted to its original state:\n\n` +
|
||||
`<file_content path="${relPath.toPosix()}">\n${originalContent}\n</file_content>\n\n` +
|
||||
`Now that you have the latest state of the file, try the operation again with fewer, more precise SEARCH blocks. For large files especially, it may be prudent to try to limit yourself to <5 SEARCH/REPLACE blocks at a time, then wait for the user to respond with the result of the operation before following up with another replace_in_file call to make additional edits.\n(If you run into this error 3 times in a row, you may use the write_to_file tool as a fallback.)`,
|
||||
|
||||
@@ -604,7 +604,6 @@ RULES
|
||||
- Before executing commands, check the "Actively Running Terminals" section in environment_details. If present, consider how these active processes might impact your task. For example, if a local development server is already running, you wouldn't need to start it again. If no active terminals are listed, proceed with command execution as normal.
|
||||
- When using the replace_in_file tool, you must include complete lines in your SEARCH blocks, not partial lines. The system requires exact line matches and cannot match partial lines. For example, if you want to match a line containing "const x = 5;", your SEARCH block must include the entire line, not just "x = 5" or other fragments.
|
||||
- When using the replace_in_file tool, if you use multiple SEARCH/REPLACE blocks, list them in the order they appear in the file. For example if you need to make changes to both line 10 and line 50, first include the SEARCH/REPLACE block for line 10, followed by the SEARCH/REPLACE block for line 50.
|
||||
- When using the replace_in_file tool, Do NOT add extra characters to the markers (e.g., <<<<<<< SEARCH> is INVALID). Do NOT forget to use the closing >>>>>>> REPLACE marker. Do NOT modify the marker format in any way. Malformed XML will cause complete tool failure and break the entire editing process.
|
||||
- It is critical you wait for the user's response after each tool use, in order to confirm the success of the tool use. For example, if asked to make a todo app, you would create a file, wait for the user's response it was created successfully, then create another file if needed, wait for the user's response it was created successfully, etc.${
|
||||
supportsBrowserUse
|
||||
? " Then if you want to test your work, you might use browser_action to launch the site, wait for the user's response confirming the site was launched along with a screenshot, then perhaps e.g., click a button to test functionality if needed, wait for the user's response confirming the button was clicked along with a screenshot of the new state, before finally closing the browser."
|
||||
|
||||
@@ -8,8 +8,7 @@ import fs from "fs/promises"
|
||||
*/
|
||||
export async function parseSlashCommands(
|
||||
text: string,
|
||||
localWorkflowToggles: ClineRulesToggles,
|
||||
globalWorkflowToggles: ClineRulesToggles,
|
||||
workflowToggles: ClineRulesToggles,
|
||||
): Promise<{ processedText: string; needsClinerulesFileCheck: boolean }> {
|
||||
const SUPPORTED_DEFAULT_COMMANDS = ["newtask", "smol", "compact", "newrule", "reportbug"]
|
||||
|
||||
@@ -59,29 +58,18 @@ export async function parseSlashCommands(
|
||||
return { processedText: processedText, needsClinerulesFileCheck: commandName === "newrule" ? true : false }
|
||||
}
|
||||
|
||||
const globalWorkflows = Object.entries(globalWorkflowToggles)
|
||||
// in practice we want to minimize this work, so we only do it if theres a possible match
|
||||
const enabledWorkflows = Object.entries(workflowToggles)
|
||||
.filter(([_, enabled]) => enabled)
|
||||
.map(([filePath, _]) => {
|
||||
const fileName = filePath.replace(/^.*[/\\]/, "")
|
||||
|
||||
return {
|
||||
fullPath: filePath,
|
||||
fileName: fileName,
|
||||
}
|
||||
})
|
||||
|
||||
const localWorkflows = Object.entries(localWorkflowToggles)
|
||||
.filter(([_, enabled]) => enabled)
|
||||
.map(([filePath, _]) => {
|
||||
const fileName = filePath.replace(/^.*[/\\]/, "")
|
||||
return {
|
||||
fullPath: filePath,
|
||||
fileName: fileName,
|
||||
}
|
||||
})
|
||||
|
||||
// local workflows have precedence over global workflows
|
||||
const enabledWorkflows = [...localWorkflows, ...globalWorkflows]
|
||||
|
||||
// Then check if the command matches any enabled workflow filename
|
||||
const matchingWorkflow = enabledWorkflows.find((workflow) => workflow.fileName === commandName)
|
||||
|
||||
|
||||
@@ -76,17 +76,6 @@ export async function ensureRulesDirectoryExists(): Promise<string> {
|
||||
return clineRulesDir
|
||||
}
|
||||
|
||||
export async function ensureWorkflowsDirectoryExists(): Promise<string> {
|
||||
const userDocumentsPath = await getDocumentsPath()
|
||||
const clineWorkflowsDir = path.join(userDocumentsPath, "Cline", "Workflows")
|
||||
try {
|
||||
await fs.mkdir(clineWorkflowsDir, { recursive: true })
|
||||
} catch (error) {
|
||||
return path.join(os.homedir(), "Documents", "Cline", "Workflows") // in case creating a directory in documents fails for whatever reason (e.g. permissions) - this is fine because we will fail gracefully with a path that does not exist
|
||||
}
|
||||
return clineWorkflowsDir
|
||||
}
|
||||
|
||||
export async function ensureMcpServersDirectoryExists(): Promise<string> {
|
||||
const userDocumentsPath = await getDocumentsPath()
|
||||
const mcpServersDir = path.join(userDocumentsPath, "Cline", "MCP")
|
||||
|
||||
@@ -19,7 +19,6 @@ export type SecretKey =
|
||||
| "authNonce"
|
||||
| "asksageApiKey"
|
||||
| "xaiApiKey"
|
||||
| "nebiusApiKey"
|
||||
| "sambanovaApiKey"
|
||||
|
||||
export type GlobalStateKey =
|
||||
@@ -55,7 +54,6 @@ export type GlobalStateKey =
|
||||
| "openRouterProviderSorting"
|
||||
| "autoApprovalSettings"
|
||||
| "globalClineRulesToggles"
|
||||
| "globalWorkflowToggles"
|
||||
| "browserSettings"
|
||||
| "chatSettings"
|
||||
| "vsCodeLmModelSelector"
|
||||
|
||||
@@ -155,7 +155,6 @@ export async function getAllExtensionState(context: vscode.ExtensionContext) {
|
||||
thinkingBudgetTokens,
|
||||
reasoningEffort,
|
||||
sambanovaApiKey,
|
||||
nebiusApiKey,
|
||||
planActSeparateModelsSettingRaw,
|
||||
favoritedModelIds,
|
||||
globalClineRulesToggles,
|
||||
@@ -163,7 +162,6 @@ export async function getAllExtensionState(context: vscode.ExtensionContext) {
|
||||
shellIntegrationTimeout,
|
||||
enableCheckpointsSettingRaw,
|
||||
mcpMarketplaceEnabledRaw,
|
||||
globalWorkflowToggles,
|
||||
] = await Promise.all([
|
||||
getGlobalState(context, "isNewUser") as Promise<boolean | undefined>,
|
||||
getGlobalState(context, "apiProvider") as Promise<ApiProvider | undefined>,
|
||||
@@ -244,7 +242,6 @@ export async function getAllExtensionState(context: vscode.ExtensionContext) {
|
||||
getGlobalState(context, "thinkingBudgetTokens") as Promise<number | undefined>,
|
||||
getGlobalState(context, "reasoningEffort") as Promise<string | undefined>,
|
||||
getSecret(context, "sambanovaApiKey") as Promise<string | undefined>,
|
||||
getSecret(context, "nebiusApiKey") as Promise<string | undefined>,
|
||||
getGlobalState(context, "planActSeparateModelsSetting") as Promise<boolean | undefined>,
|
||||
getGlobalState(context, "favoritedModelIds") as Promise<string[] | undefined>,
|
||||
getGlobalState(context, "globalClineRulesToggles") as Promise<ClineRulesToggles | undefined>,
|
||||
@@ -252,7 +249,6 @@ export async function getAllExtensionState(context: vscode.ExtensionContext) {
|
||||
getGlobalState(context, "shellIntegrationTimeout") as Promise<number | undefined>,
|
||||
getGlobalState(context, "enableCheckpointsSetting") as Promise<boolean | undefined>,
|
||||
getGlobalState(context, "mcpMarketplaceEnabled") as Promise<boolean | undefined>,
|
||||
getGlobalState(context, "globalWorkflowToggles") as Promise<ClineRulesToggles | undefined>,
|
||||
fetch,
|
||||
])
|
||||
|
||||
@@ -357,7 +353,6 @@ export async function getAllExtensionState(context: vscode.ExtensionContext) {
|
||||
asksageApiUrl,
|
||||
xaiApiKey,
|
||||
sambanovaApiKey,
|
||||
nebiusApiKey,
|
||||
favoritedModelIds,
|
||||
requestTimeoutMs,
|
||||
},
|
||||
@@ -387,7 +382,6 @@ export async function getAllExtensionState(context: vscode.ExtensionContext) {
|
||||
planActSeparateModelsSetting,
|
||||
enableCheckpointsSetting: enableCheckpointsSetting,
|
||||
shellIntegrationTimeout: shellIntegrationTimeout || 4000,
|
||||
globalWorkflowToggles: globalWorkflowToggles || {},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -451,7 +445,6 @@ export async function updateApiConfiguration(context: vscode.ExtensionContext, a
|
||||
reasoningEffort,
|
||||
clineApiKey,
|
||||
sambanovaApiKey,
|
||||
nebiusApiKey,
|
||||
favoritedModelIds,
|
||||
} = apiConfiguration
|
||||
await updateGlobalState(context, "apiProvider", apiProvider)
|
||||
@@ -512,7 +505,6 @@ export async function updateApiConfiguration(context: vscode.ExtensionContext, a
|
||||
await updateGlobalState(context, "reasoningEffort", reasoningEffort)
|
||||
await storeSecret(context, "clineApiKey", clineApiKey)
|
||||
await storeSecret(context, "sambanovaApiKey", sambanovaApiKey)
|
||||
await storeSecret(context, "nebiusApiKey", nebiusApiKey)
|
||||
await updateGlobalState(context, "favoritedModelIds", favoritedModelIds)
|
||||
await updateGlobalState(context, "requestTimeoutMs", apiConfiguration.requestTimeoutMs)
|
||||
}
|
||||
@@ -542,7 +534,6 @@ export async function resetExtensionState(context: vscode.ExtensionContext) {
|
||||
"asksageApiKey",
|
||||
"xaiApiKey",
|
||||
"sambanovaApiKey",
|
||||
"nebiusApiKey",
|
||||
]
|
||||
for (const key of secretKeys) {
|
||||
await storeSecret(context, key, undefined)
|
||||
|
||||
@@ -4117,7 +4117,7 @@ export class Task {
|
||||
// Track if we need to check clinerulesFile
|
||||
let needsClinerulesFileCheck = false
|
||||
|
||||
const { localWorkflowToggles, globalWorkflowToggles } = await refreshWorkflowToggles(this.getContext(), cwd)
|
||||
const workflowToggles = await refreshWorkflowToggles(this.getContext(), cwd)
|
||||
|
||||
const processUserContent = async () => {
|
||||
// This is a temporary solution to dynamically load context mentions from tool results. It checks for the presence of tags that indicate that the tool was rejected and feedback was provided (see formatToolDeniedFeedback, attemptCompletion, executeCommand, and consecutiveMistakeCount >= 3) or "<answer>" (see askFollowupQuestion), we place all user generated content in these tags so they can effectively be used as markers for when we should parse mentions). However if we allow multiple tools responses in the future, we will need to parse mentions specifically within the user content tags.
|
||||
@@ -4143,8 +4143,7 @@ export class Task {
|
||||
// when parsing slash commands, we still want to allow the user to provide their desired context
|
||||
const { processedText, needsClinerulesFileCheck: needsCheck } = await parseSlashCommands(
|
||||
parsedText,
|
||||
localWorkflowToggles,
|
||||
globalWorkflowToggles,
|
||||
workflowToggles,
|
||||
)
|
||||
|
||||
if (needsCheck) {
|
||||
|
||||
@@ -150,7 +150,27 @@ export class WebviewProvider implements vscode.WebviewViewProvider {
|
||||
|
||||
this.outputChannel.appendLine("Webview view resolved")
|
||||
|
||||
// Title setting logic removed to allow VSCode to use the container title primarily.
|
||||
// Set the title to include the keyboard shortcut
|
||||
if (this.view && "title" in this.view) {
|
||||
// Check if the view object has a title property
|
||||
const isMac = process.platform === "darwin"
|
||||
const shortcutDisplay = isMac ? " (⌘+')" : " (Ctrl+')"
|
||||
const baseTitle = this.context.extension.packageJSON.displayName || "Cline"
|
||||
const newTitleWithShortcut = `${baseTitle}${shortcutDisplay}`
|
||||
|
||||
const currentTitle = this.view.title
|
||||
|
||||
if (typeof currentTitle === "string") {
|
||||
if (!currentTitle.includes(shortcutDisplay)) {
|
||||
// Title exists and is a string, but doesn't have the shortcut
|
||||
this.view.title = newTitleWithShortcut
|
||||
}
|
||||
// If it includes shortcutDisplay, do nothing
|
||||
} else {
|
||||
// Title is undefined or not a string (e.g., for sidebar initially)
|
||||
this.view.title = newTitleWithShortcut
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,6 @@ import { Controller } from "./core/controller"
|
||||
import { ErrorService } from "./services/error/ErrorService"
|
||||
import { initializeTestMode, cleanupTestMode } from "./services/test/TestMode"
|
||||
import { telemetryService } from "./services/posthog/telemetry/TelemetryService"
|
||||
import { v4 as uuidv4 } from "uuid"
|
||||
|
||||
/*
|
||||
Built using https://github.com/microsoft/vscode-webview-ui-toolkit
|
||||
@@ -76,16 +75,6 @@ export async function activate(context: vscode.ExtensionContext) {
|
||||
console.error(`Error during post-update actions: ${errorMessage}, Stack trace: ${error.stack}`)
|
||||
}
|
||||
|
||||
// backup id in case vscMachineID doesn't work
|
||||
let installId = context.globalState.get<string>("installId")
|
||||
|
||||
if (!installId) {
|
||||
installId = uuidv4()
|
||||
await context.globalState.update("installId", installId)
|
||||
}
|
||||
|
||||
telemetryService.captureExtensionActivated(installId)
|
||||
|
||||
context.subscriptions.push(
|
||||
vscode.commands.registerCommand("cline.plusButtonClicked", async (webview: any) => {
|
||||
const openChat = async (instance?: WebviewProvider) => {
|
||||
|
||||
@@ -30,7 +30,6 @@ import { arePathsEqual } from "@utils/path"
|
||||
import { secondsToMs } from "@utils/time"
|
||||
import { GlobalFileNames } from "@core/storage/disk"
|
||||
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js"
|
||||
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js"
|
||||
import { ExtensionMessage } from "@shared/ExtensionMessage"
|
||||
|
||||
// Default timeout for internal MCP data requests in milliseconds; is not the same as the user facing timeout stored as DEFAULT_MCP_TIMEOUT_SECONDS
|
||||
@@ -39,10 +38,10 @@ const DEFAULT_REQUEST_TIMEOUT_MS = 5000
|
||||
export type McpConnection = {
|
||||
server: McpServer
|
||||
client: Client
|
||||
transport: StdioClientTransport | SSEClientTransport | StreamableHTTPClientTransport
|
||||
transport: StdioClientTransport | SSEClientTransport
|
||||
}
|
||||
|
||||
export type McpTransportType = "stdio" | "sse" | "http"
|
||||
export type McpTransportType = "stdio" | "sse"
|
||||
|
||||
export type McpServerConfig = z.infer<typeof ServerConfigSchema>
|
||||
|
||||
@@ -70,15 +69,7 @@ const StdioConfigSchema = BaseConfigSchema.extend({
|
||||
transportType: "stdio" as const,
|
||||
}))
|
||||
|
||||
const StreamableHTTPConfigSchema = BaseConfigSchema.extend({
|
||||
transportType: z.literal("http"),
|
||||
url: z.string().url(),
|
||||
}).transform((config) => ({
|
||||
...config,
|
||||
transportType: "http" as const,
|
||||
}))
|
||||
|
||||
const ServerConfigSchema = z.union([StdioConfigSchema, SseConfigSchema, StreamableHTTPConfigSchema])
|
||||
const ServerConfigSchema = z.union([StdioConfigSchema, SseConfigSchema])
|
||||
|
||||
const McpSettingsSchema = z.object({
|
||||
mcpServers: z.record(ServerConfigSchema),
|
||||
@@ -192,7 +183,7 @@ export class McpHub {
|
||||
|
||||
private async connectToServerRPC(
|
||||
name: string,
|
||||
config: z.infer<typeof StdioConfigSchema> | z.infer<typeof SseConfigSchema> | z.infer<typeof StreamableHTTPConfigSchema>,
|
||||
config: z.infer<typeof StdioConfigSchema> | z.infer<typeof SseConfigSchema>,
|
||||
): Promise<void> {
|
||||
// Remove existing connection if it exists (should never happen, the connection should be deleted beforehand)
|
||||
this.connections = this.connections.filter((conn) => conn.server.name !== name)
|
||||
@@ -209,12 +200,10 @@ export class McpHub {
|
||||
},
|
||||
)
|
||||
|
||||
let transport: StdioClientTransport | SSEClientTransport | StreamableHTTPClientTransport
|
||||
let transport: StdioClientTransport | SSEClientTransport
|
||||
|
||||
if (config.transportType === "sse") {
|
||||
transport = new SSEClientTransport(new URL(config.url), {})
|
||||
} else if (config.transportType === "http") {
|
||||
transport = new StreamableHTTPClientTransport(new URL(config.url), {})
|
||||
} else {
|
||||
transport = new StdioClientTransport({
|
||||
command: config.command,
|
||||
@@ -308,7 +297,7 @@ export class McpHub {
|
||||
|
||||
private async connectToServer(
|
||||
name: string,
|
||||
config: z.infer<typeof StdioConfigSchema> | z.infer<typeof SseConfigSchema> | z.infer<typeof StreamableHTTPConfigSchema>,
|
||||
config: z.infer<typeof StdioConfigSchema> | z.infer<typeof SseConfigSchema>,
|
||||
): Promise<void> {
|
||||
// Remove existing connection if it exists (should never happen, the connection should be deleted beforehand)
|
||||
this.connections = this.connections.filter((conn) => conn.server.name !== name)
|
||||
@@ -325,12 +314,10 @@ export class McpHub {
|
||||
},
|
||||
)
|
||||
|
||||
let transport: StdioClientTransport | SSEClientTransport | StreamableHTTPClientTransport
|
||||
let transport: StdioClientTransport | SSEClientTransport
|
||||
|
||||
if (config.transportType === "sse") {
|
||||
transport = new SSEClientTransport(new URL(config.url), {})
|
||||
} else if (config.transportType === "http") {
|
||||
transport = new StreamableHTTPClientTransport(new URL(config.url), {})
|
||||
} else {
|
||||
transport = new StdioClientTransport({
|
||||
command: config.command,
|
||||
@@ -791,7 +778,7 @@ export class McpHub {
|
||||
console.error(`Failed to parse timeout configuration for server ${serverName}: ${error}`)
|
||||
}
|
||||
|
||||
const result = await connection.client.request(
|
||||
return await connection.client.request(
|
||||
{
|
||||
method: "tools/call",
|
||||
params: {
|
||||
@@ -804,11 +791,6 @@ export class McpHub {
|
||||
timeout,
|
||||
},
|
||||
)
|
||||
|
||||
return {
|
||||
...result,
|
||||
content: result.content ?? [],
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -9,6 +9,7 @@ class PostHogClientProvider {
|
||||
this.client = new PostHog(posthogConfig.apiKey, {
|
||||
host: posthogConfig.host,
|
||||
enableExceptionAutocapture: false,
|
||||
defaultOptIn: false,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import type { BrowserSettings } from "@shared/BrowserSettings"
|
||||
import { posthogClientProvider } from "../PostHogClientProvider"
|
||||
|
||||
/**
|
||||
* TelemetryService handles telemetry event tracking for the Cline extension
|
||||
* PostHogClient handles telemetry event tracking for the Cline extension
|
||||
* Uses PostHog analytics to track user interactions and system events
|
||||
* Respects user privacy settings and VSCode's global telemetry configuration
|
||||
*/
|
||||
@@ -29,7 +29,7 @@ interface Collection {
|
||||
*/
|
||||
type TelemetryCategory = "checkpoints" | "browser"
|
||||
|
||||
class TelemetryService {
|
||||
class PostHogClient {
|
||||
// Map to control specific telemetry categories (event types)
|
||||
private telemetryCategoryEnabled: Map<TelemetryCategory, boolean> = new Map([
|
||||
["checkpoints", false], // Checkpoints telemetry disabled
|
||||
@@ -41,11 +41,6 @@ class TelemetryService {
|
||||
// Event constants for tracking user interactions and system events
|
||||
private static readonly EVENTS = {
|
||||
// Task-related events for tracking conversation and execution flow
|
||||
|
||||
USER: {
|
||||
OPT_OUT: "user.opt_out",
|
||||
EXTENSION_ACTIVATED: "user.extension_activated",
|
||||
},
|
||||
TASK: {
|
||||
// Tracks when a new task/conversation is started
|
||||
CREATED: "task.created",
|
||||
@@ -88,19 +83,37 @@ class TelemetryService {
|
||||
},
|
||||
// UI interaction events for tracking user engagement
|
||||
UI: {
|
||||
// Tracks when user switches between API providers
|
||||
PROVIDER_SWITCH: "ui.provider_switch",
|
||||
// Tracks when images are attached to a conversation
|
||||
IMAGE_ATTACHED: "ui.image_attached",
|
||||
// Tracks general button click interactions
|
||||
BUTTON_CLICK: "ui.button_click",
|
||||
// Tracks when the marketplace view is opened
|
||||
MARKETPLACE_OPENED: "ui.marketplace_opened",
|
||||
// Tracks when settings panel is opened
|
||||
SETTINGS_OPENED: "ui.settings_opened",
|
||||
// Tracks when task history view is opened
|
||||
HISTORY_OPENED: "ui.history_opened",
|
||||
// Tracks when a task is removed from history
|
||||
TASK_POPPED: "ui.task_popped",
|
||||
// Tracks when a different model is selected
|
||||
MODEL_SELECTED: "ui.model_selected",
|
||||
// Tracks when planning mode is toggled on
|
||||
PLAN_MODE_TOGGLED: "ui.plan_mode_toggled",
|
||||
// Tracks when action mode is toggled on
|
||||
ACT_MODE_TOGGLED: "ui.act_mode_toggled",
|
||||
// Tracks when users use the "favorite" button in the model picker
|
||||
MODEL_FAVORITE_TOGGLED: "ui.model_favorite_toggled",
|
||||
},
|
||||
}
|
||||
|
||||
/** Singleton instance of the TelemetryService */
|
||||
private static instance: TelemetryService
|
||||
/** Singleton instance of the PostHogClient */
|
||||
private static instance: PostHogClient
|
||||
/** PostHog client instance for sending analytics events */
|
||||
private client: PostHog
|
||||
/** Unique identifier for the current VSCode instance */
|
||||
public distinctId: string = vscode.env.machineId
|
||||
private distinctId: string = vscode.env.machineId
|
||||
/** Whether telemetry is currently enabled based on user and VSCode settings */
|
||||
private telemetryEnabled: boolean = false
|
||||
/** Current version of the extension */
|
||||
@@ -116,18 +129,14 @@ class TelemetryService {
|
||||
this.client = posthogClientProvider.getClient()
|
||||
}
|
||||
|
||||
private setDistinctId(installId: string) {
|
||||
if (this.distinctId === "someValue.machineId") {
|
||||
this.distinctId = installId
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the telemetry state based on user preferences and VSCode settings
|
||||
* Only enables telemetry if both VSCode global telemetry is enabled and user has opted in
|
||||
* @param didUserOptIn Whether the user has explicitly opted into telemetry
|
||||
*/
|
||||
public async updateTelemetryState(didUserOptIn: boolean): Promise<void> {
|
||||
public updateTelemetryState(didUserOptIn: boolean): void {
|
||||
this.telemetryEnabled = false
|
||||
|
||||
// First check global telemetry level - telemetry should only be enabled when level is "all"
|
||||
const telemetryLevel = vscode.workspace.getConfiguration("telemetry").get<string>("telemetryLevel", "all")
|
||||
const globalTelemetryEnabled = telemetryLevel === "all"
|
||||
@@ -135,53 +144,25 @@ class TelemetryService {
|
||||
// We only enable telemetry if global vscode telemetry is enabled
|
||||
if (globalTelemetryEnabled) {
|
||||
this.telemetryEnabled = didUserOptIn
|
||||
} else {
|
||||
// Show warning to user that global telemetry is disabled
|
||||
void vscode.window
|
||||
.showWarningMessage(
|
||||
"VSCode telemetry is disabled. To enable telemetry for this extension, first enable VSCode telemetry in settings.",
|
||||
"Open Settings",
|
||||
)
|
||||
.then((selection) => {
|
||||
if (selection === "Open Settings") {
|
||||
void vscode.commands.executeCommand("workbench.action.openSettings", "telemetry.telemetryLevel")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Update PostHog client state based on telemetry preference
|
||||
if (this.telemetryEnabled) {
|
||||
this.client.optIn()
|
||||
this.client.identify({ distinctId: this.distinctId })
|
||||
} else {
|
||||
this.client.capture({
|
||||
distinctId: this.distinctId,
|
||||
event: TelemetryService.EVENTS.USER.OPT_OUT,
|
||||
properties: this.addProperties({}),
|
||||
})
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000)) // Delay 1 second before opting out
|
||||
this.client.optOut()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets or creates the singleton instance of TelemetryService
|
||||
* @returns The TelemetryService instance
|
||||
* Gets or creates the singleton instance of PostHogClient
|
||||
* @returns The PostHogClient instance
|
||||
*/
|
||||
public static getInstance(): TelemetryService {
|
||||
if (!TelemetryService.instance) {
|
||||
TelemetryService.instance = new TelemetryService()
|
||||
}
|
||||
return TelemetryService.instance
|
||||
}
|
||||
|
||||
private addProperties(properties: any): any {
|
||||
return {
|
||||
...properties,
|
||||
extension_version: this.version,
|
||||
is_dev: this.isDev,
|
||||
public static getInstance(): PostHogClient {
|
||||
if (!PostHogClient.instance) {
|
||||
PostHogClient.instance = new PostHogClient()
|
||||
}
|
||||
return PostHogClient.instance
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -190,14 +171,13 @@ class TelemetryService {
|
||||
* @param collect If true, store the event in collectedEvents instead of sending to PostHog
|
||||
*/
|
||||
public capture(event: { event: string; properties?: any }, collect: boolean = false): void {
|
||||
if (!this.telemetryEnabled) {
|
||||
return
|
||||
}
|
||||
const taskId = event.properties.taskId
|
||||
|
||||
const propertiesWithVersion = this.addProperties(event.properties)
|
||||
|
||||
if (collect && taskId) {
|
||||
const propertiesWithVersion = {
|
||||
...event.properties,
|
||||
extension_version: this.version,
|
||||
is_dev: this.isDev,
|
||||
}
|
||||
if (collect) {
|
||||
const existingTask = this.collectedTasks.find((task) => task.taskId === taskId)
|
||||
if (existingTask) {
|
||||
existingTask.collection.push({
|
||||
@@ -215,20 +195,11 @@ class TelemetryService {
|
||||
],
|
||||
})
|
||||
}
|
||||
} else {
|
||||
} else if (this.telemetryEnabled) {
|
||||
this.client.capture({ distinctId: this.distinctId, event: event.event, properties: propertiesWithVersion })
|
||||
}
|
||||
}
|
||||
|
||||
public captureExtensionActivated(installId: string) {
|
||||
this.setDistinctId(installId)
|
||||
|
||||
if (this.telemetryEnabled) {
|
||||
this.client.identify({ distinctId: this.distinctId })
|
||||
this.client.capture({ distinctId: this.distinctId, event: TelemetryService.EVENTS.USER.EXTENSION_ACTIVATED })
|
||||
}
|
||||
}
|
||||
|
||||
// Task events
|
||||
/**
|
||||
* Records when a new task/conversation is started
|
||||
@@ -239,7 +210,7 @@ class TelemetryService {
|
||||
public captureTaskCreated(taskId: string, apiProvider?: string, collect: boolean = false) {
|
||||
this.capture(
|
||||
{
|
||||
event: TelemetryService.EVENTS.TASK.CREATED,
|
||||
event: PostHogClient.EVENTS.TASK.CREATED,
|
||||
properties: { taskId, apiProvider },
|
||||
},
|
||||
collect,
|
||||
@@ -255,7 +226,7 @@ class TelemetryService {
|
||||
public captureTaskRestarted(taskId: string, apiProvider?: string, collect: boolean = false) {
|
||||
this.capture(
|
||||
{
|
||||
event: TelemetryService.EVENTS.TASK.RESTARTED,
|
||||
event: PostHogClient.EVENTS.TASK.RESTARTED,
|
||||
properties: { taskId, apiProvider },
|
||||
},
|
||||
collect,
|
||||
@@ -270,7 +241,7 @@ class TelemetryService {
|
||||
public captureTaskCompleted(taskId: string, collect: boolean = false) {
|
||||
this.capture(
|
||||
{
|
||||
event: TelemetryService.EVENTS.TASK.COMPLETED,
|
||||
event: PostHogClient.EVENTS.TASK.COMPLETED,
|
||||
properties: { taskId },
|
||||
},
|
||||
collect,
|
||||
@@ -307,7 +278,7 @@ class TelemetryService {
|
||||
|
||||
this.capture(
|
||||
{
|
||||
event: TelemetryService.EVENTS.TASK.CONVERSATION_TURN,
|
||||
event: PostHogClient.EVENTS.TASK.CONVERSATION_TURN,
|
||||
properties,
|
||||
},
|
||||
collect,
|
||||
@@ -324,7 +295,7 @@ class TelemetryService {
|
||||
public captureTokenUsage(taskId: string, tokensIn: number, tokensOut: number, model: string, collect: boolean = false) {
|
||||
this.capture(
|
||||
{
|
||||
event: TelemetryService.EVENTS.TASK.TOKEN_USAGE,
|
||||
event: PostHogClient.EVENTS.TASK.TOKEN_USAGE,
|
||||
properties: {
|
||||
taskId,
|
||||
tokensIn,
|
||||
@@ -344,7 +315,7 @@ class TelemetryService {
|
||||
public captureModeSwitch(taskId: string, mode: "plan" | "act", collect: boolean = false) {
|
||||
this.capture(
|
||||
{
|
||||
event: TelemetryService.EVENTS.TASK.MODE_SWITCH,
|
||||
event: PostHogClient.EVENTS.TASK.MODE_SWITCH,
|
||||
properties: {
|
||||
taskId,
|
||||
mode,
|
||||
@@ -363,7 +334,7 @@ class TelemetryService {
|
||||
console.info("TelemetryService: Capturing task feedback", { taskId, feedbackType })
|
||||
this.capture(
|
||||
{
|
||||
event: TelemetryService.EVENTS.TASK.FEEDBACK,
|
||||
event: PostHogClient.EVENTS.TASK.FEEDBACK,
|
||||
properties: {
|
||||
taskId,
|
||||
feedbackType,
|
||||
@@ -384,7 +355,7 @@ class TelemetryService {
|
||||
public captureToolUsage(taskId: string, tool: string, autoApproved: boolean, success: boolean, collect: boolean = false) {
|
||||
this.capture(
|
||||
{
|
||||
event: TelemetryService.EVENTS.TASK.TOOL_USED,
|
||||
event: PostHogClient.EVENTS.TASK.TOOL_USED,
|
||||
properties: {
|
||||
taskId,
|
||||
tool,
|
||||
@@ -414,7 +385,7 @@ class TelemetryService {
|
||||
|
||||
this.capture(
|
||||
{
|
||||
event: TelemetryService.EVENTS.TASK.CHECKPOINT_USED,
|
||||
event: PostHogClient.EVENTS.TASK.CHECKPOINT_USED,
|
||||
properties: {
|
||||
taskId,
|
||||
action,
|
||||
@@ -425,6 +396,135 @@ class TelemetryService {
|
||||
)
|
||||
}
|
||||
|
||||
// UI events
|
||||
/**
|
||||
* Records when the user switches between different API providers
|
||||
* @param from Previous provider name
|
||||
* @param to New provider name
|
||||
* @param location Where the switch occurred (settings panel or bottom bar)
|
||||
* @param taskId Optional task identifier if switch occurred during a task
|
||||
*/
|
||||
public captureProviderSwitch(
|
||||
from: string,
|
||||
to: string,
|
||||
location: "settings" | "bottom",
|
||||
taskId?: string,
|
||||
collect: boolean = false,
|
||||
) {
|
||||
this.capture(
|
||||
{
|
||||
event: PostHogClient.EVENTS.UI.PROVIDER_SWITCH,
|
||||
properties: {
|
||||
from,
|
||||
to,
|
||||
location,
|
||||
taskId,
|
||||
},
|
||||
},
|
||||
collect,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Records when images are attached to a conversation
|
||||
* @param taskId Unique identifier for the task
|
||||
* @param imageCount Number of images attached
|
||||
*/
|
||||
public captureImageAttached(taskId: string, imageCount: number, collect: boolean = false) {
|
||||
this.capture(
|
||||
{
|
||||
event: PostHogClient.EVENTS.UI.IMAGE_ATTACHED,
|
||||
properties: {
|
||||
taskId,
|
||||
imageCount,
|
||||
},
|
||||
},
|
||||
collect,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Records general button click interactions in the UI
|
||||
* @param button Identifier for the button that was clicked
|
||||
* @param taskId Optional task identifier if click occurred during a task
|
||||
*/
|
||||
public captureButtonClick(button: string, taskId?: string, collect: boolean = false) {
|
||||
this.capture(
|
||||
{
|
||||
event: PostHogClient.EVENTS.UI.BUTTON_CLICK,
|
||||
properties: {
|
||||
button,
|
||||
taskId,
|
||||
},
|
||||
},
|
||||
collect,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Records when the marketplace view is opened
|
||||
* @param taskId Optional task identifier if marketplace was opened during a task
|
||||
*/
|
||||
public captureMarketplaceOpened(taskId?: string, collect: boolean = false) {
|
||||
this.capture(
|
||||
{
|
||||
event: PostHogClient.EVENTS.UI.MARKETPLACE_OPENED,
|
||||
properties: {
|
||||
taskId,
|
||||
},
|
||||
},
|
||||
collect,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Records when the settings panel is opened
|
||||
* @param taskId Optional task identifier if settings were opened during a task
|
||||
*/
|
||||
public captureSettingsOpened(taskId?: string, collect: boolean = false) {
|
||||
this.capture(
|
||||
{
|
||||
event: PostHogClient.EVENTS.UI.SETTINGS_OPENED,
|
||||
properties: {
|
||||
taskId,
|
||||
},
|
||||
},
|
||||
collect,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Records when the task history view is opened
|
||||
* @param taskId Optional task identifier if history was opened during a task
|
||||
*/
|
||||
public captureHistoryOpened(taskId?: string, collect: boolean = false) {
|
||||
this.capture(
|
||||
{
|
||||
event: PostHogClient.EVENTS.UI.HISTORY_OPENED,
|
||||
properties: {
|
||||
taskId,
|
||||
},
|
||||
},
|
||||
collect,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Records when a task is removed from the task history
|
||||
* @param taskId Unique identifier for the task being removed
|
||||
*/
|
||||
public captureTaskPopped(taskId: string, collect: boolean = false) {
|
||||
this.capture(
|
||||
{
|
||||
event: PostHogClient.EVENTS.UI.TASK_POPPED,
|
||||
properties: {
|
||||
taskId,
|
||||
},
|
||||
},
|
||||
collect,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Records when a diff edit (replace_in_file) operation fails
|
||||
* @param taskId Unique identifier for the task
|
||||
@@ -433,7 +533,7 @@ class TelemetryService {
|
||||
public captureDiffEditFailure(taskId: string, modelId: string, errorType?: string, collect: boolean = false) {
|
||||
this.capture(
|
||||
{
|
||||
event: TelemetryService.EVENTS.TASK.DIFF_EDIT_FAILED,
|
||||
event: PostHogClient.EVENTS.TASK.DIFF_EDIT_FAILED,
|
||||
properties: {
|
||||
taskId,
|
||||
errorType,
|
||||
@@ -453,7 +553,7 @@ class TelemetryService {
|
||||
public captureModelSelected(model: string, provider: string, taskId?: string, collect: boolean = false) {
|
||||
this.capture(
|
||||
{
|
||||
event: TelemetryService.EVENTS.UI.MODEL_SELECTED,
|
||||
event: PostHogClient.EVENTS.UI.MODEL_SELECTED,
|
||||
properties: {
|
||||
model,
|
||||
provider,
|
||||
@@ -471,7 +571,7 @@ class TelemetryService {
|
||||
public captureHistoricalTaskLoaded(taskId: string, collect: boolean = false) {
|
||||
this.capture(
|
||||
{
|
||||
event: TelemetryService.EVENTS.TASK.HISTORICAL_LOADED,
|
||||
event: PostHogClient.EVENTS.TASK.HISTORICAL_LOADED,
|
||||
properties: {
|
||||
taskId,
|
||||
},
|
||||
@@ -487,7 +587,7 @@ class TelemetryService {
|
||||
public captureRetryClicked(taskId: string, collect: boolean = false) {
|
||||
this.capture(
|
||||
{
|
||||
event: TelemetryService.EVENTS.TASK.RETRY_CLICKED,
|
||||
event: PostHogClient.EVENTS.TASK.RETRY_CLICKED,
|
||||
properties: {
|
||||
taskId,
|
||||
},
|
||||
@@ -508,7 +608,7 @@ class TelemetryService {
|
||||
|
||||
this.capture(
|
||||
{
|
||||
event: TelemetryService.EVENTS.TASK.BROWSER_TOOL_START,
|
||||
event: PostHogClient.EVENTS.TASK.BROWSER_TOOL_START,
|
||||
properties: {
|
||||
taskId,
|
||||
viewport: browserSettings.viewport,
|
||||
@@ -541,7 +641,7 @@ class TelemetryService {
|
||||
|
||||
this.capture(
|
||||
{
|
||||
event: TelemetryService.EVENTS.TASK.BROWSER_TOOL_END,
|
||||
event: PostHogClient.EVENTS.TASK.BROWSER_TOOL_END,
|
||||
properties: {
|
||||
taskId,
|
||||
actionCount: stats.actionCount,
|
||||
@@ -579,7 +679,7 @@ class TelemetryService {
|
||||
|
||||
this.capture(
|
||||
{
|
||||
event: TelemetryService.EVENTS.TASK.BROWSER_ERROR,
|
||||
event: PostHogClient.EVENTS.TASK.BROWSER_ERROR,
|
||||
properties: {
|
||||
taskId,
|
||||
errorType,
|
||||
@@ -601,7 +701,7 @@ class TelemetryService {
|
||||
public captureOptionSelected(taskId: string, qty: number, mode: "plan" | "act", collect: boolean = false) {
|
||||
this.capture(
|
||||
{
|
||||
event: TelemetryService.EVENTS.TASK.OPTION_SELECTED,
|
||||
event: PostHogClient.EVENTS.TASK.OPTION_SELECTED,
|
||||
properties: {
|
||||
taskId,
|
||||
qty,
|
||||
@@ -621,7 +721,7 @@ class TelemetryService {
|
||||
public captureOptionsIgnored(taskId: string, qty: number, mode: "plan" | "act", collect: boolean = false) {
|
||||
this.capture(
|
||||
{
|
||||
event: TelemetryService.EVENTS.TASK.OPTIONS_IGNORED,
|
||||
event: PostHogClient.EVENTS.TASK.OPTIONS_IGNORED,
|
||||
properties: {
|
||||
taskId,
|
||||
qty,
|
||||
@@ -658,7 +758,7 @@ class TelemetryService {
|
||||
) {
|
||||
this.capture(
|
||||
{
|
||||
event: TelemetryService.EVENTS.TASK.GEMINI_API_PERFORMANCE,
|
||||
event: PostHogClient.EVENTS.TASK.GEMINI_API_PERFORMANCE,
|
||||
properties: {
|
||||
taskId,
|
||||
modelId,
|
||||
@@ -677,7 +777,7 @@ class TelemetryService {
|
||||
public captureModelFavoritesUsage(model: string, isFavorited: boolean, collect: boolean = false) {
|
||||
this.capture(
|
||||
{
|
||||
event: TelemetryService.EVENTS.UI.MODEL_FAVORITE_TOGGLED,
|
||||
event: PostHogClient.EVENTS.UI.MODEL_FAVORITE_TOGGLED,
|
||||
properties: {
|
||||
model,
|
||||
isFavorited,
|
||||
@@ -706,17 +806,13 @@ class TelemetryService {
|
||||
}
|
||||
|
||||
public async sendCollectedEvents(taskId?: string): Promise<void> {
|
||||
if (!this.telemetryEnabled) {
|
||||
return
|
||||
}
|
||||
|
||||
if (this.collectedTasks.length > 0) {
|
||||
if (taskId) {
|
||||
const task = this.collectedTasks.find((t) => t.taskId === taskId)
|
||||
if (task) {
|
||||
this.capture(
|
||||
{
|
||||
event: TelemetryService.EVENTS.TASK.TASK_COLLECTION,
|
||||
event: PostHogClient.EVENTS.TASK.TASK_COLLECTION,
|
||||
properties: { taskId, events: task.collection },
|
||||
},
|
||||
false,
|
||||
@@ -727,7 +823,7 @@ class TelemetryService {
|
||||
for (const task of this.collectedTasks) {
|
||||
this.capture(
|
||||
{
|
||||
event: TelemetryService.EVENTS.TASK.TASK_COLLECTION,
|
||||
event: PostHogClient.EVENTS.TASK.TASK_COLLECTION,
|
||||
properties: { taskId: task.taskId, events: task.collection },
|
||||
},
|
||||
false,
|
||||
@@ -743,4 +839,4 @@ class TelemetryService {
|
||||
}
|
||||
}
|
||||
|
||||
export const telemetryService = TelemetryService.getInstance()
|
||||
export const telemetryService = PostHogClient.getInstance()
|
||||
|
||||
@@ -137,11 +137,10 @@ export interface ExtensionState {
|
||||
photoURL: string | null
|
||||
}
|
||||
version: string
|
||||
distinctId: string
|
||||
vscMachineId: string
|
||||
globalClineRulesToggles: ClineRulesToggles
|
||||
localClineRulesToggles: ClineRulesToggles
|
||||
localWorkflowToggles: ClineRulesToggles
|
||||
globalWorkflowToggles: ClineRulesToggles
|
||||
workflowToggles: ClineRulesToggles
|
||||
localCursorRulesToggles: ClineRulesToggles
|
||||
localWindsurfRulesToggles: ClineRulesToggles
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { ApiConfiguration } from "./api"
|
||||
import { AutoApprovalSettings } from "./AutoApprovalSettings"
|
||||
import { BrowserSettings } from "./BrowserSettings"
|
||||
import { ChatSettings } from "./ChatSettings"
|
||||
import { UserInfo } from "./UserInfo"
|
||||
@@ -13,9 +14,13 @@ export interface WebviewMessage {
|
||||
| "newTask"
|
||||
| "condense"
|
||||
| "reportBug"
|
||||
| "didShowAnnouncement"
|
||||
| "openInBrowser"
|
||||
| "showChatView"
|
||||
| "refreshClineRules"
|
||||
| "openMcpSettings"
|
||||
| "autoApprovalSettings"
|
||||
| "togglePlanActMode"
|
||||
| "openExtensionSettings"
|
||||
| "requestVsCodeLmModels"
|
||||
| "showAccountViewClicked"
|
||||
@@ -25,6 +30,7 @@ export interface WebviewMessage {
|
||||
| "searchCommits"
|
||||
| "fetchLatestMcpServersFromHub"
|
||||
| "telemetrySetting"
|
||||
| "openSettings"
|
||||
| "invoke"
|
||||
| "updateSettings"
|
||||
| "clearAllTaskHistory"
|
||||
@@ -42,6 +48,7 @@ export interface WebviewMessage {
|
||||
images?: string[]
|
||||
bool?: boolean
|
||||
number?: number
|
||||
autoApprovalSettings?: AutoApprovalSettings
|
||||
browserSettings?: BrowserSettings
|
||||
chatSettings?: ChatSettings
|
||||
chatContent?: ChatContent
|
||||
|
||||
+6
-164
@@ -19,7 +19,6 @@ export type ApiProvider =
|
||||
| "vscode-lm"
|
||||
| "cline"
|
||||
| "litellm"
|
||||
| "nebius"
|
||||
| "fireworks"
|
||||
| "asksage"
|
||||
| "xai"
|
||||
@@ -82,7 +81,6 @@ export interface ApiHandlerOptions {
|
||||
azureApiVersion?: string
|
||||
vsCodeLmModelSelector?: LanguageModelChatSelector
|
||||
qwenApiLine?: string
|
||||
nebiusApiKey?: string
|
||||
asksageApiUrl?: string
|
||||
asksageApiKey?: string
|
||||
xaiApiKey?: string
|
||||
@@ -138,29 +136,8 @@ export interface OpenAiCompatibleModelInfo extends ModelInfo {
|
||||
// Anthropic
|
||||
// https://docs.anthropic.com/en/docs/about-claude/models // prices updated 2025-01-02
|
||||
export type AnthropicModelId = keyof typeof anthropicModels
|
||||
export const anthropicDefaultModelId: AnthropicModelId = "claude-sonnet-4-20250514"
|
||||
export const anthropicDefaultModelId: AnthropicModelId = "claude-3-7-sonnet-20250219"
|
||||
export const anthropicModels = {
|
||||
"claude-sonnet-4-20250514": {
|
||||
maxTokens: 8192,
|
||||
contextWindow: 200_000,
|
||||
supportsImages: true,
|
||||
|
||||
supportsPromptCache: true,
|
||||
inputPrice: 3.0,
|
||||
outputPrice: 15.0,
|
||||
cacheWritesPrice: 3.75,
|
||||
cacheReadsPrice: 0.3,
|
||||
},
|
||||
"claude-opus-4-20250514": {
|
||||
maxTokens: 8192,
|
||||
contextWindow: 200_000,
|
||||
supportsImages: true,
|
||||
supportsPromptCache: true,
|
||||
inputPrice: 15.0,
|
||||
outputPrice: 75.0,
|
||||
cacheWritesPrice: 18.75,
|
||||
cacheReadsPrice: 1.5,
|
||||
},
|
||||
"claude-3-7-sonnet-20250219": {
|
||||
maxTokens: 8192,
|
||||
contextWindow: 200_000,
|
||||
@@ -218,28 +195,8 @@ export const anthropicModels = {
|
||||
// AWS Bedrock
|
||||
// https://docs.aws.amazon.com/bedrock/latest/userguide/conversation-inference.html
|
||||
export type BedrockModelId = keyof typeof bedrockModels
|
||||
export const bedrockDefaultModelId: BedrockModelId = "anthropic.claude-sonnet-4-20250514-v1:0"
|
||||
export const bedrockDefaultModelId: BedrockModelId = "anthropic.claude-3-7-sonnet-20250219-v1:0"
|
||||
export const bedrockModels = {
|
||||
"anthropic.claude-sonnet-4-20250514-v1:0": {
|
||||
maxTokens: 8192,
|
||||
contextWindow: 200_000,
|
||||
supportsImages: true,
|
||||
supportsPromptCache: true,
|
||||
inputPrice: 3.0,
|
||||
outputPrice: 15.0,
|
||||
cacheWritesPrice: 3.75,
|
||||
cacheReadsPrice: 0.3,
|
||||
},
|
||||
"anthropic.claude-opus-4-20250514-v1:0": {
|
||||
maxTokens: 8192,
|
||||
contextWindow: 200_000,
|
||||
supportsImages: true,
|
||||
supportsPromptCache: true,
|
||||
inputPrice: 15.0,
|
||||
outputPrice: 75.0,
|
||||
cacheWritesPrice: 18.75,
|
||||
cacheReadsPrice: 1.5,
|
||||
},
|
||||
"amazon.nova-premier-v1:0": {
|
||||
maxTokens: 10_000,
|
||||
contextWindow: 1_000_000,
|
||||
@@ -358,7 +315,7 @@ export const bedrockModels = {
|
||||
|
||||
// OpenRouter
|
||||
// https://openrouter.ai/models?order=newest&supported_parameters=tools
|
||||
export const openRouterDefaultModelId = "anthropic/claude-sonnet-4" // will always exist in openRouterModels
|
||||
export const openRouterDefaultModelId = "anthropic/claude-3.7-sonnet" // will always exist in openRouterModels
|
||||
export const openRouterDefaultModelInfo: ModelInfo = {
|
||||
maxTokens: 8192,
|
||||
contextWindow: 200_000,
|
||||
@@ -370,34 +327,14 @@ export const openRouterDefaultModelInfo: ModelInfo = {
|
||||
cacheWritesPrice: 3.75,
|
||||
cacheReadsPrice: 0.3,
|
||||
description:
|
||||
"Claude 4 Sonnet is an advanced large language model with improved reasoning, coding, and problem-solving capabilities. It introduces a hybrid reasoning approach, allowing users to choose between rapid responses and extended, step-by-step processing for complex tasks. The model demonstrates notable improvements in coding, particularly in front-end development and full-stack updates, and excels in agentic workflows, where it can autonomously navigate multi-step processes. \n\nClaude 4 Sonnet maintains performance parity with its predecessor in standard mode while offering an extended reasoning mode for enhanced accuracy in math, coding, and instruction-following tasks.\n\nRead more at the [blog post here](https://www.anthropic.com/news/claude-4)",
|
||||
"Claude 3.7 Sonnet is an advanced large language model with improved reasoning, coding, and problem-solving capabilities. It introduces a hybrid reasoning approach, allowing users to choose between rapid responses and extended, step-by-step processing for complex tasks. The model demonstrates notable improvements in coding, particularly in front-end development and full-stack updates, and excels in agentic workflows, where it can autonomously navigate multi-step processes. \n\nClaude 3.7 Sonnet maintains performance parity with its predecessor in standard mode while offering an extended reasoning mode for enhanced accuracy in math, coding, and instruction-following tasks.\n\nRead more at the [blog post here](https://www.anthropic.com/news/claude-3-7-sonnet)",
|
||||
}
|
||||
// Vertex AI
|
||||
// https://cloud.google.com/vertex-ai/generative-ai/docs/partner-models/use-claude
|
||||
// https://cloud.google.com/vertex-ai/generative-ai/pricing#partner-models
|
||||
export type VertexModelId = keyof typeof vertexModels
|
||||
export const vertexDefaultModelId: VertexModelId = "claude-sonnet-4@20250514"
|
||||
export const vertexDefaultModelId: VertexModelId = "claude-3-7-sonnet@20250219"
|
||||
export const vertexModels = {
|
||||
"claude-sonnet-4@20250514": {
|
||||
maxTokens: 8192,
|
||||
contextWindow: 200_000,
|
||||
supportsImages: true,
|
||||
supportsPromptCache: true,
|
||||
inputPrice: 3.0,
|
||||
outputPrice: 15.0,
|
||||
cacheWritesPrice: 3.75,
|
||||
cacheReadsPrice: 0.3,
|
||||
},
|
||||
"claude-opus-4@20250514": {
|
||||
maxTokens: 8192,
|
||||
contextWindow: 200_000,
|
||||
supportsImages: true,
|
||||
supportsPromptCache: true,
|
||||
inputPrice: 15.0,
|
||||
outputPrice: 75.0,
|
||||
cacheWritesPrice: 18.75,
|
||||
cacheReadsPrice: 1.5,
|
||||
},
|
||||
"claude-3-7-sonnet@20250219": {
|
||||
maxTokens: 8192,
|
||||
contextWindow: 200_000,
|
||||
@@ -1455,7 +1392,7 @@ export const doubaoModels = {
|
||||
// Mistral
|
||||
// https://docs.mistral.ai/getting-started/models/models_overview/
|
||||
export type MistralModelId = keyof typeof mistralModels
|
||||
export const mistralDefaultModelId: MistralModelId = "devstral-small-2505"
|
||||
export const mistralDefaultModelId: MistralModelId = "codestral-2501"
|
||||
export const mistralModels = {
|
||||
"mistral-large-2411": {
|
||||
maxTokens: 131_000,
|
||||
@@ -1545,14 +1482,6 @@ export const mistralModels = {
|
||||
inputPrice: 0.3,
|
||||
outputPrice: 0.9,
|
||||
},
|
||||
"devstral-small-2505": {
|
||||
maxTokens: 128_000,
|
||||
contextWindow: 131_072,
|
||||
supportsImages: false,
|
||||
supportsPromptCache: false,
|
||||
inputPrice: 0.1,
|
||||
outputPrice: 0.3,
|
||||
},
|
||||
} as const satisfies Record<string, ModelInfo>
|
||||
|
||||
// LiteLLM
|
||||
@@ -1623,93 +1552,6 @@ export const askSageModels = {
|
||||
},
|
||||
}
|
||||
|
||||
// Nebius AI Studio
|
||||
// https://docs.nebius.com/studio/inference/models
|
||||
export const nebiusModels = {
|
||||
"deepseek-ai/DeepSeek-V3": {
|
||||
maxTokens: 32_000,
|
||||
contextWindow: 96_000,
|
||||
supportsImages: false,
|
||||
supportsPromptCache: false,
|
||||
inputPrice: 0.5,
|
||||
outputPrice: 1.5,
|
||||
},
|
||||
"deepseek-ai/DeepSeek-V3-0324-fast": {
|
||||
maxTokens: 128_000,
|
||||
contextWindow: 128_000,
|
||||
supportsImages: false,
|
||||
supportsPromptCache: false,
|
||||
inputPrice: 2,
|
||||
outputPrice: 6,
|
||||
},
|
||||
"deepseek-ai/DeepSeek-R1": {
|
||||
maxTokens: 32_000,
|
||||
contextWindow: 96_000,
|
||||
supportsImages: false,
|
||||
supportsPromptCache: false,
|
||||
inputPrice: 0.8,
|
||||
outputPrice: 2.4,
|
||||
},
|
||||
"deepseek-ai/DeepSeek-R1-fast": {
|
||||
maxTokens: 32_000,
|
||||
contextWindow: 96_000,
|
||||
supportsImages: false,
|
||||
supportsPromptCache: false,
|
||||
inputPrice: 2,
|
||||
outputPrice: 6,
|
||||
},
|
||||
"meta-llama/Llama-3.3-70B-Instruct-fast": {
|
||||
maxTokens: 32_000,
|
||||
contextWindow: 96_000,
|
||||
supportsImages: false,
|
||||
supportsPromptCache: false,
|
||||
inputPrice: 0.25,
|
||||
outputPrice: 0.75,
|
||||
},
|
||||
"Qwen/Qwen2.5-32B-Instruct-fast": {
|
||||
maxTokens: 8_192,
|
||||
contextWindow: 32_768,
|
||||
supportsImages: false,
|
||||
supportsPromptCache: false,
|
||||
inputPrice: 0.13,
|
||||
outputPrice: 0.4,
|
||||
},
|
||||
"Qwen/Qwen2.5-Coder-32B-Instruct-fast": {
|
||||
maxTokens: 128_000,
|
||||
contextWindow: 128_000,
|
||||
supportsImages: false,
|
||||
supportsPromptCache: false,
|
||||
inputPrice: 0.1,
|
||||
outputPrice: 0.3,
|
||||
},
|
||||
"Qwen/Qwen3-4B-fast": {
|
||||
maxTokens: 32_000,
|
||||
contextWindow: 41_000,
|
||||
supportsImages: false,
|
||||
supportsPromptCache: false,
|
||||
inputPrice: 0.08,
|
||||
outputPrice: 0.24,
|
||||
},
|
||||
"Qwen/Qwen3-30B-A3B-fast": {
|
||||
maxTokens: 32_000,
|
||||
contextWindow: 41_000,
|
||||
supportsImages: false,
|
||||
supportsPromptCache: false,
|
||||
inputPrice: 0.3,
|
||||
outputPrice: 0.9,
|
||||
},
|
||||
"Qwen/Qwen3-235B-A22B": {
|
||||
maxTokens: 32_000,
|
||||
contextWindow: 41_000,
|
||||
supportsImages: false,
|
||||
supportsPromptCache: false,
|
||||
inputPrice: 0.2,
|
||||
outputPrice: 0.6,
|
||||
},
|
||||
} as const satisfies Record<string, ModelInfo>
|
||||
export type NebiusModelId = keyof typeof nebiusModels
|
||||
export const nebiusDefaultModelId = "Qwen/Qwen2.5-32B-Instruct-fast" satisfies NebiusModelId
|
||||
|
||||
// X AI
|
||||
// https://docs.x.ai/docs/api-reference
|
||||
export type XAIModelId = keyof typeof xaiModels
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
import { AutoApprovalSettings } from "../../AutoApprovalSettings"
|
||||
import { AutoApprovalSettingsRequest } from "../../proto/state"
|
||||
|
||||
// Converts domain AutoApprovalSettings to proto AutoApprovalSettingsRequest
|
||||
export function convertAutoApprovalSettingsToProto(settings: AutoApprovalSettings): AutoApprovalSettingsRequest {
|
||||
return {
|
||||
metadata: {},
|
||||
version: settings.version,
|
||||
enabled: settings.enabled,
|
||||
actions: {
|
||||
readFiles: settings.actions.readFiles || false,
|
||||
readFilesExternally: settings.actions.readFilesExternally || false,
|
||||
editFiles: settings.actions.editFiles || false,
|
||||
editFilesExternally: settings.actions.editFilesExternally || false,
|
||||
executeSafeCommands: settings.actions.executeSafeCommands || false,
|
||||
executeAllCommands: settings.actions.executeAllCommands || false,
|
||||
useBrowser: settings.actions.useBrowser || false,
|
||||
useMcp: settings.actions.useMcp || false,
|
||||
},
|
||||
maxRequests: settings.maxRequests || 20,
|
||||
enableNotifications: settings.enableNotifications || false,
|
||||
favorites: settings.favorites || [],
|
||||
}
|
||||
}
|
||||
|
||||
// Converts proto AutoApprovalSettingsRequest to domain AutoApprovalSettings
|
||||
export function convertProtoToAutoApprovalSettings(protoSettings: AutoApprovalSettingsRequest): AutoApprovalSettings {
|
||||
return {
|
||||
version: protoSettings.version,
|
||||
enabled: protoSettings.enabled,
|
||||
actions: {
|
||||
readFiles: protoSettings.actions?.readFiles || false,
|
||||
readFilesExternally: protoSettings.actions?.readFilesExternally || false,
|
||||
editFiles: protoSettings.actions?.editFiles || false,
|
||||
editFilesExternally: protoSettings.actions?.editFilesExternally || false,
|
||||
executeSafeCommands: protoSettings.actions?.executeSafeCommands || false,
|
||||
executeAllCommands: protoSettings.actions?.executeAllCommands || false,
|
||||
useBrowser: protoSettings.actions?.useBrowser || false,
|
||||
useMcp: protoSettings.actions?.useMcp || false,
|
||||
},
|
||||
maxRequests: protoSettings.maxRequests || 20,
|
||||
enableNotifications: protoSettings.enableNotifications || false,
|
||||
favorites: protoSettings.favorites || [],
|
||||
}
|
||||
}
|
||||
@@ -10,15 +10,6 @@ import { Empty, EmptyRequest, Metadata, StringArray, StringRequest } from "./com
|
||||
|
||||
export const protobufPackage = "cline"
|
||||
|
||||
/** Response for refreshRules operation */
|
||||
export interface RefreshedRules {
|
||||
globalClineRulesToggles?: ClineRulesToggles | undefined
|
||||
localClineRulesToggles?: ClineRulesToggles | undefined
|
||||
localCursorRulesToggles?: ClineRulesToggles | undefined
|
||||
localWindsurfRulesToggles?: ClineRulesToggles | undefined
|
||||
workflowToggles?: ClineRulesToggles | undefined
|
||||
}
|
||||
|
||||
/** Request to toggle a Windsurf rule */
|
||||
export interface ToggleWindsurfRuleRequest {
|
||||
metadata?: Metadata | undefined
|
||||
@@ -141,159 +132,6 @@ export interface ToggleCursorRuleRequest {
|
||||
enabled: boolean
|
||||
}
|
||||
|
||||
function createBaseRefreshedRules(): RefreshedRules {
|
||||
return {
|
||||
globalClineRulesToggles: undefined,
|
||||
localClineRulesToggles: undefined,
|
||||
localCursorRulesToggles: undefined,
|
||||
localWindsurfRulesToggles: undefined,
|
||||
workflowToggles: undefined,
|
||||
}
|
||||
}
|
||||
|
||||
export const RefreshedRules: MessageFns<RefreshedRules> = {
|
||||
encode(message: RefreshedRules, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
|
||||
if (message.globalClineRulesToggles !== undefined) {
|
||||
ClineRulesToggles.encode(message.globalClineRulesToggles, writer.uint32(10).fork()).join()
|
||||
}
|
||||
if (message.localClineRulesToggles !== undefined) {
|
||||
ClineRulesToggles.encode(message.localClineRulesToggles, writer.uint32(18).fork()).join()
|
||||
}
|
||||
if (message.localCursorRulesToggles !== undefined) {
|
||||
ClineRulesToggles.encode(message.localCursorRulesToggles, writer.uint32(26).fork()).join()
|
||||
}
|
||||
if (message.localWindsurfRulesToggles !== undefined) {
|
||||
ClineRulesToggles.encode(message.localWindsurfRulesToggles, writer.uint32(34).fork()).join()
|
||||
}
|
||||
if (message.workflowToggles !== undefined) {
|
||||
ClineRulesToggles.encode(message.workflowToggles, writer.uint32(42).fork()).join()
|
||||
}
|
||||
return writer
|
||||
},
|
||||
|
||||
decode(input: BinaryReader | Uint8Array, length?: number): RefreshedRules {
|
||||
const reader = input instanceof BinaryReader ? input : new BinaryReader(input)
|
||||
let end = length === undefined ? reader.len : reader.pos + length
|
||||
const message = createBaseRefreshedRules()
|
||||
while (reader.pos < end) {
|
||||
const tag = reader.uint32()
|
||||
switch (tag >>> 3) {
|
||||
case 1: {
|
||||
if (tag !== 10) {
|
||||
break
|
||||
}
|
||||
|
||||
message.globalClineRulesToggles = ClineRulesToggles.decode(reader, reader.uint32())
|
||||
continue
|
||||
}
|
||||
case 2: {
|
||||
if (tag !== 18) {
|
||||
break
|
||||
}
|
||||
|
||||
message.localClineRulesToggles = ClineRulesToggles.decode(reader, reader.uint32())
|
||||
continue
|
||||
}
|
||||
case 3: {
|
||||
if (tag !== 26) {
|
||||
break
|
||||
}
|
||||
|
||||
message.localCursorRulesToggles = ClineRulesToggles.decode(reader, reader.uint32())
|
||||
continue
|
||||
}
|
||||
case 4: {
|
||||
if (tag !== 34) {
|
||||
break
|
||||
}
|
||||
|
||||
message.localWindsurfRulesToggles = ClineRulesToggles.decode(reader, reader.uint32())
|
||||
continue
|
||||
}
|
||||
case 5: {
|
||||
if (tag !== 42) {
|
||||
break
|
||||
}
|
||||
|
||||
message.workflowToggles = ClineRulesToggles.decode(reader, reader.uint32())
|
||||
continue
|
||||
}
|
||||
}
|
||||
if ((tag & 7) === 4 || tag === 0) {
|
||||
break
|
||||
}
|
||||
reader.skip(tag & 7)
|
||||
}
|
||||
return message
|
||||
},
|
||||
|
||||
fromJSON(object: any): RefreshedRules {
|
||||
return {
|
||||
globalClineRulesToggles: isSet(object.globalClineRulesToggles)
|
||||
? ClineRulesToggles.fromJSON(object.globalClineRulesToggles)
|
||||
: undefined,
|
||||
localClineRulesToggles: isSet(object.localClineRulesToggles)
|
||||
? ClineRulesToggles.fromJSON(object.localClineRulesToggles)
|
||||
: undefined,
|
||||
localCursorRulesToggles: isSet(object.localCursorRulesToggles)
|
||||
? ClineRulesToggles.fromJSON(object.localCursorRulesToggles)
|
||||
: undefined,
|
||||
localWindsurfRulesToggles: isSet(object.localWindsurfRulesToggles)
|
||||
? ClineRulesToggles.fromJSON(object.localWindsurfRulesToggles)
|
||||
: undefined,
|
||||
workflowToggles: isSet(object.workflowToggles) ? ClineRulesToggles.fromJSON(object.workflowToggles) : undefined,
|
||||
}
|
||||
},
|
||||
|
||||
toJSON(message: RefreshedRules): unknown {
|
||||
const obj: any = {}
|
||||
if (message.globalClineRulesToggles !== undefined) {
|
||||
obj.globalClineRulesToggles = ClineRulesToggles.toJSON(message.globalClineRulesToggles)
|
||||
}
|
||||
if (message.localClineRulesToggles !== undefined) {
|
||||
obj.localClineRulesToggles = ClineRulesToggles.toJSON(message.localClineRulesToggles)
|
||||
}
|
||||
if (message.localCursorRulesToggles !== undefined) {
|
||||
obj.localCursorRulesToggles = ClineRulesToggles.toJSON(message.localCursorRulesToggles)
|
||||
}
|
||||
if (message.localWindsurfRulesToggles !== undefined) {
|
||||
obj.localWindsurfRulesToggles = ClineRulesToggles.toJSON(message.localWindsurfRulesToggles)
|
||||
}
|
||||
if (message.workflowToggles !== undefined) {
|
||||
obj.workflowToggles = ClineRulesToggles.toJSON(message.workflowToggles)
|
||||
}
|
||||
return obj
|
||||
},
|
||||
|
||||
create<I extends Exact<DeepPartial<RefreshedRules>, I>>(base?: I): RefreshedRules {
|
||||
return RefreshedRules.fromPartial(base ?? ({} as any))
|
||||
},
|
||||
fromPartial<I extends Exact<DeepPartial<RefreshedRules>, I>>(object: I): RefreshedRules {
|
||||
const message = createBaseRefreshedRules()
|
||||
message.globalClineRulesToggles =
|
||||
object.globalClineRulesToggles !== undefined && object.globalClineRulesToggles !== null
|
||||
? ClineRulesToggles.fromPartial(object.globalClineRulesToggles)
|
||||
: undefined
|
||||
message.localClineRulesToggles =
|
||||
object.localClineRulesToggles !== undefined && object.localClineRulesToggles !== null
|
||||
? ClineRulesToggles.fromPartial(object.localClineRulesToggles)
|
||||
: undefined
|
||||
message.localCursorRulesToggles =
|
||||
object.localCursorRulesToggles !== undefined && object.localCursorRulesToggles !== null
|
||||
? ClineRulesToggles.fromPartial(object.localCursorRulesToggles)
|
||||
: undefined
|
||||
message.localWindsurfRulesToggles =
|
||||
object.localWindsurfRulesToggles !== undefined && object.localWindsurfRulesToggles !== null
|
||||
? ClineRulesToggles.fromPartial(object.localWindsurfRulesToggles)
|
||||
: undefined
|
||||
message.workflowToggles =
|
||||
object.workflowToggles !== undefined && object.workflowToggles !== null
|
||||
? ClineRulesToggles.fromPartial(object.workflowToggles)
|
||||
: undefined
|
||||
return message
|
||||
},
|
||||
}
|
||||
|
||||
function createBaseToggleWindsurfRuleRequest(): ToggleWindsurfRuleRequest {
|
||||
return { metadata: undefined, rulePath: "", enabled: false }
|
||||
}
|
||||
@@ -1766,15 +1604,6 @@ export const FileServiceDefinition = {
|
||||
responseStream: false,
|
||||
options: {},
|
||||
},
|
||||
/** Refreshes all rule toggles (Cline, External, and Workflows) */
|
||||
refreshRules: {
|
||||
name: "refreshRules",
|
||||
requestType: EmptyRequest,
|
||||
requestStream: false,
|
||||
responseType: RefreshedRules,
|
||||
responseStream: false,
|
||||
options: {},
|
||||
},
|
||||
},
|
||||
} as const
|
||||
|
||||
|
||||
@@ -64,28 +64,6 @@ export interface ChatContent {
|
||||
images: string[]
|
||||
}
|
||||
|
||||
/** Message for auto approval settings */
|
||||
export interface AutoApprovalSettingsRequest {
|
||||
metadata?: Metadata | undefined
|
||||
version: number
|
||||
enabled: boolean
|
||||
actions?: AutoApprovalSettingsRequest_Actions | undefined
|
||||
maxRequests: number
|
||||
enableNotifications: boolean
|
||||
favorites: string[]
|
||||
}
|
||||
|
||||
export interface AutoApprovalSettingsRequest_Actions {
|
||||
readFiles: boolean
|
||||
readFilesExternally: boolean
|
||||
editFiles: boolean
|
||||
editFilesExternally: boolean
|
||||
executeSafeCommands: boolean
|
||||
executeAllCommands: boolean
|
||||
useBrowser: boolean
|
||||
useMcp: boolean
|
||||
}
|
||||
|
||||
function createBaseState(): State {
|
||||
return { stateJson: "" }
|
||||
}
|
||||
@@ -413,357 +391,6 @@ export const ChatContent: MessageFns<ChatContent> = {
|
||||
},
|
||||
}
|
||||
|
||||
function createBaseAutoApprovalSettingsRequest(): AutoApprovalSettingsRequest {
|
||||
return {
|
||||
metadata: undefined,
|
||||
version: 0,
|
||||
enabled: false,
|
||||
actions: undefined,
|
||||
maxRequests: 0,
|
||||
enableNotifications: false,
|
||||
favorites: [],
|
||||
}
|
||||
}
|
||||
|
||||
export const AutoApprovalSettingsRequest: MessageFns<AutoApprovalSettingsRequest> = {
|
||||
encode(message: AutoApprovalSettingsRequest, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
|
||||
if (message.metadata !== undefined) {
|
||||
Metadata.encode(message.metadata, writer.uint32(10).fork()).join()
|
||||
}
|
||||
if (message.version !== 0) {
|
||||
writer.uint32(16).int32(message.version)
|
||||
}
|
||||
if (message.enabled !== false) {
|
||||
writer.uint32(24).bool(message.enabled)
|
||||
}
|
||||
if (message.actions !== undefined) {
|
||||
AutoApprovalSettingsRequest_Actions.encode(message.actions, writer.uint32(34).fork()).join()
|
||||
}
|
||||
if (message.maxRequests !== 0) {
|
||||
writer.uint32(40).int32(message.maxRequests)
|
||||
}
|
||||
if (message.enableNotifications !== false) {
|
||||
writer.uint32(48).bool(message.enableNotifications)
|
||||
}
|
||||
for (const v of message.favorites) {
|
||||
writer.uint32(58).string(v!)
|
||||
}
|
||||
return writer
|
||||
},
|
||||
|
||||
decode(input: BinaryReader | Uint8Array, length?: number): AutoApprovalSettingsRequest {
|
||||
const reader = input instanceof BinaryReader ? input : new BinaryReader(input)
|
||||
let end = length === undefined ? reader.len : reader.pos + length
|
||||
const message = createBaseAutoApprovalSettingsRequest()
|
||||
while (reader.pos < end) {
|
||||
const tag = reader.uint32()
|
||||
switch (tag >>> 3) {
|
||||
case 1: {
|
||||
if (tag !== 10) {
|
||||
break
|
||||
}
|
||||
|
||||
message.metadata = Metadata.decode(reader, reader.uint32())
|
||||
continue
|
||||
}
|
||||
case 2: {
|
||||
if (tag !== 16) {
|
||||
break
|
||||
}
|
||||
|
||||
message.version = reader.int32()
|
||||
continue
|
||||
}
|
||||
case 3: {
|
||||
if (tag !== 24) {
|
||||
break
|
||||
}
|
||||
|
||||
message.enabled = reader.bool()
|
||||
continue
|
||||
}
|
||||
case 4: {
|
||||
if (tag !== 34) {
|
||||
break
|
||||
}
|
||||
|
||||
message.actions = AutoApprovalSettingsRequest_Actions.decode(reader, reader.uint32())
|
||||
continue
|
||||
}
|
||||
case 5: {
|
||||
if (tag !== 40) {
|
||||
break
|
||||
}
|
||||
|
||||
message.maxRequests = reader.int32()
|
||||
continue
|
||||
}
|
||||
case 6: {
|
||||
if (tag !== 48) {
|
||||
break
|
||||
}
|
||||
|
||||
message.enableNotifications = reader.bool()
|
||||
continue
|
||||
}
|
||||
case 7: {
|
||||
if (tag !== 58) {
|
||||
break
|
||||
}
|
||||
|
||||
message.favorites.push(reader.string())
|
||||
continue
|
||||
}
|
||||
}
|
||||
if ((tag & 7) === 4 || tag === 0) {
|
||||
break
|
||||
}
|
||||
reader.skip(tag & 7)
|
||||
}
|
||||
return message
|
||||
},
|
||||
|
||||
fromJSON(object: any): AutoApprovalSettingsRequest {
|
||||
return {
|
||||
metadata: isSet(object.metadata) ? Metadata.fromJSON(object.metadata) : undefined,
|
||||
version: isSet(object.version) ? globalThis.Number(object.version) : 0,
|
||||
enabled: isSet(object.enabled) ? globalThis.Boolean(object.enabled) : false,
|
||||
actions: isSet(object.actions) ? AutoApprovalSettingsRequest_Actions.fromJSON(object.actions) : undefined,
|
||||
maxRequests: isSet(object.maxRequests) ? globalThis.Number(object.maxRequests) : 0,
|
||||
enableNotifications: isSet(object.enableNotifications) ? globalThis.Boolean(object.enableNotifications) : false,
|
||||
favorites: globalThis.Array.isArray(object?.favorites) ? object.favorites.map((e: any) => globalThis.String(e)) : [],
|
||||
}
|
||||
},
|
||||
|
||||
toJSON(message: AutoApprovalSettingsRequest): unknown {
|
||||
const obj: any = {}
|
||||
if (message.metadata !== undefined) {
|
||||
obj.metadata = Metadata.toJSON(message.metadata)
|
||||
}
|
||||
if (message.version !== 0) {
|
||||
obj.version = Math.round(message.version)
|
||||
}
|
||||
if (message.enabled !== false) {
|
||||
obj.enabled = message.enabled
|
||||
}
|
||||
if (message.actions !== undefined) {
|
||||
obj.actions = AutoApprovalSettingsRequest_Actions.toJSON(message.actions)
|
||||
}
|
||||
if (message.maxRequests !== 0) {
|
||||
obj.maxRequests = Math.round(message.maxRequests)
|
||||
}
|
||||
if (message.enableNotifications !== false) {
|
||||
obj.enableNotifications = message.enableNotifications
|
||||
}
|
||||
if (message.favorites?.length) {
|
||||
obj.favorites = message.favorites
|
||||
}
|
||||
return obj
|
||||
},
|
||||
|
||||
create<I extends Exact<DeepPartial<AutoApprovalSettingsRequest>, I>>(base?: I): AutoApprovalSettingsRequest {
|
||||
return AutoApprovalSettingsRequest.fromPartial(base ?? ({} as any))
|
||||
},
|
||||
fromPartial<I extends Exact<DeepPartial<AutoApprovalSettingsRequest>, I>>(object: I): AutoApprovalSettingsRequest {
|
||||
const message = createBaseAutoApprovalSettingsRequest()
|
||||
message.metadata =
|
||||
object.metadata !== undefined && object.metadata !== null ? Metadata.fromPartial(object.metadata) : undefined
|
||||
message.version = object.version ?? 0
|
||||
message.enabled = object.enabled ?? false
|
||||
message.actions =
|
||||
object.actions !== undefined && object.actions !== null
|
||||
? AutoApprovalSettingsRequest_Actions.fromPartial(object.actions)
|
||||
: undefined
|
||||
message.maxRequests = object.maxRequests ?? 0
|
||||
message.enableNotifications = object.enableNotifications ?? false
|
||||
message.favorites = object.favorites?.map((e) => e) || []
|
||||
return message
|
||||
},
|
||||
}
|
||||
|
||||
function createBaseAutoApprovalSettingsRequest_Actions(): AutoApprovalSettingsRequest_Actions {
|
||||
return {
|
||||
readFiles: false,
|
||||
readFilesExternally: false,
|
||||
editFiles: false,
|
||||
editFilesExternally: false,
|
||||
executeSafeCommands: false,
|
||||
executeAllCommands: false,
|
||||
useBrowser: false,
|
||||
useMcp: false,
|
||||
}
|
||||
}
|
||||
|
||||
export const AutoApprovalSettingsRequest_Actions: MessageFns<AutoApprovalSettingsRequest_Actions> = {
|
||||
encode(message: AutoApprovalSettingsRequest_Actions, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
|
||||
if (message.readFiles !== false) {
|
||||
writer.uint32(8).bool(message.readFiles)
|
||||
}
|
||||
if (message.readFilesExternally !== false) {
|
||||
writer.uint32(16).bool(message.readFilesExternally)
|
||||
}
|
||||
if (message.editFiles !== false) {
|
||||
writer.uint32(24).bool(message.editFiles)
|
||||
}
|
||||
if (message.editFilesExternally !== false) {
|
||||
writer.uint32(32).bool(message.editFilesExternally)
|
||||
}
|
||||
if (message.executeSafeCommands !== false) {
|
||||
writer.uint32(40).bool(message.executeSafeCommands)
|
||||
}
|
||||
if (message.executeAllCommands !== false) {
|
||||
writer.uint32(48).bool(message.executeAllCommands)
|
||||
}
|
||||
if (message.useBrowser !== false) {
|
||||
writer.uint32(56).bool(message.useBrowser)
|
||||
}
|
||||
if (message.useMcp !== false) {
|
||||
writer.uint32(64).bool(message.useMcp)
|
||||
}
|
||||
return writer
|
||||
},
|
||||
|
||||
decode(input: BinaryReader | Uint8Array, length?: number): AutoApprovalSettingsRequest_Actions {
|
||||
const reader = input instanceof BinaryReader ? input : new BinaryReader(input)
|
||||
let end = length === undefined ? reader.len : reader.pos + length
|
||||
const message = createBaseAutoApprovalSettingsRequest_Actions()
|
||||
while (reader.pos < end) {
|
||||
const tag = reader.uint32()
|
||||
switch (tag >>> 3) {
|
||||
case 1: {
|
||||
if (tag !== 8) {
|
||||
break
|
||||
}
|
||||
|
||||
message.readFiles = reader.bool()
|
||||
continue
|
||||
}
|
||||
case 2: {
|
||||
if (tag !== 16) {
|
||||
break
|
||||
}
|
||||
|
||||
message.readFilesExternally = reader.bool()
|
||||
continue
|
||||
}
|
||||
case 3: {
|
||||
if (tag !== 24) {
|
||||
break
|
||||
}
|
||||
|
||||
message.editFiles = reader.bool()
|
||||
continue
|
||||
}
|
||||
case 4: {
|
||||
if (tag !== 32) {
|
||||
break
|
||||
}
|
||||
|
||||
message.editFilesExternally = reader.bool()
|
||||
continue
|
||||
}
|
||||
case 5: {
|
||||
if (tag !== 40) {
|
||||
break
|
||||
}
|
||||
|
||||
message.executeSafeCommands = reader.bool()
|
||||
continue
|
||||
}
|
||||
case 6: {
|
||||
if (tag !== 48) {
|
||||
break
|
||||
}
|
||||
|
||||
message.executeAllCommands = reader.bool()
|
||||
continue
|
||||
}
|
||||
case 7: {
|
||||
if (tag !== 56) {
|
||||
break
|
||||
}
|
||||
|
||||
message.useBrowser = reader.bool()
|
||||
continue
|
||||
}
|
||||
case 8: {
|
||||
if (tag !== 64) {
|
||||
break
|
||||
}
|
||||
|
||||
message.useMcp = reader.bool()
|
||||
continue
|
||||
}
|
||||
}
|
||||
if ((tag & 7) === 4 || tag === 0) {
|
||||
break
|
||||
}
|
||||
reader.skip(tag & 7)
|
||||
}
|
||||
return message
|
||||
},
|
||||
|
||||
fromJSON(object: any): AutoApprovalSettingsRequest_Actions {
|
||||
return {
|
||||
readFiles: isSet(object.readFiles) ? globalThis.Boolean(object.readFiles) : false,
|
||||
readFilesExternally: isSet(object.readFilesExternally) ? globalThis.Boolean(object.readFilesExternally) : false,
|
||||
editFiles: isSet(object.editFiles) ? globalThis.Boolean(object.editFiles) : false,
|
||||
editFilesExternally: isSet(object.editFilesExternally) ? globalThis.Boolean(object.editFilesExternally) : false,
|
||||
executeSafeCommands: isSet(object.executeSafeCommands) ? globalThis.Boolean(object.executeSafeCommands) : false,
|
||||
executeAllCommands: isSet(object.executeAllCommands) ? globalThis.Boolean(object.executeAllCommands) : false,
|
||||
useBrowser: isSet(object.useBrowser) ? globalThis.Boolean(object.useBrowser) : false,
|
||||
useMcp: isSet(object.useMcp) ? globalThis.Boolean(object.useMcp) : false,
|
||||
}
|
||||
},
|
||||
|
||||
toJSON(message: AutoApprovalSettingsRequest_Actions): unknown {
|
||||
const obj: any = {}
|
||||
if (message.readFiles !== false) {
|
||||
obj.readFiles = message.readFiles
|
||||
}
|
||||
if (message.readFilesExternally !== false) {
|
||||
obj.readFilesExternally = message.readFilesExternally
|
||||
}
|
||||
if (message.editFiles !== false) {
|
||||
obj.editFiles = message.editFiles
|
||||
}
|
||||
if (message.editFilesExternally !== false) {
|
||||
obj.editFilesExternally = message.editFilesExternally
|
||||
}
|
||||
if (message.executeSafeCommands !== false) {
|
||||
obj.executeSafeCommands = message.executeSafeCommands
|
||||
}
|
||||
if (message.executeAllCommands !== false) {
|
||||
obj.executeAllCommands = message.executeAllCommands
|
||||
}
|
||||
if (message.useBrowser !== false) {
|
||||
obj.useBrowser = message.useBrowser
|
||||
}
|
||||
if (message.useMcp !== false) {
|
||||
obj.useMcp = message.useMcp
|
||||
}
|
||||
return obj
|
||||
},
|
||||
|
||||
create<I extends Exact<DeepPartial<AutoApprovalSettingsRequest_Actions>, I>>(base?: I): AutoApprovalSettingsRequest_Actions {
|
||||
return AutoApprovalSettingsRequest_Actions.fromPartial(base ?? ({} as any))
|
||||
},
|
||||
fromPartial<I extends Exact<DeepPartial<AutoApprovalSettingsRequest_Actions>, I>>(
|
||||
object: I,
|
||||
): AutoApprovalSettingsRequest_Actions {
|
||||
const message = createBaseAutoApprovalSettingsRequest_Actions()
|
||||
message.readFiles = object.readFiles ?? false
|
||||
message.readFilesExternally = object.readFilesExternally ?? false
|
||||
message.editFiles = object.editFiles ?? false
|
||||
message.editFilesExternally = object.editFilesExternally ?? false
|
||||
message.executeSafeCommands = object.executeSafeCommands ?? false
|
||||
message.executeAllCommands = object.executeAllCommands ?? false
|
||||
message.useBrowser = object.useBrowser ?? false
|
||||
message.useMcp = object.useMcp ?? false
|
||||
return message
|
||||
},
|
||||
}
|
||||
|
||||
export type StateServiceDefinition = typeof StateServiceDefinition
|
||||
export const StateServiceDefinition = {
|
||||
name: "StateService",
|
||||
@@ -817,14 +444,6 @@ export const StateServiceDefinition = {
|
||||
responseStream: false,
|
||||
options: {},
|
||||
},
|
||||
updateAutoApprovalSettings: {
|
||||
name: "updateAutoApprovalSettings",
|
||||
requestType: AutoApprovalSettingsRequest,
|
||||
requestStream: false,
|
||||
responseType: Empty,
|
||||
responseStream: false,
|
||||
options: {},
|
||||
},
|
||||
},
|
||||
} as const
|
||||
|
||||
|
||||
+1
-10
@@ -5,7 +5,7 @@
|
||||
// source: ui.proto
|
||||
|
||||
/* eslint-disable */
|
||||
import { Boolean, Empty, EmptyRequest, StringRequest } from "./common"
|
||||
import { Empty, StringRequest } from "./common"
|
||||
|
||||
export const protobufPackage = "cline"
|
||||
|
||||
@@ -24,14 +24,5 @@ export const UiServiceDefinition = {
|
||||
responseStream: false,
|
||||
options: {},
|
||||
},
|
||||
/** Marks the current announcement as shown and returns whether an announcement should still be shown */
|
||||
onDidShowAnnouncement: {
|
||||
name: "onDidShowAnnouncement",
|
||||
requestType: EmptyRequest,
|
||||
requestStream: false,
|
||||
responseType: Boolean,
|
||||
responseStream: false,
|
||||
options: {},
|
||||
},
|
||||
},
|
||||
} as const
|
||||
|
||||
@@ -34,7 +34,6 @@ import { searchFiles } from "../core/controller/file/searchFiles"
|
||||
import { toggleClineRule } from "../core/controller/file/toggleClineRule"
|
||||
import { toggleCursorRule } from "../core/controller/file/toggleCursorRule"
|
||||
import { toggleWindsurfRule } from "../core/controller/file/toggleWindsurfRule"
|
||||
import { refreshRules } from "../core/controller/file/refreshRules"
|
||||
|
||||
// Mcp Service
|
||||
import { toggleMcpServer } from "../core/controller/mcp/toggleMcpServer"
|
||||
@@ -65,7 +64,6 @@ import { toggleFavoriteModel } from "../core/controller/state/toggleFavoriteMode
|
||||
import { resetState } from "../core/controller/state/resetState"
|
||||
import { togglePlanActMode } from "../core/controller/state/togglePlanActMode"
|
||||
import { updateTerminalConnectionTimeout } from "../core/controller/state/updateTerminalConnectionTimeout"
|
||||
import { updateAutoApprovalSettings } from "../core/controller/state/updateAutoApprovalSettings"
|
||||
|
||||
// Task Service
|
||||
import { cancelTask } from "../core/controller/task/cancelTask"
|
||||
@@ -83,7 +81,6 @@ import { taskCompletionViewChanges } from "../core/controller/task/taskCompletio
|
||||
|
||||
// Ui Service
|
||||
import { scrollToSettings } from "../core/controller/ui/scrollToSettings"
|
||||
import { onDidShowAnnouncement } from "../core/controller/ui/onDidShowAnnouncement"
|
||||
|
||||
// Web Service
|
||||
import { checkIsImageUrl } from "../core/controller/web/checkIsImageUrl"
|
||||
@@ -133,7 +130,6 @@ export function addServices(
|
||||
toggleClineRule: wrapper(toggleClineRule, controller),
|
||||
toggleCursorRule: wrapper(toggleCursorRule, controller),
|
||||
toggleWindsurfRule: wrapper(toggleWindsurfRule, controller),
|
||||
refreshRules: wrapper(refreshRules, controller),
|
||||
})
|
||||
|
||||
// Mcp Service
|
||||
@@ -172,7 +168,6 @@ export function addServices(
|
||||
resetState: wrapper(resetState, controller),
|
||||
togglePlanActMode: wrapper(togglePlanActMode, controller),
|
||||
updateTerminalConnectionTimeout: wrapper(updateTerminalConnectionTimeout, controller),
|
||||
updateAutoApprovalSettings: wrapper(updateAutoApprovalSettings, controller),
|
||||
})
|
||||
|
||||
// Task Service
|
||||
@@ -194,7 +189,6 @@ export function addServices(
|
||||
// Ui Service
|
||||
server.addService(proto.cline.UiService.service, {
|
||||
scrollToSettings: wrapper(scrollToSettings, controller),
|
||||
onDidShowAnnouncement: wrapper(onDidShowAnnouncement, controller),
|
||||
})
|
||||
|
||||
// Web Service
|
||||
|
||||
Generated
+62
-51
@@ -11,7 +11,6 @@
|
||||
"@floating-ui/react": "^0.27.4",
|
||||
"@heroui/react": "^2.8.0-beta.2",
|
||||
"@vscode/webview-ui-toolkit": "^1.4.0",
|
||||
"clsx": "^2.1.1",
|
||||
"debounce": "^2.1.1",
|
||||
"dompurify": "^3.2.4",
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
@@ -20,7 +19,6 @@
|
||||
"fuse.js": "^7.0.0",
|
||||
"fzf": "^0.5.2",
|
||||
"katex": "^0.16.22",
|
||||
"lucide-react": "^0.511.0",
|
||||
"mermaid": "^11.4.1",
|
||||
"package.json": "^2.0.1",
|
||||
"posthog-js": "^1.224.0",
|
||||
@@ -39,7 +37,6 @@
|
||||
"remark-math": "^6.0.0",
|
||||
"remark-stringify": "^11.0.0",
|
||||
"styled-components": "^6.1.15",
|
||||
"tailwind-merge": "^3.3.0",
|
||||
"unified": "^11.0.5",
|
||||
"uuid": "^9.0.1"
|
||||
},
|
||||
@@ -2516,14 +2513,6 @@
|
||||
"react": ">=18 || >=19.0.0-rc.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@heroui/system-rsc/node_modules/clsx": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz",
|
||||
"integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/@heroui/table": {
|
||||
"version": "2.2.17-beta.2",
|
||||
"resolved": "https://registry.npmjs.org/@heroui/table/-/table-2.2.17-beta.2.tgz",
|
||||
@@ -2601,23 +2590,6 @@
|
||||
"tailwindcss": ">=4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@heroui/theme/node_modules/clsx": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz",
|
||||
"integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/@heroui/theme/node_modules/tailwind-merge": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.0.2.tgz",
|
||||
"integrity": "sha512-l7z+OYZ7mu3DTqrL88RiKrKIqO3NcpEO8V/Od04bNpvk0kiIFndGEoqfuzvj4yuhRkHKjRkII2z+KS2HfPcSxw==",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/dcastil"
|
||||
}
|
||||
},
|
||||
"node_modules/@heroui/toast": {
|
||||
"version": "2.0.8-beta.2",
|
||||
"resolved": "https://registry.npmjs.org/@heroui/toast/-/toast-2.0.8-beta.2.tgz",
|
||||
@@ -3643,6 +3615,15 @@
|
||||
"react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-aria/focus/node_modules/clsx": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
|
||||
"integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-aria/form": {
|
||||
"version": "3.0.14",
|
||||
"resolved": "https://registry.npmjs.org/@react-aria/form/-/form-3.0.14.tgz",
|
||||
@@ -3858,6 +3839,15 @@
|
||||
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-aria/grid/node_modules/clsx": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
|
||||
"integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-aria/i18n": {
|
||||
"version": "3.12.7",
|
||||
"resolved": "https://registry.npmjs.org/@react-aria/i18n/-/i18n-3.12.7.tgz",
|
||||
@@ -3980,6 +3970,15 @@
|
||||
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-aria/landmark/node_modules/clsx": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
|
||||
"integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-aria/link": {
|
||||
"version": "3.7.10",
|
||||
"resolved": "https://registry.npmjs.org/@react-aria/link/-/link-3.7.10.tgz",
|
||||
@@ -4291,6 +4290,15 @@
|
||||
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-aria/spinbutton/node_modules/clsx": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
|
||||
"integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-aria/ssr": {
|
||||
"version": "3.9.7",
|
||||
"resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.9.7.tgz",
|
||||
@@ -4527,6 +4535,15 @@
|
||||
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-aria/toggle/node_modules/clsx": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
|
||||
"integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-aria/toolbar": {
|
||||
"version": "3.0.0-beta.14",
|
||||
"resolved": "https://registry.npmjs.org/@react-aria/toolbar/-/toolbar-3.0.0-beta.14.tgz",
|
||||
@@ -4580,6 +4597,15 @@
|
||||
"react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-aria/utils/node_modules/clsx": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
|
||||
"integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-aria/visually-hidden": {
|
||||
"version": "3.8.21",
|
||||
"resolved": "https://registry.npmjs.org/@react-aria/visually-hidden/-/visually-hidden-3.8.21.tgz",
|
||||
@@ -8252,9 +8278,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/clsx": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
|
||||
"integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz",
|
||||
"integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
@@ -12460,14 +12487,6 @@
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/lucide-react": {
|
||||
"version": "0.511.0",
|
||||
"resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.511.0.tgz",
|
||||
"integrity": "sha512-VK5a2ydJ7xm8GvBeKLS9mu1pVK6ucef9780JVUjw6bAjJL/QXnd4Y0p7SPeOUMC27YhzNCZvm5d/QX0Tp3rc0w==",
|
||||
"peerDependencies": {
|
||||
"react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/lz-string": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz",
|
||||
@@ -15846,9 +15865,10 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/tailwind-merge": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.3.0.tgz",
|
||||
"integrity": "sha512-fyW/pEfcQSiigd5SNn0nApUOxx0zB/dm6UDU/rEwc2c3sX2smWUNbapHv+QRqLGVp9GWX3THIa7MUGPo+YkDzQ==",
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.0.2.tgz",
|
||||
"integrity": "sha512-l7z+OYZ7mu3DTqrL88RiKrKIqO3NcpEO8V/Od04bNpvk0kiIFndGEoqfuzvj4yuhRkHKjRkII2z+KS2HfPcSxw==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/dcastil"
|
||||
@@ -15870,15 +15890,6 @@
|
||||
"tailwindcss": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/tailwind-variants/node_modules/tailwind-merge": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.0.2.tgz",
|
||||
"integrity": "sha512-l7z+OYZ7mu3DTqrL88RiKrKIqO3NcpEO8V/Od04bNpvk0kiIFndGEoqfuzvj4yuhRkHKjRkII2z+KS2HfPcSxw==",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/dcastil"
|
||||
}
|
||||
},
|
||||
"node_modules/tailwindcss": {
|
||||
"version": "4.1.5",
|
||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.5.tgz",
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
"@floating-ui/react": "^0.27.4",
|
||||
"@heroui/react": "^2.8.0-beta.2",
|
||||
"@vscode/webview-ui-toolkit": "^1.4.0",
|
||||
"clsx": "^2.1.1",
|
||||
"debounce": "^2.1.1",
|
||||
"dompurify": "^3.2.4",
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
@@ -27,7 +26,6 @@
|
||||
"fuse.js": "^7.0.0",
|
||||
"fzf": "^0.5.2",
|
||||
"katex": "^0.16.22",
|
||||
"lucide-react": "^0.511.0",
|
||||
"mermaid": "^11.4.1",
|
||||
"package.json": "^2.0.1",
|
||||
"posthog-js": "^1.224.0",
|
||||
@@ -46,7 +44,6 @@
|
||||
"remark-math": "^6.0.0",
|
||||
"remark-stringify": "^11.0.0",
|
||||
"styled-components": "^6.1.15",
|
||||
"tailwind-merge": "^3.3.0",
|
||||
"unified": "^11.0.5",
|
||||
"uuid": "^9.0.1"
|
||||
},
|
||||
|
||||
+2
-12
@@ -5,10 +5,9 @@ import SettingsView from "./components/settings/SettingsView"
|
||||
import WelcomeView from "./components/welcome/WelcomeView"
|
||||
import AccountView from "./components/account/AccountView"
|
||||
import { useExtensionState } from "./context/ExtensionStateContext"
|
||||
import { UiServiceClient } from "./services/grpc-client"
|
||||
import { vscode } from "./utils/vscode"
|
||||
import McpView from "./components/mcp/configuration/McpConfigurationView"
|
||||
import { Providers } from "./Providers"
|
||||
import { Boolean, EmptyRequest } from "@shared/proto/common"
|
||||
|
||||
const AppContent = () => {
|
||||
const {
|
||||
@@ -22,7 +21,6 @@ const AppContent = () => {
|
||||
showAccount,
|
||||
showAnnouncement,
|
||||
setShowAnnouncement,
|
||||
setShouldShowAnnouncement,
|
||||
closeMcpView,
|
||||
navigateToHistory,
|
||||
hideSettings,
|
||||
@@ -34,15 +32,7 @@ const AppContent = () => {
|
||||
useEffect(() => {
|
||||
if (shouldShowAnnouncement) {
|
||||
setShowAnnouncement(true)
|
||||
|
||||
// Use the gRPC client instead of direct WebviewMessage
|
||||
UiServiceClient.onDidShowAnnouncement({} as EmptyRequest)
|
||||
.then((response: Boolean) => {
|
||||
setShouldShowAnnouncement(response.value)
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("Failed to acknowledge announcement:", error)
|
||||
})
|
||||
vscode.postMessage({ type: "didShowAnnouncement" })
|
||||
}
|
||||
}, [shouldShowAnnouncement])
|
||||
|
||||
|
||||
@@ -5,44 +5,32 @@ import { posthogConfig } from "@shared/services/config/posthog-config"
|
||||
import { useExtensionState } from "./context/ExtensionStateContext"
|
||||
|
||||
export function CustomPostHogProvider({ children }: { children: ReactNode }) {
|
||||
const { telemetrySetting, distinctId, version } = useExtensionState()
|
||||
const { telemetrySetting, vscMachineId } = useExtensionState()
|
||||
const isTelemetryEnabled = telemetrySetting !== "disabled"
|
||||
|
||||
useEffect(() => {
|
||||
posthog.init(posthogConfig.apiKey, {
|
||||
api_host: posthogConfig.host,
|
||||
ui_host: posthogConfig.uiHost,
|
||||
disable_session_recording: true,
|
||||
capture_pageview: false,
|
||||
capture_dead_clicks: true,
|
||||
})
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
if (distinctId.length === 0 || version.length === 0) {
|
||||
if (vscMachineId.length === 0) {
|
||||
return
|
||||
}
|
||||
|
||||
posthog.set_config({
|
||||
before_send: (payload: any) => {
|
||||
if (payload?.properties) {
|
||||
payload.properties.extension_version = version
|
||||
payload.properties.distinct_id = distinctId
|
||||
}
|
||||
return payload
|
||||
posthog.init(posthogConfig.apiKey, {
|
||||
api_host: posthogConfig.host,
|
||||
ui_host: posthogConfig.uiHost,
|
||||
opt_out_capturing_by_default: true,
|
||||
disable_session_recording: true,
|
||||
capture_pageview: false,
|
||||
capture_dead_clicks: true,
|
||||
bootstrap: {
|
||||
distinctID: vscMachineId,
|
||||
},
|
||||
})
|
||||
|
||||
const optedIn = posthog.has_opted_in_capturing()
|
||||
const optedOut = posthog.has_opted_out_capturing()
|
||||
|
||||
if (isTelemetryEnabled && !optedIn) {
|
||||
if (isTelemetryEnabled) {
|
||||
posthog.opt_in_capturing()
|
||||
posthog.identify(distinctId)
|
||||
} else if (!isTelemetryEnabled && !optedOut) {
|
||||
} else {
|
||||
posthog.opt_out_capturing()
|
||||
}
|
||||
}, [isTelemetryEnabled, distinctId, version])
|
||||
}, [isTelemetryEnabled, vscMachineId])
|
||||
|
||||
return <PostHogProvider client={posthog}>{children}</PostHogProvider>
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ interface ConnectionInfo {
|
||||
}
|
||||
|
||||
export const BrowserSettingsMenu = () => {
|
||||
const { browserSettings, navigateToSettings } = useExtensionState()
|
||||
const { browserSettings } = useExtensionState()
|
||||
const containerRef = useRef<HTMLDivElement>(null)
|
||||
const [showInfoPopover, setShowInfoPopover] = useState(false)
|
||||
const [connectionInfo, setConnectionInfo] = useState<ConnectionInfo>({
|
||||
@@ -65,13 +65,15 @@ export const BrowserSettingsMenu = () => {
|
||||
}, [showInfoPopover])
|
||||
|
||||
const openBrowserSettings = () => {
|
||||
// First open the settings panel using direct navigation
|
||||
navigateToSettings()
|
||||
// First open the settings panel
|
||||
vscode.postMessage({
|
||||
type: "openSettings",
|
||||
})
|
||||
|
||||
// After a short delay, send a message to scroll to browser settings
|
||||
setTimeout(async () => {
|
||||
try {
|
||||
await UiServiceClient.scrollToSettings({ value: "browser" })
|
||||
await UiServiceClient.scrollToSettings({ value: "browser-settings-section" })
|
||||
} catch (error) {
|
||||
console.error("Error scrolling to browser settings:", error)
|
||||
}
|
||||
|
||||
@@ -44,15 +44,21 @@ const Announcement = ({ version, hideAnnouncement }: AnnouncementProps) => {
|
||||
</h3>
|
||||
<ul style={ulStyle}>
|
||||
<li>
|
||||
<b>Claude 4 Models:</b> Now with support for Anthropic Claude Sonnet 4 and Claude Opus 4 in both Anthropic and
|
||||
Vertex providers.
|
||||
<b>Workflows:</b> Create and manage workflow files that can be injected into conversations via slash commands,
|
||||
making it easy to automate repetitive tasks.
|
||||
</li>
|
||||
<li>
|
||||
<b>New Settings Page:</b> Redesigned settings, now split into tabs for easier navigation and a cleaner
|
||||
experience.
|
||||
<b>Collapsible Task List:</b> Hide your recent tasks when sharing your screen to keep your prompts private.
|
||||
</li>
|
||||
<li>
|
||||
<b>Nebius AI Studio:</b> Added Nebius AI Studio as a new provider. (Thanks @Aktsvigun!)
|
||||
<b>Global Endpoint for Vertex AI:</b> Improved availability and reduced rate limiting errors for Vertex AI
|
||||
users.
|
||||
</li>
|
||||
<li>
|
||||
<b>New User Experience:</b> Special components and guidance for new users to help them get started with Cline.
|
||||
</li>
|
||||
<li>
|
||||
<b>UI Improvements:</b> Fixed loading states and improved settings organization for a smoother experience.
|
||||
</li>
|
||||
</ul>
|
||||
<Accordion isCompact className="pl-0">
|
||||
@@ -67,26 +73,6 @@ const Announcement = ({ version, hideAnnouncement }: AnnouncementProps) => {
|
||||
"text-[var(--vscode-foreground)] mb-0.5 -rotate-180 data-[open=true]:-rotate-90 rtl:rotate-0 rtl:data-[open=true]:-rotate-90",
|
||||
}}>
|
||||
<ul style={ulStyle}>
|
||||
<li>
|
||||
<b>Workflows:</b> Create and manage workflow files that can be injected into conversations via slash
|
||||
commands, making it easy to automate repetitive tasks.
|
||||
</li>
|
||||
<li>
|
||||
<b>Collapsible Task List:</b> Hide your recent tasks when sharing your screen to keep your prompts
|
||||
private.
|
||||
</li>
|
||||
<li>
|
||||
<b>Global Endpoint for Vertex AI:</b> Improved availability and reduced rate limiting errors for
|
||||
Vertex AI users.
|
||||
</li>
|
||||
<li>
|
||||
<b>New User Experience:</b> Special components and guidance for new users to help them get started
|
||||
with Cline.
|
||||
</li>
|
||||
<li>
|
||||
<b>UI Improvements:</b> Fixed loading states and improved settings organization for a smoother
|
||||
experience.
|
||||
</li>
|
||||
<li>
|
||||
<b>Task Timeline:</b> See the history of your coding journey with a visual timeline of checkpoints.
|
||||
</li>
|
||||
|
||||
@@ -39,7 +39,6 @@ import SlashCommandMenu from "@/components/chat/SlashCommandMenu"
|
||||
import { ChatSettings } from "@shared/ChatSettings"
|
||||
import ServersToggleModal from "./ServersToggleModal"
|
||||
import ClineRulesToggleModal from "../cline-rules/ClineRulesToggleModal"
|
||||
import { PlanActMode } from "@shared/proto/state"
|
||||
|
||||
const getImageDimensions = (dataUrl: string): Promise<{ width: number; height: number }> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
@@ -260,15 +259,7 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
|
||||
},
|
||||
ref,
|
||||
) => {
|
||||
const {
|
||||
filePaths,
|
||||
chatSettings,
|
||||
apiConfiguration,
|
||||
openRouterModels,
|
||||
platform,
|
||||
localWorkflowToggles,
|
||||
globalWorkflowToggles,
|
||||
} = useExtensionState()
|
||||
const { filePaths, chatSettings, apiConfiguration, openRouterModels, platform, workflowToggles } = useExtensionState()
|
||||
const [isTextAreaFocused, setIsTextAreaFocused] = useState(false)
|
||||
const [isDraggingOver, setIsDraggingOver] = useState(false)
|
||||
const [gitCommits, setGitCommits] = useState<GitCommit[]>([])
|
||||
@@ -492,11 +483,7 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
|
||||
setSelectedSlashCommandsIndex((prevIndex) => {
|
||||
const direction = event.key === "ArrowUp" ? -1 : 1
|
||||
// Get commands with workflow toggles
|
||||
const allCommands = getMatchingSlashCommands(
|
||||
slashCommandsQuery,
|
||||
localWorkflowToggles,
|
||||
globalWorkflowToggles,
|
||||
)
|
||||
const allCommands = getMatchingSlashCommands(slashCommandsQuery, workflowToggles)
|
||||
|
||||
if (allCommands.length === 0) {
|
||||
return prevIndex
|
||||
@@ -514,7 +501,7 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
|
||||
|
||||
if ((event.key === "Enter" || event.key === "Tab") && selectedSlashCommandsIndex !== -1) {
|
||||
event.preventDefault()
|
||||
const commands = getMatchingSlashCommands(slashCommandsQuery, localWorkflowToggles, globalWorkflowToggles)
|
||||
const commands = getMatchingSlashCommands(slashCommandsQuery, workflowToggles)
|
||||
if (commands.length > 0) {
|
||||
handleSlashCommandsSelect(commands[selectedSlashCommandsIndex])
|
||||
}
|
||||
@@ -917,7 +904,7 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
|
||||
|
||||
// extract and validate the exact command text
|
||||
const commandText = processedText.substring(slashIndex + 1, endIndex)
|
||||
const isValidCommand = validateSlashCommand(commandText, localWorkflowToggles, globalWorkflowToggles)
|
||||
const isValidCommand = validateSlashCommand(commandText, workflowToggles)
|
||||
|
||||
if (isValidCommand) {
|
||||
const fullCommand = processedText.substring(slashIndex, endIndex) // includes slash
|
||||
@@ -930,7 +917,7 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
|
||||
highlightLayerRef.current.innerHTML = processedText
|
||||
highlightLayerRef.current.scrollTop = textAreaRef.current.scrollTop
|
||||
highlightLayerRef.current.scrollLeft = textAreaRef.current.scrollLeft
|
||||
}, [localWorkflowToggles, globalWorkflowToggles])
|
||||
}, [workflowToggles])
|
||||
|
||||
useLayoutEffect(() => {
|
||||
updateHighlights()
|
||||
@@ -978,8 +965,9 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
|
||||
changeModeDelay = 250 // necessary to let the api config update (we send message and wait for it to be saved) FIXME: this is a hack and we ideally should check for api config changes, then wait for it to be saved, before switching modes
|
||||
}
|
||||
setTimeout(() => {
|
||||
const newMode = chatSettings.mode === "plan" ? PlanActMode.ACT : PlanActMode.PLAN
|
||||
StateServiceClient.togglePlanActMode({
|
||||
const newMode = chatSettings.mode === "plan" ? "act" : "plan"
|
||||
vscode.postMessage({
|
||||
type: "togglePlanActMode",
|
||||
chatSettings: {
|
||||
mode: newMode,
|
||||
preferredLanguage: chatSettings.preferredLanguage,
|
||||
@@ -1083,7 +1071,6 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
|
||||
case "litellm":
|
||||
return `${selectedProvider}:${apiConfiguration.liteLlmModelId}`
|
||||
case "requesty":
|
||||
return `${selectedProvider}:${apiConfiguration.requestyModelId}`
|
||||
case "anthropic":
|
||||
case "openrouter":
|
||||
default:
|
||||
@@ -1412,8 +1399,7 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
|
||||
setSelectedIndex={setSelectedSlashCommandsIndex}
|
||||
onMouseDown={handleMenuMouseDown}
|
||||
query={slashCommandsQuery}
|
||||
localWorkflowToggles={localWorkflowToggles}
|
||||
globalWorkflowToggles={globalWorkflowToggles}
|
||||
workflowToggles={workflowToggles}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -7,8 +7,7 @@ interface SlashCommandMenuProps {
|
||||
setSelectedIndex: (index: number) => void
|
||||
onMouseDown: () => void
|
||||
query: string
|
||||
localWorkflowToggles?: Record<string, boolean>
|
||||
globalWorkflowToggles?: Record<string, boolean>
|
||||
workflowToggles?: Record<string, boolean>
|
||||
}
|
||||
|
||||
const SlashCommandMenu: React.FC<SlashCommandMenuProps> = ({
|
||||
@@ -17,8 +16,7 @@ const SlashCommandMenu: React.FC<SlashCommandMenuProps> = ({
|
||||
setSelectedIndex,
|
||||
onMouseDown,
|
||||
query,
|
||||
localWorkflowToggles = {},
|
||||
globalWorkflowToggles = {},
|
||||
workflowToggles = {},
|
||||
}) => {
|
||||
const menuRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
@@ -46,7 +44,7 @@ const SlashCommandMenu: React.FC<SlashCommandMenuProps> = ({
|
||||
}, [selectedIndex])
|
||||
|
||||
// Filter commands based on query
|
||||
const filteredCommands = getMatchingSlashCommands(query, localWorkflowToggles, globalWorkflowToggles)
|
||||
const filteredCommands = getMatchingSlashCommands(query, workflowToggles)
|
||||
const defaultCommands = filteredCommands.filter((cmd) => cmd.section === "default" || !cmd.section)
|
||||
const workflowCommands = filteredCommands.filter((cmd) => cmd.section === "custom")
|
||||
|
||||
|
||||
@@ -8,8 +8,8 @@ interface AutoApproveMenuItemProps {
|
||||
action: ActionMetadata
|
||||
isChecked: (action: ActionMetadata) => boolean
|
||||
isFavorited?: (action: ActionMetadata) => boolean
|
||||
onToggle: (action: ActionMetadata, checked: boolean) => Promise<void>
|
||||
onToggleFavorite?: (actionId: string) => Promise<void>
|
||||
onToggle: (action: ActionMetadata, checked: boolean) => void
|
||||
onToggleFavorite?: (actionId: string) => void
|
||||
condensed?: boolean
|
||||
showIcon?: boolean
|
||||
}
|
||||
@@ -83,9 +83,9 @@ const AutoApproveMenuItem = ({
|
||||
const checked = isChecked(action)
|
||||
const favorited = isFavorited?.(action)
|
||||
|
||||
const onChange = async (e: Event) => {
|
||||
const onChange = (e: Event) => {
|
||||
e.stopPropagation()
|
||||
await onToggle(action, !checked)
|
||||
onToggle(action, !checked)
|
||||
}
|
||||
|
||||
const content = (
|
||||
@@ -107,10 +107,9 @@ const AutoApproveMenuItem = ({
|
||||
style={{
|
||||
cursor: "pointer",
|
||||
}}
|
||||
onClick={async (e) => {
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
if (action.id === "enableAll") return
|
||||
await onToggleFavorite?.(action.id)
|
||||
onToggleFavorite?.(action.id)
|
||||
}}
|
||||
/>
|
||||
</HeroTooltip>
|
||||
|
||||
@@ -170,13 +170,13 @@ const AutoApproveModal: React.FC<AutoApproveModalProps> = ({
|
||||
<VSCodeTextField
|
||||
className="flex-1 w-full pr-[35px] ml-4"
|
||||
value={autoApprovalSettings.maxRequests.toString()}
|
||||
onInput={async (e) => {
|
||||
onInput={(e) => {
|
||||
const input = e.target as HTMLInputElement
|
||||
// Remove any non-numeric characters
|
||||
input.value = input.value.replace(/[^0-9]/g, "")
|
||||
const value = parseInt(input.value)
|
||||
if (!isNaN(value) && value > 0) {
|
||||
await updateMaxRequests(value)
|
||||
updateMaxRequests(value)
|
||||
}
|
||||
}}
|
||||
onKeyDown={(e) => {
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
import { StateServiceClient } from "@/services/grpc-client"
|
||||
import { AutoApprovalSettings } from "@shared/AutoApprovalSettings"
|
||||
import { convertAutoApprovalSettingsToProto } from "@shared/proto-conversions/models/auto-approval-settings-conversion"
|
||||
|
||||
/**
|
||||
* Updates auto approval settings using the gRPC/Protobus client
|
||||
* @param settings The auto approval settings to update
|
||||
* @throws Error if the update fails
|
||||
*/
|
||||
export async function updateAutoApproveSettings(settings: AutoApprovalSettings) {
|
||||
try {
|
||||
const protoSettings = convertAutoApprovalSettingsToProto(settings)
|
||||
await StateServiceClient.updateAutoApprovalSettings(protoSettings)
|
||||
} catch (error) {
|
||||
console.error("Failed to update auto approval settings:", error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
@@ -4,12 +4,11 @@ import { useExtensionState } from "@/context/ExtensionStateContext"
|
||||
import { CODE_BLOCK_BG_COLOR } from "@/components/common/CodeBlock"
|
||||
import { vscode } from "@/utils/vscode"
|
||||
import { FileServiceClient } from "@/services/grpc-client"
|
||||
import { VSCodeButton, VSCodeLink } from "@vscode/webview-ui-toolkit/react"
|
||||
import { VSCodeButton } from "@vscode/webview-ui-toolkit/react"
|
||||
import RulesToggleList from "./RulesToggleList"
|
||||
import Tooltip from "@/components/common/Tooltip"
|
||||
import styled from "styled-components"
|
||||
import { ClineRulesToggles, RefreshedRules, ToggleWindsurfRuleRequest } from "@shared/proto/file"
|
||||
import { EmptyRequest } from "@shared/proto/common"
|
||||
import { ClineRulesToggles, ToggleWindsurfRuleRequest } from "@shared/proto/file"
|
||||
|
||||
const ClineRulesToggleModal: React.FC = () => {
|
||||
const {
|
||||
@@ -17,14 +16,11 @@ const ClineRulesToggleModal: React.FC = () => {
|
||||
localClineRulesToggles = {},
|
||||
localCursorRulesToggles = {},
|
||||
localWindsurfRulesToggles = {},
|
||||
localWorkflowToggles = {},
|
||||
globalWorkflowToggles = {},
|
||||
workflowToggles = {},
|
||||
setGlobalClineRulesToggles,
|
||||
setLocalClineRulesToggles,
|
||||
setLocalCursorRulesToggles,
|
||||
setLocalWindsurfRulesToggles,
|
||||
setLocalWorkflowToggles,
|
||||
setGlobalWorkflowToggles,
|
||||
} = useExtensionState()
|
||||
const [isVisible, setIsVisible] = useState(false)
|
||||
const buttonRef = useRef<HTMLDivElement>(null)
|
||||
@@ -36,31 +32,7 @@ const ClineRulesToggleModal: React.FC = () => {
|
||||
|
||||
useEffect(() => {
|
||||
if (isVisible) {
|
||||
FileServiceClient.refreshRules({} as EmptyRequest)
|
||||
.then((response: RefreshedRules) => {
|
||||
// Update state with the response data using all available setters
|
||||
if (response.globalClineRulesToggles?.toggles) {
|
||||
setGlobalClineRulesToggles(response.globalClineRulesToggles.toggles)
|
||||
}
|
||||
if (response.localClineRulesToggles?.toggles) {
|
||||
setLocalClineRulesToggles(response.localClineRulesToggles.toggles)
|
||||
}
|
||||
if (response.localCursorRulesToggles?.toggles) {
|
||||
setLocalCursorRulesToggles(response.localCursorRulesToggles.toggles)
|
||||
}
|
||||
if (response.localWindsurfRulesToggles?.toggles) {
|
||||
setLocalWindsurfRulesToggles(response.localWindsurfRulesToggles.toggles)
|
||||
}
|
||||
if (response.localWorkflowToggles?.toggles) {
|
||||
setLocalWorkflowToggles(response.localWorkflowToggles.toggles)
|
||||
}
|
||||
if (response.globalWorkflowToggles?.toggles) {
|
||||
setGlobalWorkflowToggles(response.globalWorkflowToggles.toggles)
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("Failed to refresh rules:", error)
|
||||
})
|
||||
vscode.postMessage({ type: "refreshClineRules" })
|
||||
}
|
||||
}, [isVisible])
|
||||
|
||||
@@ -82,11 +54,7 @@ const ClineRulesToggleModal: React.FC = () => {
|
||||
.map(([path, enabled]): [string, boolean] => [path, enabled as boolean])
|
||||
.sort(([a], [b]) => a.localeCompare(b))
|
||||
|
||||
const localWorkflows = Object.entries(localWorkflowToggles || {})
|
||||
.map(([path, enabled]): [string, boolean] => [path, enabled as boolean])
|
||||
.sort(([a], [b]) => a.localeCompare(b))
|
||||
|
||||
const globalWorkflows = Object.entries(globalWorkflowToggles || {})
|
||||
const workflows = Object.entries(workflowToggles || {})
|
||||
.map(([path, enabled]): [string, boolean] => [path, enabled as boolean])
|
||||
.sort(([a], [b]) => a.localeCompare(b))
|
||||
|
||||
@@ -142,12 +110,11 @@ const ClineRulesToggleModal: React.FC = () => {
|
||||
})
|
||||
}
|
||||
|
||||
const toggleWorkflow = (isGlobal: boolean, workflowPath: string, enabled: boolean) => {
|
||||
const toggleWorkflow = (workflowPath: string, enabled: boolean) => {
|
||||
vscode.postMessage({
|
||||
type: "toggleWorkflow",
|
||||
workflowPath,
|
||||
enabled,
|
||||
isGlobal,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -232,13 +199,7 @@ const ClineRulesToggleModal: React.FC = () => {
|
||||
{currentView === "rules" ? (
|
||||
<p>
|
||||
Rules allow you to provide Cline with system-level guidance. Think of them as a persistent way to
|
||||
include context and preferences for your projects or globally for every conversation.{" "}
|
||||
<VSCodeLink
|
||||
href="https://docs.cline.bot/features/cline-rules"
|
||||
style={{ display: "inline" }}
|
||||
className="text-xs">
|
||||
Docs
|
||||
</VSCodeLink>
|
||||
include context and preferences for your projects or globally for every conversation.
|
||||
</p>
|
||||
) : (
|
||||
<p>
|
||||
@@ -249,13 +210,7 @@ const ClineRulesToggleModal: React.FC = () => {
|
||||
text-[var(--vscode-foreground)] font-bold">
|
||||
/workflow-name
|
||||
</span>{" "}
|
||||
in the chat.{" "}
|
||||
<VSCodeLink
|
||||
href="https://docs.cline.bot/features/slash-commands/workflows"
|
||||
style={{ display: "inline" }}
|
||||
className="text-xs">
|
||||
Docs
|
||||
</VSCodeLink>
|
||||
in the chat.
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
@@ -309,35 +264,19 @@ const ClineRulesToggleModal: React.FC = () => {
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
{/* Global Workflows Section */}
|
||||
<div className="mb-3">
|
||||
<div className="text-sm font-normal mb-2">Global Workflows</div>
|
||||
<RulesToggleList
|
||||
rules={globalWorkflows}
|
||||
toggleRule={(rulePath, enabled) => toggleWorkflow(true, rulePath, enabled)}
|
||||
listGap="small"
|
||||
isGlobal={true}
|
||||
ruleType={"workflow"}
|
||||
showNewRule={true}
|
||||
showNoRules={false}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Local Workflows Section */}
|
||||
<div style={{ marginBottom: -10 }}>
|
||||
<div className="text-sm font-normal mb-2">Workspace Workflows</div>
|
||||
<RulesToggleList
|
||||
rules={localWorkflows}
|
||||
toggleRule={(rulePath, enabled) => toggleWorkflow(false, rulePath, enabled)}
|
||||
listGap="small"
|
||||
isGlobal={false}
|
||||
ruleType={"workflow"}
|
||||
showNewRule={true}
|
||||
showNoRules={false}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
/* Workflows section */
|
||||
<div style={{ marginBottom: -10 }}>
|
||||
<div className="text-sm font-normal mb-2">Workspace Workflows</div>
|
||||
<RulesToggleList
|
||||
rules={workflows}
|
||||
toggleRule={toggleWorkflow}
|
||||
listGap="small"
|
||||
isGlobal={false}
|
||||
ruleType={"workflow"}
|
||||
showNewRule={true}
|
||||
showNoRules={false}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -9,10 +9,8 @@ const RuleRow: React.FC<{
|
||||
ruleType: string
|
||||
toggleRule: (rulePath: string, enabled: boolean) => void
|
||||
}> = ({ rulePath, enabled, isGlobal, toggleRule, ruleType }) => {
|
||||
// Check if the path type is Windows
|
||||
const win32Path = /^[a-zA-Z]:\\/.test(rulePath)
|
||||
// Get the filename from the path for display
|
||||
const displayName = rulePath.split(win32Path ? "\\" : "/").pop() || rulePath
|
||||
const displayName = rulePath.split("/").pop() || rulePath
|
||||
|
||||
const getRuleTypeIcon = () => {
|
||||
switch (ruleType) {
|
||||
|
||||
@@ -1,111 +0,0 @@
|
||||
import React, { ReactNode } from "react"
|
||||
import { cn } from "@/utils/cn"
|
||||
import { VSCodeButton } from "@vscode/webview-ui-toolkit/react"
|
||||
import { AlertTriangle } from "lucide-react"
|
||||
import { OPENROUTER_MODEL_PICKER_Z_INDEX } from "../settings/OpenRouterModelPicker"
|
||||
|
||||
interface AlertDialogProps {
|
||||
open: boolean
|
||||
onOpenChange: (open: boolean) => void
|
||||
children: ReactNode
|
||||
}
|
||||
|
||||
export function AlertDialog({ open, onOpenChange, children }: AlertDialogProps) {
|
||||
if (!open) return null
|
||||
|
||||
// Close the dialog when clicking on the backdrop
|
||||
const handleBackdropClick = (e: React.MouseEvent) => {
|
||||
if (e.target === e.currentTarget) {
|
||||
onOpenChange(false)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(`fixed inset-0 bg-black/50 flex items-center justify-center`)}
|
||||
onClick={handleBackdropClick}
|
||||
style={{ zIndex: OPENROUTER_MODEL_PICKER_Z_INDEX + 50 }}>
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export function AlertDialogContent({ className, children, ...props }: React.HTMLAttributes<HTMLDivElement>) {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
`fixed top-[50%] left-[50%] grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%]`,
|
||||
className,
|
||||
)}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
{...props}>
|
||||
<div className="bg-[var(--vscode-editor-background)] rounded-sm gap-3 border border-[var(--vscode-panel-border)] p-4 shadow-lg sm:max-w-md">
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export function AlertDialogHeader({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
|
||||
return <div className={cn("flex flex-col gap-1 text-left", className)} {...props} />
|
||||
}
|
||||
|
||||
export function AlertDialogFooter({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
|
||||
return <div className={cn("flex flex-row justify-end gap-2 mt-4", className)} {...props} />
|
||||
}
|
||||
|
||||
export function AlertDialogTitle({ className, ...props }: React.HTMLAttributes<HTMLHeadingElement>) {
|
||||
return (
|
||||
<h2
|
||||
className={cn(
|
||||
"text-base font-medium text-[var(--vscode-editor-foreground)] flex items-center gap-2 text-left",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export function AlertDialogDescription({ className, ...props }: React.HTMLAttributes<HTMLParagraphElement>) {
|
||||
return <p className={cn("text-[var(--vscode-descriptionForeground)] text-sm text-left", className)} {...props} />
|
||||
}
|
||||
|
||||
export function AlertDialogAction({ className, ...props }: React.ComponentProps<typeof VSCodeButton>) {
|
||||
return <VSCodeButton appearance="primary" {...props} />
|
||||
}
|
||||
|
||||
export function AlertDialogCancel({ className, ...props }: React.ComponentProps<typeof VSCodeButton>) {
|
||||
return <VSCodeButton appearance="secondary" {...props} />
|
||||
}
|
||||
|
||||
export function UnsavedChangesDialog({
|
||||
open,
|
||||
onOpenChange,
|
||||
onConfirm,
|
||||
onCancel,
|
||||
}: {
|
||||
open: boolean
|
||||
onOpenChange: (open: boolean) => void
|
||||
onConfirm: () => void
|
||||
onCancel: () => void
|
||||
}) {
|
||||
return (
|
||||
<AlertDialog open={open} onOpenChange={onOpenChange}>
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle>
|
||||
<AlertTriangle className="w-5 h-5 text-[var(--vscode-errorForeground)]" />
|
||||
Unsaved Changes
|
||||
</AlertDialogTitle>
|
||||
<AlertDialogDescription>
|
||||
You have unsaved changes. Are you sure you want to discard them?
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel onClick={onCancel}>Cancel</AlertDialogCancel>
|
||||
<AlertDialogAction onClick={onConfirm}>Discard Changes</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
)
|
||||
}
|
||||
@@ -10,27 +10,6 @@ import type { Node } from "unist"
|
||||
import { useExtensionState } from "@/context/ExtensionStateContext"
|
||||
import { CODE_BLOCK_BG_COLOR } from "@/components/common/CodeBlock"
|
||||
import MermaidBlock from "@/components/common/MermaidBlock"
|
||||
import { StateServiceClient } from "@/services/grpc-client"
|
||||
import { PlanActMode } from "@shared/proto/state"
|
||||
|
||||
// Styled component for Act Mode text with more specific styling
|
||||
const ActModeHighlight: React.FC = () => (
|
||||
<span
|
||||
onClick={() => {
|
||||
StateServiceClient.togglePlanActMode({
|
||||
chatSettings: {
|
||||
mode: PlanActMode.ACT,
|
||||
},
|
||||
})
|
||||
}}
|
||||
title="Click to toggle to Act Mode"
|
||||
className="text-[var(--vscode-textLink-foreground)] hover:opacity-90 cursor-pointer inline-flex items-center gap-1">
|
||||
<div className="p-1 rounded-[12px] bg-[var(--vscode-editor-background)] flex items-center justify-end w-4 border-[1px] border-[var(--vscode-input-border)]">
|
||||
<div className="rounded-full bg-[var(--vscode-textLink-foreground)] w-2 h-2" />
|
||||
</div>
|
||||
Act Mode (⌘⇧A)
|
||||
</span>
|
||||
)
|
||||
|
||||
interface MarkdownBlockProps {
|
||||
markdown?: string
|
||||
@@ -76,68 +55,6 @@ const remarkUrlToLink = () => {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom remark plugin that highlights "to Act Mode" mentions and adds keyboard shortcut hint
|
||||
*/
|
||||
const remarkHighlightActMode = () => {
|
||||
return (tree: Node) => {
|
||||
visit(tree, "text", (node: any, index, parent) => {
|
||||
// Case-insensitive regex to match "to Act Mode" in various capitalizations
|
||||
// Using word boundaries to avoid matching within words
|
||||
// Added negative lookahead to avoid matching if already followed by the shortcut
|
||||
const actModeRegex = /\bto\s+Act\s+Mode\b(?!\s*\(⌘⇧A\))/i
|
||||
|
||||
if (!node.value.match(actModeRegex)) return
|
||||
|
||||
// Split the text by the matches
|
||||
const parts = node.value.split(actModeRegex)
|
||||
const matches = node.value.match(actModeRegex)
|
||||
|
||||
if (!matches || parts.length <= 1) return
|
||||
|
||||
const children: any[] = []
|
||||
|
||||
parts.forEach((part: string, i: number) => {
|
||||
// Add the text before the match
|
||||
if (part) children.push({ type: "text", value: part })
|
||||
|
||||
// Add the match, but only make "Act Mode" bold (not the "to" part)
|
||||
if (matches[i]) {
|
||||
// Extract "to" and "Act Mode" parts
|
||||
const matchText = matches[i]
|
||||
const toIndex = matchText.toLowerCase().indexOf("to")
|
||||
const actModeIndex = matchText.toLowerCase().indexOf("act mode", toIndex + 2)
|
||||
|
||||
if (toIndex !== -1 && actModeIndex !== -1) {
|
||||
// Add "to" as regular text
|
||||
const toPart = matchText.substring(toIndex, actModeIndex).trim()
|
||||
children.push({ type: "text", value: toPart + " " })
|
||||
|
||||
// Add "Act Mode" as bold with keyboard shortcut
|
||||
const actModePart = matchText.substring(actModeIndex)
|
||||
children.push({
|
||||
type: "strong",
|
||||
children: [{ type: "text", value: `${actModePart} (⌘⇧A)` }],
|
||||
})
|
||||
} else {
|
||||
// Fallback if we can't parse it correctly
|
||||
children.push({ type: "text", value: matchText + " " })
|
||||
children.push({
|
||||
type: "strong",
|
||||
children: [{ type: "text", value: `(⌘⇧A)` }],
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// Replace the original text node with our new nodes
|
||||
if (parent) {
|
||||
parent.children.splice(index, 1, ...children)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom remark plugin that prevents filenames with extensions from being parsed as bold text
|
||||
* For example: __init__.py should not be rendered as bold "init" followed by ".py"
|
||||
@@ -369,7 +286,6 @@ const MarkdownBlock = memo(({ markdown }: MarkdownBlockProps) => {
|
||||
remarkPlugins: [
|
||||
remarkPreventBoldFilenames,
|
||||
remarkUrlToLink,
|
||||
remarkHighlightActMode,
|
||||
remarkMath,
|
||||
() => {
|
||||
return (tree) => {
|
||||
@@ -413,27 +329,6 @@ const MarkdownBlock = memo(({ markdown }: MarkdownBlockProps) => {
|
||||
}
|
||||
return <code {...props} />
|
||||
},
|
||||
strong: (props: ComponentProps<"strong">) => {
|
||||
// Check if this is an "Act Mode" strong element by looking for the keyboard shortcut
|
||||
// Handle both string children and array of children cases
|
||||
const childrenText = React.Children.toArray(props.children)
|
||||
.map((child) => {
|
||||
if (typeof child === "string") return child
|
||||
if (typeof child === "object" && "props" in child && child.props.children)
|
||||
return String(child.props.children)
|
||||
return ""
|
||||
})
|
||||
.join("")
|
||||
|
||||
// Case-insensitive check for "Act Mode (⌘⇧A)" pattern
|
||||
// This ensures we only style the exact "Act Mode" mentions with keyboard shortcut
|
||||
// Using case-insensitive flag to catch all capitalization variations
|
||||
if (/^act mode\s*\(⌘⇧A\)$/i.test(childrenText)) {
|
||||
return <ActModeHighlight />
|
||||
}
|
||||
|
||||
return <strong {...props} />
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
@@ -1,93 +0,0 @@
|
||||
import React, { HTMLAttributes, useCallback, forwardRef } from "react"
|
||||
|
||||
import { useExtensionState } from "@/context/ExtensionStateContext"
|
||||
import { cn } from "@/utils/cn"
|
||||
|
||||
type TabProps = HTMLAttributes<HTMLDivElement>
|
||||
|
||||
export const Tab = ({ className, children, ...props }: TabProps) => (
|
||||
<div className={cn("fixed inset-0 flex flex-col", className)} {...props}>
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
|
||||
export const TabHeader = ({ className, children, ...props }: TabProps) => (
|
||||
<div className={cn("px-5 py-2.5 border-b border-[var(--vscode-panel-border)]", className)} {...props}>
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
|
||||
export const TabContent = ({ className, children, ...props }: TabProps) => {
|
||||
const onWheel = useCallback((e: React.WheelEvent<HTMLDivElement>) => {
|
||||
const target = e.target as HTMLElement
|
||||
|
||||
// Prevent scrolling if the target is a listbox or option
|
||||
// (e.g. selects, dropdowns, etc).
|
||||
if (target.role === "listbox" || target.role === "option") {
|
||||
return
|
||||
}
|
||||
|
||||
e.currentTarget.scrollTop += e.deltaY
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className={cn("flex-1 overflow-auto", className)} onWheel={onWheel} {...props}>
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export const TabList = forwardRef<
|
||||
HTMLDivElement,
|
||||
HTMLAttributes<HTMLDivElement> & {
|
||||
value: string
|
||||
onValueChange: (value: string) => void
|
||||
}
|
||||
>(({ children, className, value, onValueChange, ...props }, ref) => {
|
||||
const handleTabSelect = useCallback(
|
||||
(tabValue: string) => {
|
||||
console.log("Tab selected:", tabValue)
|
||||
onValueChange(tabValue)
|
||||
},
|
||||
[onValueChange],
|
||||
)
|
||||
|
||||
return (
|
||||
<div ref={ref} role="tablist" className={cn("flex", className)} {...props}>
|
||||
{React.Children.map(children, (child) => {
|
||||
if (React.isValidElement(child)) {
|
||||
// Make sure we're passing the correct props to the TabTrigger
|
||||
return React.cloneElement(child as React.ReactElement<any>, {
|
||||
isSelected: child.props.value === value,
|
||||
onSelect: () => handleTabSelect(child.props.value),
|
||||
})
|
||||
}
|
||||
return child
|
||||
})}
|
||||
</div>
|
||||
)
|
||||
})
|
||||
|
||||
export const TabTrigger = forwardRef<
|
||||
HTMLButtonElement,
|
||||
React.ButtonHTMLAttributes<HTMLButtonElement> & {
|
||||
value: string
|
||||
isSelected?: boolean
|
||||
onSelect?: () => void
|
||||
}
|
||||
>(({ children, className, value, isSelected, onSelect, ...props }, ref) => {
|
||||
// Ensure we're using the value prop correctly
|
||||
return (
|
||||
<button
|
||||
ref={ref}
|
||||
role="tab"
|
||||
aria-selected={isSelected}
|
||||
tabIndex={isSelected ? 0 : -1}
|
||||
className={cn("focus:outline-none", className)}
|
||||
onClick={onSelect}
|
||||
data-value={value} // Add data-value attribute for debugging
|
||||
{...props}>
|
||||
{children}
|
||||
</button>
|
||||
)
|
||||
})
|
||||
@@ -1,7 +1,6 @@
|
||||
import { VSCodeButton, VSCodeLink } from "@vscode/webview-ui-toolkit/react"
|
||||
import { memo, useState } from "react"
|
||||
import styled from "styled-components"
|
||||
import { useExtensionState } from "@/context/ExtensionStateContext"
|
||||
import { vscode } from "@/utils/vscode"
|
||||
import { TelemetrySetting } from "@shared/TelemetrySetting"
|
||||
|
||||
@@ -46,11 +45,9 @@ const ButtonContainer = styled.div`
|
||||
`
|
||||
|
||||
const TelemetryBanner = () => {
|
||||
const { navigateToSettings } = useExtensionState()
|
||||
|
||||
const handleOpenSettings = () => {
|
||||
handleClose()
|
||||
navigateToSettings()
|
||||
vscode.postMessage({ type: "openSettings" })
|
||||
}
|
||||
|
||||
const handleClose = () => {
|
||||
|
||||
+5
-15
@@ -1,12 +1,9 @@
|
||||
import { VSCodeButton, VSCodeLink } from "@vscode/webview-ui-toolkit/react"
|
||||
import { vscode } from "@/utils/vscode"
|
||||
import { useExtensionState } from "@/context/ExtensionStateContext"
|
||||
|
||||
import { UiServiceClient } from "@/services/grpc-client"
|
||||
|
||||
import ServersToggleList from "./ServersToggleList"
|
||||
const InstalledServersView = () => {
|
||||
const { mcpServers: servers, navigateToSettings } = useExtensionState()
|
||||
const { mcpServers: servers } = useExtensionState()
|
||||
|
||||
return (
|
||||
<div style={{ padding: "16px 20px" }}>
|
||||
@@ -49,17 +46,10 @@ const InstalledServersView = () => {
|
||||
<div style={{ textAlign: "center" }}>
|
||||
<VSCodeLink
|
||||
onClick={() => {
|
||||
// First open the settings panel using direct navigation
|
||||
navigateToSettings()
|
||||
|
||||
// After a short delay, send a message to scroll to browser settings
|
||||
setTimeout(async () => {
|
||||
try {
|
||||
await UiServiceClient.scrollToSettings({ value: "features" })
|
||||
} catch (error) {
|
||||
console.error("Error scrolling to mcp settings:", error)
|
||||
}
|
||||
}, 300)
|
||||
vscode.postMessage({
|
||||
type: "openExtensionSettings",
|
||||
text: "cline.mcp",
|
||||
})
|
||||
}}
|
||||
style={{ fontSize: "12px" }}>
|
||||
Advanced MCP Settings
|
||||
|
||||
@@ -47,14 +47,13 @@ import {
|
||||
askSageDefaultURL,
|
||||
xaiDefaultModelId,
|
||||
xaiModels,
|
||||
nebiusModels,
|
||||
nebiusDefaultModelId,
|
||||
sambanovaModels,
|
||||
sambanovaDefaultModelId,
|
||||
doubaoModels,
|
||||
doubaoDefaultModelId,
|
||||
liteLlmModelInfoSaneDefaults,
|
||||
} from "@shared/api"
|
||||
import { ExtensionMessage } from "@shared/ExtensionMessage"
|
||||
import { useExtensionState } from "@/context/ExtensionStateContext"
|
||||
import { vscode } from "@/utils/vscode"
|
||||
import { ModelsServiceClient } from "@/services/grpc-client"
|
||||
@@ -325,7 +324,6 @@ const ApiOptions = ({
|
||||
<VSCodeOption value="lmstudio">LM Studio</VSCodeOption>
|
||||
<VSCodeOption value="ollama">Ollama</VSCodeOption>
|
||||
<VSCodeOption value="litellm">LiteLLM</VSCodeOption>
|
||||
<VSCodeOption value="nebius">Nebius AI Studio</VSCodeOption>
|
||||
<VSCodeOption value="asksage">AskSage</VSCodeOption>
|
||||
<VSCodeOption value="xai">xAI</VSCodeOption>
|
||||
<VSCodeOption value="sambanova">SambaNova</VSCodeOption>
|
||||
@@ -893,14 +891,8 @@ const ApiOptions = ({
|
||||
</div>
|
||||
)}
|
||||
{(selectedModelId === "anthropic.claude-3-7-sonnet-20250219-v1:0" ||
|
||||
selectedModelId === "anthropic.claude-sonnet-4-20250514-v1:0" ||
|
||||
selectedModelId === "anthropic.claude-opus-4-20250514-v1:0" ||
|
||||
(apiConfiguration?.awsBedrockCustomSelected &&
|
||||
apiConfiguration?.awsBedrockCustomModelBaseId === "anthropic.claude-3-7-sonnet-20250219-v1:0") ||
|
||||
(apiConfiguration?.awsBedrockCustomSelected &&
|
||||
apiConfiguration?.awsBedrockCustomModelBaseId === "anthropic.claude-sonnet-4-20250514-v1:0") ||
|
||||
(apiConfiguration?.awsBedrockCustomSelected &&
|
||||
apiConfiguration?.awsBedrockCustomModelBaseId === "anthropic.claude-opus-4-20250514-v1:0")) && (
|
||||
apiConfiguration?.awsBedrockCustomModelBaseId === "anthropic.claude-3-7-sonnet-20250219-v1:0")) && (
|
||||
<ThinkingBudgetSlider apiConfiguration={apiConfiguration} setApiConfiguration={setApiConfiguration} />
|
||||
)}
|
||||
<ModelInfoView
|
||||
@@ -1653,7 +1645,7 @@ const ApiOptions = ({
|
||||
value={apiConfiguration?.liteLlmModelId || ""}
|
||||
style={{ width: "100%" }}
|
||||
onInput={handleInputChange("liteLlmModelId")}
|
||||
placeholder={"e.g. anthropic/claude-sonnet-4-20250514"}>
|
||||
placeholder={"e.g. anthropic/claude-3-7-sonnet-20250219"}>
|
||||
<span style={{ fontWeight: 500 }}>Model ID</span>
|
||||
</VSCodeTextField>
|
||||
|
||||
@@ -1687,7 +1679,7 @@ const ApiOptions = ({
|
||||
marginTop: "5px",
|
||||
color: "var(--vscode-descriptionForeground)",
|
||||
}}>
|
||||
Extended thinking is available for models such as Sonnet-4, o3-mini, Deepseek R1, etc. More info on{" "}
|
||||
Extended thinking is available for models as Sonnet-3-7, o3-mini, Deepseek R1, etc. More info on{" "}
|
||||
<VSCodeLink
|
||||
href="https://docs.litellm.ai/docs/reasoning_content"
|
||||
style={{ display: "inline", fontSize: "inherit" }}>
|
||||
@@ -1892,52 +1884,6 @@ const ApiOptions = ({
|
||||
</div>
|
||||
)}
|
||||
|
||||
{selectedProvider === "nebius" && (
|
||||
<div>
|
||||
<VSCodeTextField
|
||||
value={apiConfiguration?.nebiusApiKey || ""}
|
||||
style={{ width: "100%" }}
|
||||
type="password"
|
||||
onInput={handleInputChange("nebiusApiKey")}
|
||||
placeholder="Enter API Key...">
|
||||
<span style={{ fontWeight: 500 }}>Nebius API Key</span>
|
||||
</VSCodeTextField>
|
||||
<p
|
||||
style={{
|
||||
fontSize: "12px",
|
||||
marginTop: 3,
|
||||
color: "var(--vscode-descriptionForeground)",
|
||||
}}>
|
||||
This key is stored locally and only used to make API requests from this extension.{" "}
|
||||
{!apiConfiguration?.nebiusApiKey && (
|
||||
<VSCodeLink
|
||||
href="https://studio.nebius.com/settings/api-keys"
|
||||
style={{
|
||||
display: "inline",
|
||||
fontSize: "inherit",
|
||||
}}>
|
||||
You can get a Nebius API key by signing up here.{" "}
|
||||
</VSCodeLink>
|
||||
)}
|
||||
<span style={{ color: "var(--vscode-errorForeground)" }}>
|
||||
(<span style={{ fontWeight: 500 }}>Note:</span> Cline uses complex prompts and works best with Claude
|
||||
models. Less capable models may not work as expected.)
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{apiErrorMessage && (
|
||||
<p
|
||||
style={{
|
||||
margin: "-10px 0 4px 0",
|
||||
fontSize: 12,
|
||||
color: "var(--vscode-errorForeground)",
|
||||
}}>
|
||||
{apiErrorMessage}
|
||||
</p>
|
||||
)}
|
||||
|
||||
{selectedProvider === "xai" && (
|
||||
<div>
|
||||
<VSCodeTextField
|
||||
@@ -1954,10 +1900,6 @@ const ApiOptions = ({
|
||||
marginTop: 3,
|
||||
color: "var(--vscode-descriptionForeground)",
|
||||
}}>
|
||||
<span style={{ color: "var(--vscode-errorForeground)" }}>
|
||||
(<span style={{ fontWeight: 500 }}>Note:</span> Cline uses complex prompts and works best with Claude
|
||||
models. Less capable models may not work as expected.)
|
||||
</span>
|
||||
This key is stored locally and only used to make API requests from this extension.
|
||||
{!apiConfiguration?.xaiApiKey && (
|
||||
<VSCodeLink href="https://x.ai" style={{ display: "inline", fontSize: "inherit" }}>
|
||||
@@ -2130,28 +2072,12 @@ const ApiOptions = ({
|
||||
{selectedProvider === "asksage" && createDropdown(askSageModels)}
|
||||
{selectedProvider === "xai" && createDropdown(xaiModels)}
|
||||
{selectedProvider === "sambanova" && createDropdown(sambanovaModels)}
|
||||
{selectedProvider === "nebius" && createDropdown(nebiusModels)}
|
||||
</DropdownContainer>
|
||||
|
||||
{selectedProvider === "anthropic" &&
|
||||
(selectedModelId === "claude-3-7-sonnet-20250219" ||
|
||||
selectedModelId === "claude-sonnet-4-20250514" ||
|
||||
selectedModelId === "claude-opus-4-20250514") && (
|
||||
<ThinkingBudgetSlider
|
||||
apiConfiguration={apiConfiguration}
|
||||
setApiConfiguration={setApiConfiguration}
|
||||
/>
|
||||
)}
|
||||
|
||||
{selectedProvider === "vertex" &&
|
||||
(selectedModelId === "claude-3-7-sonnet@20250219" ||
|
||||
selectedModelId === "claude-sonnet-4@20250514" ||
|
||||
selectedModelId === "claude-opus-4@20250514") && (
|
||||
<ThinkingBudgetSlider
|
||||
apiConfiguration={apiConfiguration}
|
||||
setApiConfiguration={setApiConfiguration}
|
||||
/>
|
||||
)}
|
||||
{((selectedProvider === "anthropic" && selectedModelId === "claude-3-7-sonnet-20250219") ||
|
||||
(selectedProvider === "vertex" && selectedModelId === "claude-3-7-sonnet@20250219")) && (
|
||||
<ThinkingBudgetSlider apiConfiguration={apiConfiguration} setApiConfiguration={setApiConfiguration} />
|
||||
)}
|
||||
|
||||
{selectedProvider === "xai" && selectedModelId.includes("3-mini") && (
|
||||
<>
|
||||
@@ -2204,6 +2130,7 @@ const ApiOptions = ({
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
<ModelInfoView
|
||||
selectedModelId={selectedModelId}
|
||||
modelInfo={selectedModelInfo}
|
||||
@@ -2561,8 +2488,6 @@ export function normalizeApiConfiguration(apiConfiguration?: ApiConfiguration):
|
||||
}
|
||||
case "xai":
|
||||
return getProviderData(xaiModels, xaiDefaultModelId)
|
||||
case "nebius":
|
||||
return getProviderData(nebiusModels, nebiusDefaultModelId)
|
||||
case "sambanova":
|
||||
return getProviderData(sambanovaModels, sambanovaDefaultModelId)
|
||||
default:
|
||||
|
||||
@@ -366,7 +366,11 @@ export const BrowserSettingsSection: React.FC = () => {
|
||||
const isSubSettingsOpen = !(browserSettings.disableToolUse || false)
|
||||
|
||||
return (
|
||||
<div id="browser-settings-section" style={{ marginBottom: 20 }}>
|
||||
<div
|
||||
id="browser-settings-section"
|
||||
style={{ marginBottom: 20, borderTop: "1px solid var(--vscode-panel-border)", paddingTop: 15 }}>
|
||||
<h3 style={{ color: "var(--vscode-foreground)", margin: "0 0 10px 0", fontSize: "14px" }}>Browser Settings</h3>
|
||||
|
||||
{/* Master Toggle */}
|
||||
<div style={{ marginBottom: isSubSettingsOpen ? 0 : 10 }}>
|
||||
<VSCodeCheckbox
|
||||
|
||||
@@ -14,7 +14,8 @@ const FeatureSettingsSection = () => {
|
||||
} = useExtensionState()
|
||||
|
||||
return (
|
||||
<div style={{ marginBottom: 20 }}>
|
||||
<div style={{ marginBottom: 20, borderTop: "1px solid var(--vscode-panel-border)", paddingTop: 15 }}>
|
||||
<h3 style={{ color: "var(--vscode-foreground)", margin: "0 0 10px 0", fontSize: "14px" }}>Feature Settings</h3>
|
||||
<div>
|
||||
<VSCodeCheckbox
|
||||
checked={enableCheckpointsSetting}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user