Compare commits

...

507 Commits

Author SHA1 Message Date
Saoud Rizwan b092284f63 Merge branch 'main' into dev-script-create-tasks 2025-03-24 17:07:14 -07:00
Evan 19c4439877 ContextManager Tests (#2415)
* add tests around truncating context

* remove comments
2025-03-24 17:00:43 -07:00
Toshii ec01e1f19d move remaining context management out of Cline.ts (#2367)
* move context management out

* changeset
2025-03-24 16:49:11 -07:00
Peter Dave Hello 4a0a40ead1 Add OpenAI "dynamic" model chatgpt-4o-latest (#2385)
This model represents the latest GPT-4o on chatgpt.com, dynamically
updated to always reflect the most current version, bridging the gap
between GPT-4o and GPT-4.5.

Reference:
- https://platform.openai.com/docs/models/chatgpt-4o-latest
- https://x.com/OpenAIDevs/status/1823510395619000525
- https://x.com/lmarena_ai/status/1823515229491515715
2025-03-24 16:20:45 -07:00
Nir 124569c391 feat: add QwQ-32B model to SambaNova provider (#2389)
Signed-off-by: Nir Ben-Or <nir@swirldslabs.com>
Co-authored-by: Nir Ben-Or <nir@swirldslabs.com>
2025-03-24 16:20:05 -07:00
Andrei Edell 5a08f977c3 #2272 fix 'see more' not showing up (#2390)
* #2272 fix 'see more' not showing up

* changeset & format

---------

Co-authored-by: Andrei Edell <andrei@nugbase.com>
2025-03-24 16:19:36 -07:00
Jille 4ec85af1fb fix: improve file handling for NextJS folder naming conventions and increase limits (#2398)
* fix: improve file handling for NextJS folder naming conventions and increase limits

* Update WorkspaceTracker.ts

* Update list-files.ts

* Update beige-monkeys-prove.md

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2025-03-24 16:16:49 -07:00
Peter Dave Hello 00f88050f7 Fix gpt-4.5-preview's supportsPromptCache value to true (#2399)
Reference:
- https://platform.openai.com/docs/models/gpt-4.5-preview
2025-03-24 16:11:56 -07:00
Evan c4f73e7752 Hoist The Sails - Danger Ahead! (#2405)
* Create Tailwind DangerButton

* changeset

* remove unused import

* Remove old DangerButton component

* rename DangerButtonTW to DangerButton
2025-03-24 16:03:39 -07:00
watany e84b8669a6 feat(bedrock): adding Amazon Nova (#2406)
* amazon nova

* changeset
2025-03-24 16:01:21 -07:00
Evan 5b6eae2905 remove delay package (#2414) 2025-03-24 15:57:29 -07:00
Evan c153077bde Handle Anthropic token input too large (#2407)
* correct calculation of quarter truncation

* changeset

* truncate and retry if we encounter context window exceeded error

* optional chaining for error message check

* changeset

* Handle Anthropic input breaks context window error

* changeset

* remove extra changeset

* remove logger

* use optional chaining
2025-03-24 15:56:16 -07:00
Saoud Rizwan 1796558733 Prepare for release 2025-03-24 14:51:59 -07:00
Saoud Rizwan 2f7f8bff20 Fix openrouter model id being set to object when switching plan/act (#2412) 2025-03-24 14:49:37 -07:00
Saoud Rizwan dc9fb4ddb8 Fix vs code lm plan/act toggle (#2409) 2025-03-24 14:03:31 -07:00
Saoud Rizwan f9c46f898f Fixes 2025-03-22 19:28:36 -07:00
Saoud Rizwan 2d2eb5dd36 Add Announcement 2025-03-22 19:25:46 -07:00
Saoud Rizwan 9562671bcc Remove gallery banner color on marketplace 2025-03-22 19:04:29 -07:00
Saoud Rizwan 6a2aef27ea FIx z-index for dropdowns 2025-03-22 18:58:05 -07:00
Saoud Rizwan c7d6173e20 Fixes 2025-03-22 18:40:12 -07:00
Saoud Rizwan 62da7fe720 Fixes 2025-03-22 18:32:11 -07:00
Saoud Rizwan f97f3e47cc Move sort routing option 2025-03-22 17:43:24 -07:00
Saoud Rizwan f47be1a7bb Add OpenRouter usage feature with original generation endpoint as fallback 2025-03-22 17:21:31 -07:00
Saoud Rizwan 0cd2e69551 Revert "Revert "Fix bug where OpenRouter/Cline providers generation endpoint failed (#2262)""
This reverts commit 2ca4738b59.
2025-03-22 17:15:36 -07:00
Saoud Rizwan 4b19010d56 Revert "Use OpenRouter's new usage flag for more reliable pricing responses"
This reverts commit a29689dfd3.
2025-03-22 17:15:26 -07:00
Saoud Rizwan 49803bf958 Fixes 2025-03-22 17:13:58 -07:00
Saoud Rizwan 33704dcdc1 Copy 2025-03-22 14:53:46 -07:00
Saoud Rizwan 78552287ae Update CHANGELOG 2025-03-22 14:51:56 -07:00
Saoud Rizwan a29689dfd3 Use OpenRouter's new usage flag for more reliable pricing responses 2025-03-22 14:40:23 -07:00
Saoud Rizwan 2ca4738b59 Revert "Fix bug where OpenRouter/Cline providers generation endpoint failed (#2262)"
This reverts commit 8cc7759584.
2025-03-22 14:39:30 -07:00
Saoud Rizwan b4a2d5a06d Add 'Add to Cline' and 'Fix with Cline' menu options 2025-03-22 14:18:50 -07:00
Saoud Rizwan 7f2de3956c Add docs button 2025-03-22 11:47:03 -07:00
Saoud Rizwan 059b14e494 Fix #2167 where openRouterModelId was returning a non-string value 2025-03-22 11:37:55 -07:00
Saoud Rizwan 4d95817eec Move tracer initialization to capture 2025-03-22 00:48:52 -07:00
Saoud Rizwan d8c13db683 Make conversation telemetry only available in dev builds for testing 2025-03-22 00:44:12 -07:00
Saoud Rizwan a35e1ff8df Add delete mcp server button for failed server 2025-03-21 23:48:36 -07:00
Saoud Rizwan a0c5276fb1 Fixes 2025-03-21 23:43:35 -07:00
Saoud Rizwan 696a2c4e48 Prepare for release 2025-03-21 23:34:43 -07:00
Saoud Rizwan e983358dde Clean up new accounts billing view styles 2025-03-21 23:22:57 -07:00
Saoud Rizwan 9ae90c99ad Allow anyone to opt in to conversation telemetry 2025-03-21 21:29:41 -07:00
Saoud Rizwan 4cd914970e Update llm obs description 2025-03-21 20:11:05 -07:00
Evan 4919424faf Handle Openrouter Input Too Large (#2375)
* correct calculation of quarter truncation

* changeset

* truncate and retry if we encounter context window exceeded error

* optional chaining for error message check

* changeset
2025-03-21 20:05:41 -07:00
Frostbourne e65fa89566 [ENG-251] Add Account View (#2324)
* Accounts Modal mocked

* fix dividers

* Fix flex styling

* Fix account card in other views

* Move AccountInfoCard to tailwind and fix flex styling

* Get credits data from endpoint

* refactor creditshistorytable

* prettier

* changeset

* Properly connect and format data from endpoint

* Overflow scroll box

* linter

* Update webview-ui/src/utils/format.ts

Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>

* Make svg asset a component

* fix svg type

* Use clineprovider weakref

* Move shared types to shared folder

* Make account view buttons full width

* Make history table take full height

* Update webview-ui/src/components/account/AccountView.tsx

Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>

---------

Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>
Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2025-03-21 20:03:41 -07:00
Saoud Rizwan 935a68fedc Add provider sorting option to openrouter/cline 2025-03-21 14:37:39 -07:00
Saoud Rizwan 5c78f64978 Revert "[cline.ts] Refactor out ensureTaskDirectoryExists [1/8] (#2356)"
This reverts commit af0cd9bdff.
2025-03-21 13:21:02 -07:00
Evan 328e49bd3c Correct Calculation of Quarter Truncation (#2373)
* correct calculation of quarter truncation

* changeset
2025-03-21 12:59:47 -07:00
pashpashpash 0f7c0205b6 LLM Observability (#2198)
* conversation telemetry

* also tracing responses

* moved to advanced settings

* checkbox instead of dropdown

* system prompt llm observability

* explicit comments

* explicit comments

* better dates

* logs

* dont forget the key

* cleanup step is important

* curly braces after if

* wow that was harder than i thought

* changeset

* throwing behind is dev

* Update packages

* Resolve conflicts

* Revert lock

* Fix conflict

* Fixes

* Add conversation data setting

* Remove conversationDataSetting state

* Fixes

* Fixes

* Fixes

* package lock

* removing uneccessary logging

* Update package-lock.json

* Update package-lock.json

* Clean up tracer

* Update lock

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2025-03-20 18:22:05 -07:00
Evan af0cd9bdff [cline.ts] Refactor out ensureTaskDirectoryExists [1/8] (#2356)
* refactor out ensureTaskDirectoryExists

* changeset

---------

Co-authored-by: Marlon Monroy <7920256+saito-sv@users.noreply.github.com>
2025-03-20 18:07:32 -07:00
Andrei Edell 647a964853 Hugelung/mcp url crash 2170 (#2177)
* #2170 stricter URL handling, URL limits

* better throttling / batching & error displays for embeds

* successfully load 100 popular url embeds

* closer

* fixed heights for embeds to avoid vertical movement

* normalize relative images in og metadata

* Fixed image loading issues

* properly size boxy or wide embed logos

* svg support

* webp support

* re-process image/link previews when plain -> rich

* remove unused functions, add isLocalhost helper

* dont modify response text when upgrading http->https

* consistently use content type header for detecting images

* fix url loading cancellation

* clean up logs, ensure timeout cancel

* testing docs & 50 max urls

* give link to echo mcp server

* force update trick to make rich toggle smoother

* fix linter error

* changeset

* big cleanup: logs, old code, webp handling, sizings

* prettier fix

* ChatErrorBoundary: a guard against evil react crashes (#2327)

* PR Review: extract ChatErrorBoundary

* ChatErrorBoundary in ImagePreview

* Remove a probably unneeded boundary

* Delete the demo errors

* formatting fix

* changeset & final cleanup

---------

Co-authored-by: Andrei Edell <andrei@nugbase.com>
2025-03-20 13:42:30 -10:00
Evan 58305dd6b3 move global file names to constants (#2353) 2025-03-20 12:03:02 -07:00
celestial-vault 677fc3a446 Merge branch 'main' into dev-script-create-tasks 2025-03-20 09:19:22 -07:00
Toshii 88c33ee96d encapsulate ContextManager (#2337)
Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2025-03-20 02:50:40 -07:00
Dennis Bartlett 23a0d41646 Add saito-sv as CodeOwner (#2339)
* Add saito-sv as CodeOwner

* Create tricky-glasses-cheer.md
2025-03-19 22:14:45 -07:00
Dennis Bartlett 974f698e54 Revert "Added option for skipping diff animation. Useful when diff animation …" (#2341)
This reverts commit 12f16a47e3.
2025-03-19 20:12:07 -07:00
deepman 12f16a47e3 Added option for skipping diff animation. Useful when diff animation … (#1765)
* Added option for skipping diff animation. Useful when diff animation is slow.

* Update README.md

Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>

* fixed copyright symbol and default value

* Update README.md

---------

Co-authored-by: Mandeep Kumar <mandeep@deepcognition.ai>
Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>
Co-authored-by: Dennis Bartlett <bartlett.dc.1@gmail.com>
2025-03-19 16:34:51 -07:00
Lobay bc7d6f0c66 fix: improve Azure API detection to support custom endpoints (#2332)
* fix: improve Azure API detection to support custom endpoints

* chore: add changeset for Azure API version detection fix
2025-03-19 11:48:42 -07:00
Saoud Rizwan c8b446ab6c Refactor 2025-03-19 11:24:13 -07:00
Evan 2cc64973fb Size up the Task (#2246)
* Calculate and display total tasks and checkpoints size

* changeset

* Remove logs and add try/catch to file deletion

* call postMessage from HistoryView instead
2025-03-18 11:34:35 -07:00
Evan 599c3f6717 SuccessButton -> TW (#2289)
* migrate SuccessButton to Tailwind

* changeset
2025-03-17 23:54:25 -07:00
Saoud Rizwan 1c491bf719 Prepare for release 2025-03-17 21:04:51 -07:00
canvrno 10ac28b9d6 Issue with 'See More' button in task header (#2302)
* Fixes issue with 'See More' button in task header

* Removed requestAnimationFrame

* Cleanup

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2025-03-17 20:59:14 -07:00
omercelik 95b2e327a8 Added mistral-small-latest (Mistral Small 3.1) (#2300) 2025-03-17 17:38:37 -07:00
Evan 53d11c88bb Upgrade MCP SDK (#2301)
* upgrade mcp sdk

* changeset
2025-03-17 17:38:09 -07:00
canvrno 7ef0f6d1b6 Add --no-verify to checkpoint commits (#2298)
* initial

* Corrected options
2025-03-17 13:53:29 -07:00
Saoud Rizwan 9012d6a1fc Copy 2025-03-14 19:07:24 -07:00
Saoud Rizwan e3d363c56f Prepare for release 2025-03-14 18:56:30 -07:00
canvrno 62a04ad38a Add extension version to telemetry 2025-03-14 18:28:16 -07:00
Saoud Rizwan 1774cf8b79 Add options to question & plan tools (#2264)
* Add question options

* Fixes

* Fix taskheader for anthropic only showing cache info after first request

* Add plan mode options

* Fixes

* Start task header not expanded
2025-03-14 18:25:40 -07:00
minorunara 6133eb4864 fix: Correct VPC endpoint parameter for AnthropicBedrock (#2237)
The VPC endpoint for AnthropicBedrock should be specified using the
'baseURL' parameter instead of the 'endpoint' parameter.
2025-03-14 17:05:56 -07:00
canvrno 087f1d3be3 eng-245 / More checkpoints git telemetry (#2258)
* Checkpoints telemetry improvements

* Checkpoints telemetry improvements
2025-03-14 17:03:43 -07:00
Saoud Rizwan 8cc7759584 Fix bug where OpenRouter/Cline providers generation endpoint failed (#2262)
* Fix openrouter/cline provider cost endpoint bug

* Increase generation endpoint timeout
2025-03-14 17:01:48 -07:00
Evan f9d70ceb6c Stream claude thinking vertex (#2244)
* stream thinking

* changeset
2025-03-14 11:36:59 -07:00
celestial-vault 3c516023c8 make is_dev checks consistent 2025-03-14 11:20:58 -07:00
celestial-vault 4c0c0c947b fix type error 2025-03-14 11:19:01 -07:00
celestial-vault 2ac6af5b08 changeset 2025-03-14 10:19:18 -07:00
celestial-vault 7e68dd8d98 test script to create tasks 2025-03-14 10:12:22 -07:00
watany e8bf106965 fix(bedrock): Support VPC Endpoint options for Deepseek (#2224)
* fix(bedrock): Support VPC Endpoint options for Deepseek

* change set
2025-03-12 23:26:51 -07:00
Saoud Rizwan b528640bd5 Add warning when checkpoints takes too long to load (#2230)
* Add warning for when checkpoints takes too long to load

* Add changeset
2025-03-12 23:25:13 -07:00
Evan 470778aa2f Display Sonnet Thinking - Bedrock (#2225)
* stream thinking blocks from anthropic

* changeset

* remove logger
2025-03-12 22:35:25 -07:00
dependabot[bot] 14f726c5e8 Bump @babel/runtime in /webview-ui in the npm_and_yarn group (#2222)
Bumps the npm_and_yarn group in /webview-ui with 1 update: [@babel/runtime](https://github.com/babel/babel/tree/HEAD/packages/babel-runtime).


Updates `@babel/runtime` from 7.26.0 to 7.26.10
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.26.10/packages/babel-runtime)

---
updated-dependencies:
- dependency-name: "@babel/runtime"
  dependency-type: indirect
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-12 19:42:33 -07:00
Saoud Rizwan 89e1086981 Fix lock 2025-03-12 19:39:46 -07:00
watany c762583d38 feat(bedrock): adding deepseek-r1 (#2185)
* feat(bedrock): adding deepseek-r1

* changeset

* fix(ellipsis-dev):Token usage

* fix(ellipsis): Consider propagating these errors or yielding an error response.

* fix msg

* fix: reuse the convertToR1Format

* fix: token count

* Revert lock

* Install package

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2025-03-12 19:35:27 -07:00
minorunara 73a340c75f feat: Enable VPC endpoint configuration for Amazon Bedrock in AWS Profile mode (#2214)
* feat: Enable VPC endpoint configuration for Amazon Bedrock in AWS Profile mode

This update allows specifying a VPC endpoint when connecting to Amazon Bedrock using AWS Profile mode. It ensures that all API requests stay within a private network and do not traverse the public internet.

* Hide VPC textfield behind toggle

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2025-03-12 19:15:19 -07:00
Evan d3bb6ff456 Configure Computer Use Openai Compatible (#2215)
* Set up branch

* feat: Allow to configure computer use for openapi compatible provider (#1923)

* feat: Allow to set computerUse for OpenAI Compatible provider

* Update webview-ui/src/components/settings/ApiOptions.tsx

Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>

---------

Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>

---------

Co-authored-by: FlavioInacta <flavio.pellanda@inacta.ch>
Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>
2025-03-12 19:00:40 -07:00
Evan 93cd64f031 Add SambaNova Provider (#2216)
* Set up branch

* Feat/add sambanova provider (#2142)

* feat(api): Add SambaNova provider type definitions

- Add 'sambanova' to ApiProvider type
- Add sambanovaApiKey to ApiHandlerOptions interface
- Add SambaNova model definitions

* feat(api): Implement SambaNova provider handler

- Create SambanovaHandler class
- Implement createMessage and getModel methods
- Register the provider in the API factory

* feat(ui): Add SambaNova provider UI components

- Add SambaNova to provider dropdown
- Add UI components for API key and model selection

* feat(storage): Update secret storage for SambaNova API key

- Update SecretKey type to include sambanovaApiKey
- Update getState method to include the SambaNova API key
- Update API configuration handler to store the SambaNova API key

* feat(sambanova): format fix

* feat(sambanova): adding previously untracked files

* fix: will ensure that when no model ID is explicitly selected for the Sambanova provider, it will use the default model ID that's already defined in the codebase (Meta-Llama-3.3-70B-Instruct)

* fix: changing how the dropdown is created by following existing pattern

* fix: changing how the dropdown is created by following existing pattern

* fix: added SamanovaHandler to buildApiHandler

---------

Co-authored-by: saad.khan.hd <saad.khan@hd.digital>
Co-authored-by: Dennis Bartlett <bartlett.dc.1@gmail.com>

* support deepseek and qwen models

* remove unused imports

* Add missing API provider implementation

---------

Co-authored-by: saad-noodleseed <saad@noodleseed.com>
Co-authored-by: saad.khan.hd <saad.khan@hd.digital>
Co-authored-by: Dennis Bartlett <bartlett.dc.1@gmail.com>
Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2025-03-12 18:59:42 -07:00
k-inoway 36836ad4d6 Fix temperature param for o3mini and o1mini when using LiteLLM (#2203)
* fix: prevent invalid temperature 0 param when using o3mini model

* fix: handle temperature param properly for o1-mini and o3-mini models
2025-03-12 11:33:12 -07:00
Taosh 4ade5daeff fix: #2153 make sure isolated qwen model setting on switching mode settings (#2210)
* fix: #2153 make sure isolated qwen model setting on switch mode settings

* Re-add openainative

---------

Co-authored-by: 青炽 <hanyuntao.hyt@alibaba-inc.com>
Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2025-03-12 11:26:49 -07:00
canvrno 24fe5c9c98 Add getDiffCount (#2195)
* changeset

* Track changes with working directory if rhs omitted

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2025-03-12 11:24:46 -07:00
TongZ 18d4a268e2 Fix tooltip layer bug (#2189) 2025-03-11 20:25:21 -07:00
Saoud Rizwan 686f51c248 Fix plan/act model switching for deepseek 2025-03-11 20:23:17 -07:00
Saoud Rizwan 9aa04a376f Add limit for files Cline reads into context (#2199)
* Revert "Merge pull request #1964 from cline/ocasta181/ENG-209"

This reverts commit ca17a06618, reversing
changes made to 6b9c2a1a45.

* Set hard 300kb limit on file size cline reads into context

* Add changeset
2025-03-11 19:46:28 -07:00
akfoster ca17a06618 Merge pull request #1964 from cline/ocasta181/ENG-209
[Fix] Cline crashes when reading files with large outputs
2025-03-11 17:23:07 -07:00
ocasta181 48ea04f2ea add test description 2025-03-11 15:06:24 -07:00
ocasta181 e11ad166e6 pass less data between functions 2025-03-11 15:01:38 -07:00
Daniel Steigman 6b9c2a1a45 Merge pull request #2182 from jiyeonseo/main
Add Korean language
2025-03-11 14:08:51 -07:00
Daniel Steigman bf0bbb4a16 Create selfish-bikes-decide.md 2025-03-11 14:07:27 -07:00
Daniel Steigman a8a24fcb62 Update locales/ko/README.md
Co-authored-by: Dennis Bartlett <bartlett.dc.1@gmail.com>
2025-03-11 14:07:09 -07:00
Daniel Steigman e570662d89 Merge pull request #2193 from cline/bug/readme_copyright
Update copyright for Readme Files
2025-03-11 14:06:28 -07:00
canvrno 78f5faf6d2 Create poor-zoos-jump.md 2025-03-11 13:01:31 -07:00
Dennis Bartlett cc28851ba8 Update copyright for Readme Files 2025-03-11 12:56:54 -07:00
Dennis Bartlett 0afa4c4fd8 Update src/utils/fs.ts
Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>
2025-03-10 20:58:18 -07:00
ocasta181 0f0512363f read config in try/catch 2025-03-10 20:52:53 -07:00
ocasta181 ddc91a46c0 Merge branch 'main' into ocasta181/ENG-209 2025-03-10 20:18:50 -07:00
ocasta181 11a32d9a3a update changelog 2025-03-10 20:08:00 -07:00
ocasta181 45ddaf99e1 fix tests 2025-03-10 20:07:12 -07:00
ocasta181 6f21cb98cc back out non-file related checks 2025-03-10 20:03:33 -07:00
ocasta181 544d4503ac current progress 2025-03-10 19:32:38 -07:00
Cheese a5663e7cf8 Update locales/ko/CONTRIBUTING.md
Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>
2025-03-11 01:47:36 +09:00
Cheese 09b0873e94 Update locales/ko/README.md
Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>
2025-03-11 01:47:25 +09:00
cheese e54623bc7e Add Korean language 2025-03-11 01:39:47 +09:00
ryo-ma 8909ee77fc Support loading by directory in .clinerules/ (#2156)
* Support loading by directory in .clinerules/

* Update src/utils/fs.ts

Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>

* Update docs/prompting/README.md

* Add changeset

* Add tests of isDirectory

* Use relative path in instructions

* Remove log

---------

Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>
Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2025-03-09 20:08:12 -07:00
Evan 3a3943623b Hoist the Sails! (#2169)
* set up tailwind

* changeset
2025-03-09 19:49:09 -07:00
Saoud Rizwan 2446b16828 Fix plan/act model switching for OpenAI native 2025-03-09 19:19:46 -07:00
Saoud Rizwan 30f1d72eb5 Prepare for release 2025-03-09 19:16:41 -07:00
Saoud Rizwan c67edf4b91 Fix type error when opening openrouter model picker with invalid model id 2025-03-09 19:15:56 -07:00
Saoud Rizwan 07cf5cddad Refactor planActSeparateModelSetting initial value implementation 2025-03-09 18:47:42 -07:00
Saoud Rizwan eda2a60a69 Prepare for release 2025-03-09 18:24:24 -07:00
Saoud Rizwan c1b9fcd05c Fix issue where planActSeparateModelsSetting was initialized with non-undefined value 2025-03-09 18:24:06 -07:00
Saoud Rizwan d470ba9150 Prepare for release 2025-03-09 17:58:23 -07:00
Saoud Rizwan ea302ff4f1 Use text display for MCP response temporarily 2025-03-09 17:40:46 -07:00
Saoud Rizwan f11bddc4be Prepare for release 2025-03-09 00:25:06 -08:00
Saoud Rizwan aceb7e8ddd Fix bug where clicking on selected plan/act tab in settings would switch modes 2025-03-09 00:24:13 -08:00
Saoud Rizwan 8ca0568005 Prepare for release 2025-03-08 20:44:19 -08:00
Saoud Rizwan 42b17cfbaa Fix issue where setting default plan/act setting for new users affected users who used anthropic before multiple api options were provided 2025-03-08 20:44:02 -08:00
Saoud Rizwan 359dac9f20 Revert version change 2025-03-08 20:29:03 -08:00
Saoud Rizwan 3948b952cc Disable delete button if no tasks 2025-03-08 20:24:34 -08:00
Saoud Rizwan 867f36dabe Update changelog 2025-03-08 20:19:54 -08:00
Dennis Bartlett 822647c3e9 Fix context window size setting on OpenAI compatible provider (#2162) 2025-03-08 20:17:57 -08:00
Saoud Rizwan fa0c4e5c36 Add delete all task history button (#2163)
* Add delete all task history button

* Add changeset

* Add error message if deleting tasks fails
2025-03-08 20:17:17 -08:00
Saoud Rizwan 59dc92c65c Fix modifying thinking budget not being persisted between mode switches 2025-03-08 18:51:41 -08:00
Saoud Rizwan 6972b2a975 Modify presentation of plan/act settings 2025-03-08 18:17:37 -08:00
Saoud Rizwan 973d18f4f3 Update plan/act tab as soon as settings are saved 2025-03-08 17:56:38 -08:00
Saoud Rizwan 5ab50a41b1 Reduce timeout after tab change 2025-03-08 17:36:25 -08:00
Saoud Rizwan d92292e405 Prepare for release 2025-03-08 17:34:07 -08:00
Saoud Rizwan d0728c58af Fix issue where settings wouldn't all update if user used invalid api config 2025-03-08 17:29:44 -08:00
Saoud Rizwan 30f6a3ca29 Fix welcome screen 2025-03-08 17:05:52 -08:00
Saoud Rizwan 6aad12dc01 Remove log 2025-03-08 16:58:45 -08:00
Saoud Rizwan 9cd0a5ed6a Set plan/act switching to false for new users 2025-03-08 16:58:16 -08:00
Saoud Rizwan 311a4d6ee8 Fix plan/act switching if no model id set or thinking budget changed 2025-03-08 16:21:54 -08:00
Saoud Rizwan 4e2f3d9b0e Update telemetry setting 2025-03-08 16:03:50 -08:00
Saoud Rizwan bdccc40a0f Update privacy policy URL 2025-03-08 16:00:23 -08:00
Saoud Rizwan 5d10a0f89a Add setting to disable plan/act model switching 2025-03-08 15:54:28 -08:00
canvrno a41bb3a70a Checkpoints documentation cleanup (#2160)
* Checkpoints documentation cleanup

* Update src/integrations/checkpoints/CheckpointTracker.ts

Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>
2025-03-08 13:44:36 -08:00
Evan 42e0c74c93 OpenAI Compatible - Temperature Controlled (#2092)
* add temperature to openai compatible model configuration

* prevent type errors

* improved options layout

* changeset

* move temperature to openaimodelinfo

* type error

* use nullish coalescing operator

* Allow user to pass decimal values

* Rename openAiTemperature to temperature

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2025-03-08 13:42:50 -08:00
Saoud Rizwan ef76c8ba5a Remove changeset release workflow 2025-03-08 13:42:05 -08:00
Saoud Rizwan 80542434c5 Disable changeset release workflow 2025-03-08 13:39:54 -08:00
kuu(Fumiya Kume) 69bda35df8 Feat: Add Kotlin Support for language paraer (#2121)
* feat: Add Kotlin support to the tree-sitter parser

* fix: Improve Define file for  kotlin

* Add Changeset

* Add kotlin tree-sitter-wasm

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2025-03-08 13:34:38 -08:00
canvrno e3b87ae56f Removed unused checkpoints telemetry (#2159)
* Removed unused checkpoints telemetry

* Changeset
2025-03-08 11:29:42 -08:00
Saoud Rizwan daf88ba19e Prepare for release 2025-03-08 07:43:14 -08:00
Saoud Rizwan 4759586a17 Revert "Fix Token Count when API incorrectly returns token count per chunk. (#2145)"
This reverts commit ca4fdcf3c4.
2025-03-08 07:41:04 -08:00
Saoud Rizwan 39359307dc Update package lock 2025-03-07 21:13:35 -08:00
Saoud Rizwan 9fa8a7a7a8 Prepare for release 2025-03-07 21:10:49 -08:00
Saoud Rizwan 7eaf1a7cbc Modify diff edit error warning and prompt response 2025-03-07 20:55:13 -08:00
乔浩 66911b89de Add support for qwq32b to OpenRouter. (#2135) 2025-03-07 20:36:38 -08:00
Evan 0de047116f Add Alibaba QwQ for Alibaba Provider - Mainland (#2128)
* add  qwen qwq-plus model

* rename variable

* set up branch

* segment alibaba models between mainland and international

* allow temperature passing to qwq

* changeset

---------

Co-authored-by: yaojunWang <gooqle.com.hk@gmail.com>
2025-03-07 20:25:50 -08:00
dependabot[bot] 60cd10eec6 Bump axios from 1.7.4 to 1.8.2 in the npm_and_yarn group (#2148)
Bumps the npm_and_yarn group with 1 update: [axios](https://github.com/axios/axios).


Updates `axios` from 1.7.4 to 1.8.2
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v1.7.4...v1.8.2)

---
updated-dependencies:
- dependency-name: axios
  dependency-type: direct:production
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-07 20:16:56 -08:00
Saoud Rizwan 209d0830c6 Prepare for release 2025-03-07 19:33:11 -08:00
Saoud Rizwan 8f938b34ab Replace branching strategy with one repo per workspace (#2147) 2025-03-07 19:31:04 -08:00
Dennis Bartlett ca4fdcf3c4 Fix Token Count when API incorrectly returns token count per chunk. (#2145)
* Fix Token Count when API incorrectly returns token count per chunk.

* changeset

* Update src/core/Cline.ts

Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>

* Update src/core/Cline.ts

Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>

---------

Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>
2025-03-07 17:27:09 -08:00
ocasta181 a2572ea233 Merge branch 'main' into ocasta181/ENG-209 2025-03-07 15:19:09 -08:00
canvrno b0b9a662e0 Now we can check git out
* Added checkpoint telemetry

* changeset

* Update src/services/telemetry/TelemetryService.ts

Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>

---------

Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>
2025-03-07 13:28:56 -08:00
Saoud Rizwan 6033d2286c Change account url 2025-03-06 16:42:38 -08:00
Saoud Rizwan 6c8accd585 Prepare for release 2025-03-06 16:39:49 -08:00
Saoud Rizwan 14ed1c088c Fix account view spacing 2025-03-06 16:38:46 -08:00
Saoud Rizwan 55e0586d95 Update package lock 2025-03-06 15:26:48 -08:00
Saoud Rizwan 5e5a6a6c21 Add Retry Request button to 'Insufficient balance' error 2025-03-05 16:37:43 -08:00
Saoud Rizwan 7f5d03c258 Prepare for release 2025-03-05 15:48:46 -08:00
Saoud Rizwan d584a8b0fb Modify copy 2025-03-05 15:40:40 -08:00
Saoud Rizwan 112eeddcd8 Update package lock 2025-03-05 15:37:39 -08:00
Leonid Bugaev 43f9541385 chore: Add test coverage for Terminal code (#2070)
* Add tests for TerminalProcess.ts

* Add missing Sinon mocking

* Test fixes

* More fixes

* Use sinon sandbox

* Add more tests, including real integration ones

* Revert lock changes

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2025-03-05 15:23:16 -08:00
pashpashpash ead395022d csp fix (#2108) 2025-03-05 15:15:00 -08:00
Shohei Ihaya ab47580073 add additional gemini models to vertex (#2080)
* add gemini-2.0-pro-exp-02-05, gemini-2.0-flash-thinking-exp-01-21 to vertex

* Run 'npm run changeset'
2025-03-05 14:58:53 -08:00
Evan ae77bcec4a Remove legacy tool use code from gemini-format.ts (#2105)
* remove legacy tool use code from gemini-format.ts

* remove unused types
2025-03-05 14:57:57 -08:00
Evan 33f94728de Remove legacy anthropic tool use code from mistral-format (#2103)
* remove claude tool use backwards compatability

* remove anthropic tool use from mistal-format

* further simplify mistral-format

* fix linting error in unrelated file
2025-03-05 14:56:53 -08:00
Steven White 49f0c92456 Add additional AskSage models (#2090)
* feat: add claude 3.7 + claude 3.5 gov models for asksage

* chore: add changeset

* chore: numeric separators
2025-03-05 14:53:05 -08:00
Dennis Bartlett 9c5a1fa917 Fix linting issue (#2107) 2025-03-05 14:51:02 -08:00
pashpashpash ffae0b9985 Welcome page + new cline provider (#1519)
* welcome page now shows login button

* adding cline apikey to context

* added cline provider + making sure welcome page closes

* adjusting welcome page language

* swapped cline provider to use openai flavor requests

* handling credit limit errors (super hacky ugh)

* welcome view hiding api options by default

* added back account page and relying more heavily on the firebase custom token to ensure persistent login

* persisting login state through window reloads

* renaming Account -> Cline Account

* use openrouter format with cline api

* Update welcome experience

* Fix error handling

* added generation details tracking to cline provider

* small edit to account view

* logging cline instead of openrouter

* logging cline instead of openrouter

* response.data instead of response.data.data

* set isdev to false for prerelease

* changeset added

* Update tricky-zebras-talk.md

* Update tricky-zebras-talk.md

* welcome page language

* more logging and updated authentication system to be more robust and reliable

* only removing custom token on explicit log out action

* even more aggressive custom token fetching + storing in apiConfig

* moving firebase auth logic to webview because nodejs firebase token refresh is not supported

* cleaned up all old logic for extension server side auth persistence

* reconciling merge conflicts with main

* reconciling merge conflicts

* package-lock.json

* package lock

* package-lock

* packagejson + lock

* use standard API Request Failed title for all API errors while maintaining detailed credit limit information in error content

* deleting packagejson line for vertexai

* reverting package-lock changes

* package-lock.json straight from main

* removing redundant csp allowance

* Fix package-lock

* Add missing options to cline stream

* Remove duplicate csp

* Remove email subscribe

* Update welcome page and cline account view

* Fixes

* Fix errors

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2025-03-05 14:50:28 -08:00
canvrno b4b307e449 fix: use new branching strategy to reduce checkpoints size (#2048)
* rebased to main

* Added files from patch

* restored ClineProvider call to Migration deletes, added try try catch

* Restored changes to Cline.ts

* Updated tests

* Updated tests, prettier

* Optimize commit operation

* Fixed branch deletion, cleanup

* Cleanup

* minor cleanuop

* changeset

* Fix checkpoint tracker initial commit not tracking any files

* Ensure we are getting hash from branch and not main

* Revert first commit logic

* Change to get the correct hash for 'See new changes' on new and resumed tasks

* Make checkpoints commits async

* Update .changeset/cold-beers-laugh.md

Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>
2025-03-05 14:26:48 -08:00
Daniel Steigman 8900df7285 Nighttrek/telemetry task tracking (#2098)
* updated service with specific events

* added telemetry to understand task creation and tool usage and task completion

* updated the telemetry system to track system messages and user messages

* added better message counting telemetry

* removed a create task which was suppose to be a restart

* updated so we can detect task completion properly

* switched to production key

* added changeset

* Modify telemetry signatures

* Fixes

* remove redundant task created

* Update src/services/telemetry/TelemetryService.ts

Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>

* Clean up apiconfiguration

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
Co-authored-by: Dennis Bartlett <bartlett.dc.1@gmail.com>
Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>
2025-03-05 13:46:00 -08:00
Linh Tran faa5e4dd61 fix: list-files will not ignore entire tree if there's error while browsing (#2089) 2025-03-05 13:19:34 -08:00
Evan 3a44bcf784 remove claude tool use backwards compatability (#2102) 2025-03-05 12:42:47 -08:00
loupzeur ccba2ed8f8 feat(ollama): use official ollama library (#1859)
Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2025-03-04 14:46:26 -08:00
Daniel Steigman 0fc2f474a4 Merge pull request #2075 from cline/improve-openrouter-error-message
Openrouter: Typing Error Message
2025-03-03 22:11:44 -08:00
Yuto 0ce66efc19 Fixed problem with "win+shift+a" shortcut not working in Windows (#2049)
* Fixed problem with “win+shift+a” shortcut not working in Windows

* fix package-lock.json

* Revert "fix package-lock.json"

This reverts commit 1ffdfef4f3.
2025-03-03 20:06:42 -08:00
Dennis Bartlett e231cfd3e5 Feature/eng 238 (#2074)
* Update linting to include webview.  Fix outstanding lint issues.

* Fix Format

* Add changeset
2025-03-03 20:01:53 -08:00
celestial-vault 10f8637fb8 changeset 2025-03-03 17:47:10 -08:00
celestial-vault bf985064b9 improve openrouter error types and pass extra error data if present 2025-03-03 17:37:59 -08:00
Saoud Rizwan 922183793f Update lock 2025-03-02 17:23:45 -08:00
Saoud Rizwan 2c02547088 Copy 2025-03-02 17:22:20 -08:00
Saoud Rizwan df65f5f5dd Prepare for release 2025-03-02 17:19:56 -08:00
Saoud Rizwan 542ce36a80 Fix issue where thinking param was being sent with non-claude 3.7 models 2025-03-02 17:12:51 -08:00
Saoud Rizwan 6baac79138 Fix bedrock prompt caching 2025-03-02 17:11:11 -08:00
Saoud Rizwan 6a2172e156 Add gemini models to vertex 2025-03-01 18:03:30 -08:00
Pao 3ca529dc62 feat: Vertex Gemini Flash 2.0 support (#1853)
* feat: Vertex Gemini Flash 2.0 support

* fix: Add changeset for PR

* Moved gemini model

* Resolve conflicts

* Prettier fix

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2025-03-01 17:55:43 -08:00
Saoud Rizwan 7728c0304c Add o3-mini support to openai compatible 2025-03-01 17:35:04 -08:00
Saoud Rizwan f837ecefeb Remove border color 2025-03-01 16:51:28 -08:00
Saoud Rizwan 2d5dcf6a14 Add dcbartlett to codeowners 2025-03-01 16:48:06 -08:00
watany fcf6734949 feat(bedrock): Add APAC Support for Cross-Region Inference Profiles (#1913)
* adding apac

* Remove unreachable 'break' after return in case 'ap-'.

Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>

* changeset

* adding testing

* revert

* revart default model

* revert package-lock

* Remove unnecessary test

---------

Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>
Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2025-03-01 16:39:10 -08:00
Saoud Rizwan d44fd44a81 Copy 2025-03-01 16:04:59 -08:00
Steven White d103f8ee62 feat: add asksage support (#2011)
* feat: add asksage support

* chore: create changeset for asksage support

* Fix Typo

Fix Typo

* chore: fix lint

* Fixes

* Validate asksage API key

---------

Co-authored-by: Dennis Bartlett <bartlett.dc.1@gmail.com>
Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2025-03-01 15:20:58 -08:00
Evan 3d34de4908 Everyone Gets a Timeout (#2018)
* re-open clean PR; formatting changes

* remove unnecessary comments

* Revert system prompt changes

* Remove vscode message after timeout

* Create tidy-kings-ring.md

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2025-03-01 14:46:29 -08:00
Roman Davydov dcd275480f Update recommendation from Claude 3.5 to Claude 3.7 (#2036)
* Update Claude 3.5 -> Claude 3.7 in error message/recommendation

* Add changeset
2025-03-01 14:22:18 -08:00
Dave Lasley aea06ce659 Add retries to bedrock createMessage (#2039)
* Add retries to bedrock createMessage

Add retries to Bedrock's createMessage as indicated by [this](https://github.com/cline/cline/pull/1605#issuecomment-2665904349)

Similar to [this comment](https://github.com/cline/cline/pull/1605#issuecomment-2666290702) I am also experiencing a 429 every once in a while. Measuring a before/after, it's about a 90% improvement when there's no prompt caching. I'm going to add some logging and see if I can track it down further but honestly this is probably worth it as is.

* Add changeset
2025-03-01 14:20:50 -08:00
Leonid Bugaev 65f582bb0d Feat: Add support for AWS Bedrock Anthropic prompt caching (#2034)
* Add Bedrock prompt caching support (optional)

This feature protected under checkbox because it is not yet rolled out
to everyone, and if you will try to send cache headers, and its not
enabled for you, you will get error

* Add changeset

* Update supported models

* Fix copy

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2025-03-01 14:09:10 -08:00
Saoud Rizwan f8796ce69a Fix changelog 2025-03-01 04:32:32 -08:00
Saoud Rizwan 4d29e1f4bd Generate checkpoints asynchronously to prevent blocking UI 2025-03-01 04:19:37 -08:00
Saoud Rizwan 499c7dfb9f Revert to legacy checkpoint tracking 2025-03-01 04:03:32 -08:00
Saoud Rizwan 57b36e16b8 Fix pricing calculation 2025-03-01 03:11:45 -08:00
Saoud Rizwan c891d24ef6 Update budget description 2025-03-01 01:43:35 -08:00
Saoud Rizwan 3f5b6f6e33 Fix changelog 2025-03-01 01:40:42 -08:00
Saoud Rizwan dda8a6c829 Add thinking budget option to Bedrock and Vertex 2025-03-01 01:39:14 -08:00
Saoud Rizwan 0959438db8 Add thinking budget slider to OpenRouter 2025-03-01 01:31:16 -08:00
Saoud Rizwan a838cf9a61 Fix changelog 2025-03-01 00:57:07 -08:00
Saoud Rizwan 4e1dab8d4b Fix changelog 2025-03-01 00:53:05 -08:00
Saoud Rizwan 93b6c937a5 Improve thinking budget slider animation; revert budget when switching plan/act modes 2025-03-01 00:49:34 -08:00
Saoud Rizwan f555b83c7c Revert "Sonnet: Make Separate Thinking Model (#2031)"
This reverts commit 29c01eb7d3.
2025-02-28 23:04:20 -08:00
Saoud Rizwan 21d4ec13e1 Fix CHANGELOG 2025-02-28 22:52:03 -08:00
Saoud Rizwan 38e51bcf73 Update changelog 2025-02-28 22:51:08 -08:00
Saoud Rizwan f40ff25d1e Update package-locks 2025-02-28 22:45:13 -08:00
Evan 29c01eb7d3 Sonnet: Make Separate Thinking Model (#2031)
* make thinking model separate and remove UI slider

* use constant for min budget tokens

* changeset
2025-02-28 22:36:02 -08:00
Saoud Rizwan 682fce458e Prepare for release 2025-02-28 22:31:44 -08:00
Saoud Rizwan 34e87af5f1 Remove webview telemetry 2025-02-28 22:24:34 -08:00
Saoud Rizwan eaa76512fc Remove requesty polling; fix deepseek cost calculation changes; fix preferred language parsing 2025-02-28 22:23:24 -08:00
ocasta181 f608b914b8 remove uneccesary pass through getMaxAllowedSize; fix tests 2025-02-28 22:09:54 -08:00
ocasta181 9f5df9836c remove exclusion from tsconfig 2025-02-28 21:58:08 -08:00
akfoster 0cdbdb057e Update src/integrations/terminal/TerminalProcess.ts
Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>
2025-02-28 21:49:29 -08:00
ocasta181 4846476f6c remove more unused logic 2025-02-28 21:47:12 -08:00
ocasta181 a6f189c5c5 remove calculateUsedContext 2025-02-28 21:26:06 -08:00
ocasta181 ad8e0b19be remove usedContext references 2025-02-28 21:21:53 -08:00
ocasta181 22b2e50210 use real context window size 2025-02-28 18:50:28 -08:00
ocasta181 af0d2f31a7 add changeset 2025-02-28 18:35:04 -08:00
ocasta181 8313fa099c reduce complexity, get working 2025-02-28 18:35:04 -08:00
Ocasta 9f418b10f6 cleanup vscode module declaration 2025-02-28 18:35:04 -08:00
Ocasta 1b56c3fc11 new attempt 2025-02-28 18:34:43 -08:00
Evan df7b458229 Sonnet, Give Me a Reason (#1961)
* wip

* added slider for setting reasoning budget tokens

* refactor out generic slider component; improve styling; add debounce

* added setting validation

* styling and adding reasoning level

* changeset

* make change to trigger test rerun

* revert useless comment
2025-02-28 18:16:56 -08:00
pashpashpash 560cefecca Only requesting requesty models if requesty api key is set (#2028)
* changeset

* changeset
2025-02-28 17:59:21 -08:00
Dennis Bartlett 12dbf1556b Revert Policy changes and TOS addition (#2027) 2025-02-28 17:49:22 -08:00
pashpashpash b4149b5a18 vertex prompt caching (#2026)
* vertex prompt caching

* duplicate declare

* changeset
2025-02-28 17:30:25 -08:00
akfoster cee959eeec Merge pull request #2019 from cline/dev
Merge dev into main
2025-02-28 17:08:06 -08:00
pashpashpash 46121094d7 package-lock.json 2025-02-28 16:56:46 -08:00
Frostbourne 6fe1015310 Update ClineProvider.ts 2025-02-28 14:26:08 -08:00
Dennis Bartlett bb902f9b6b Fix conflict 2025-02-28 14:24:51 -08:00
Dennis Bartlett dfeb57d3af Fix Lint 2025-02-28 14:06:00 -08:00
Dennis Bartlett 9232d752b2 Merge remote-tracking branch 'upstream/main' into dev 2025-02-28 14:05:27 -08:00
Daniel Trugman d1a097cbd4 Requesty dynamic model selection (#1836)
* Extract reuseable ModelDescriptionMarkdown from OpenRouter model picker

* Requesty: Add model picker component

* Refactor readOpenRouterModels to allow any dynamic list filename

* Extract parsePrice to allow reuse by other providers

* Simplify model display name switch case

* Requesty: Add dynamic model list fetching from API

* Requesty: Add default model selection

* Requesty: Specify max_tokens when sending request

* Add changeset

---------

Co-authored-by: Dennis Bartlett <bartlett.dc.1@gmail.com>
2025-02-28 13:29:34 -08:00
Daniel Steigman a02eb40c31 Merge pull request #2014 from cline/andrewmonostate/add-xai-provider
feat: add X AI provider integration
2025-02-28 13:20:39 -08:00
nighttrek c87fb06f33 Merge branch 'dev' into pr/cline/2014 2025-02-28 13:17:35 -08:00
Evan eedae086a3 Fix Merge Conflict resolutions
Fixes Merge conflict resolutions from Main to Dev that were incorrectly chosen.
2025-02-28 13:12:40 -08:00
Dennis Bartlett c1d589f6be Feature/privacy policy (#2015)
* Update URL syntax
2025-02-28 12:15:25 -08:00
Andrew Monostate d7e9ead730 feat: add X AI provider integration 2025-02-28 12:01:00 -08:00
Frostbourne 9df45fb216 Update PRIVACY.md
Fix typo
2025-02-28 12:00:32 -08:00
Daniel Trugman d6184e9dac OpenAI & DeepSeek cost calculation (#1864)
* Add OpenAI compatible cost calculation

* Requesty: Prepare for correct price calculation

* Native OpenAI: Update model caching info

According to [OpenAI's
website](https://platform.openai.com/docs/guides/prompt-caching),
gpt-4o, gpt-4o-mini, o1-preview and o1-mini support caching.

For gpt-4o, even though gpt-4o-2024-05-13 and
chatgpt-4o-latest do no support caching, users will see there are no
cached tokens, which will help avoid confusion.

* Native OpenAI: Call getModel once

* Native OpenAI: Extract yield usage into method

* Native OpenAI: Add caching and cost info to task header

* DeepSeek: Add cost info to task header

* Add changeset
2025-02-28 11:25:17 -08:00
brownrw8 d01b99463e Update Claude Sonnet 3.5 -> 3.7 in README(s) (#1983)
* update Claude Sonnet 35. -> 3.7 in README(s)

* Create calm-laws-camp.md
2025-02-27 20:07:19 -08:00
Saoud Rizwan 3525592c61 Prepare for release 2025-02-27 18:51:15 -08:00
Saoud Rizwan 1913b69ca5 Move GPT-4.5 2025-02-27 18:49:19 -08:00
Saoud Rizwan aa714f28e1 Add GPT-4.5 2025-02-27 18:48:00 -08:00
Saoud Rizwan eef04542d2 Revert package-lock 2025-02-27 18:40:37 -08:00
Jon Atkinson 602d25075b fix: Respect the user's XDG Document folder settings on Linux. (#1884)
This is related to issue #899.

Co-authored-by: Dennis Bartlett <bartlett.dc.1@gmail.com>
2025-02-27 18:23:29 -08:00
Eric Li ea2f4080f8 opt: Enhance OpenAiHandler to diagnose request issues and handle empty streams (#1702) 2025-02-27 17:21:00 -08:00
Dennis Bartlett 10cde3671e Feature/privacy policy (#1994)
* Update Privacy to point to website. Add ToS to point to website.

* Lint Fix
2025-02-27 17:19:26 -08:00
Andrei Edell 43e939666d Hugelung/gpt 4.5 (#1999)
* Add gpt-4.5-preview

* changeset

---------

Co-authored-by: Andrei Edell <andrei@nugbase.com>
2025-02-27 16:29:19 -08:00
Dennis Bartlett ff0eb8fa77 Fix rebase conflict resolution discrepency 2025-02-27 16:18:49 -08:00
Frostbourne db4f6a72c3 style: Make marketplace tags scrollable (#1857)
* Make marketplace tags scrollable

* Changeset
2025-02-27 15:54:51 -08:00
pashpashpash 39980e9be7 Revert "Better Streaming Support (#1980)" (#1993)
This reverts commit 93856ab3b0.
2025-02-27 15:50:02 -08:00
brownrw8 9b5cefd69d Better Streaming Support (#1980)
* feat: enterprise support

* Update src/api/providers/enterprise.ts

Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>

* refactor + anthropic

* fix

* update chunking

* fix enterprise providers

* minor refactor for chunking

* comments

* defaults

* tests

* remove imports

* suggested fixes

* decouple message type

* upgrade libs, prompt caching no longer in beta for claude models

* updates for tests

* enterprise -> streaming provider

* update tests

* remove section from anthropic.ts

* handle specific GCP Vertex invalid_grant error

* finish comment

* add caching stats to chunking

---------

Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>
2025-02-27 15:46:01 -08:00
canvrno 30a4a0e1f1 Check git out - Checkpoints 2.0
**Branch-Per-Task:** Each repo now has a single Shadow Git repo, with separate branches per task (instead of one Shadow Git repo per task).
-   **Legacy Support:** Existing Checkpoints remain functional, while all new Checkpoints use branch-per-task.

-   **Commits:** Legacy tasks commit to legacy Checkpoints; new tasks commit using branch-per-task.
-   **Diffing & Deletions:** Both legacy and branch-per-task Checkpoints support diffing and deletion.

No migration needed—existing tasks stay as-is, and new tasks adopt **branch-per-task** automatically.
2025-02-27 15:40:52 -08:00
Trevor Hudson e5d616b0bf add optional telemetry (#1939)
* add optional telemetry

* move capture

* add changeset

* implement suggestions
2025-02-27 15:40:47 -08:00
brownrw8 5d8172a25f feat: Set preferred language in settings and have it update system prompt (#1538)
* feat: set preferred language in settings and have it update system prompt

* Update webview-ui/src/components/settings/PreferredLanguagePicker.tsx

Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>

* remove unnecessary useEffect

* tweak prompt

* formatting from main

* Update Cline.ts

* Revert "Update Cline.ts"

This reverts commit bee6f0fee0.

* Fixes

* Update Cline.ts

* move preferredLanguage to Advanced Settings

* remove unneccessary import

* remove more imports

* skip language prompt for default language `en`

* Update package.json

* lint

---------

Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>
Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2025-02-27 14:45:46 -08:00
Frostbourne 916660a390 [ENG-126] Migrate to Vite (#1876)
* Initial webview vite migration

* Make vite work

* Fix test running

* Enable HMR, disable vite chunking

* Silence type checking errors

* Vite doesn't use browserslist

* get rid of breaking css flag

* add doc to getHMRHtmlContent

* Make it work

* Changeset

* Add IS_DEV to env definitions

* Update tasks to include HMR

* Update CSP image rules

* prettier

* reintroduce IS_DEV in env

* add new deps to pkg lock

---------

Co-authored-by: Dennis Bartlett <bartlett.dc.1@gmail.com>
2025-02-27 14:45:46 -08:00
akfoster 4506eb39a6 Add .clineignore to .gitignore (#1966)
* clineignor should be ignored by git

* changeset
2025-02-27 14:45:46 -08:00
Andrei Edell 202e3a6d05 Hugelung/rich mcp response (#1941)
* showing images after mcp responses

* images now open in a webview tab

* Open Graph link metadata display for MCP responses

* almost totally working rich mcp response display with images and embeds

* closer

* header for response display

* updated styling of mcp responses

* default to plain text if rich response is loading

* formatting fix

* added changeset output

* remove some old code

* add the dashed border back

* avoid XSS attacks by sanitizing the preview image urls and embeds

* remove incorrect vendor prefix css

* delete old version of open image implementation

* undo some comment removals and cleanups to make PR easier to read

---------

Co-authored-by: Andrei Edell <andrei@nugbase.com>
2025-02-27 14:45:42 -08:00
Doug Daniels a229b8ce9d feat(vertex): Add prompt caching support for Claude on Vertex AI (#1885)
* feat(vertex): Add prompt caching support for Claude on Vertex AI

* Remove countTokens update claude 3.7

* claude-3-7-sonnet@20250219 support in Vertex AI as default model
2025-02-26 22:28:02 -08:00
Daniel Steigman d58c947c41 v0.1 of a cline rules file for the extension (#1847)
* v0.1 of the cline rules file for the extension

* added changeset

* improved the cline rules file

* added new version of the webview state definition

* added more in depth pass from gemini
2025-02-26 20:14:43 -08:00
FlavioInacta 9449f5dcd1 fix: Checking "Support Images" had no effect on first click (#1922) 2025-02-26 19:53:33 -08:00
Minhao-Zhang 7bc02926b4 Fix Official DeepSeek-V3 cost (#1944)
* update the api price for deepseek (discount period is over)

* update the api price for deepseek (discount period is over)
2025-02-26 19:05:19 -08:00
watany 5e65ea04c9 fix: Anthropic's default model is 3.7 (#1971)
* fix: Anthropic's default model is 3.7

* changeset
2025-02-26 18:42:39 -08:00
Saoud Rizwan e14fd78767 Use GUI telemetry toggle to more clearly show user if they're opted in or not 2025-02-25 17:34:36 -08:00
Saoud Rizwan 8c5f8b253b Fix resetting telemetryEnabled if global setting is toggled off 2025-02-25 15:43:40 -08:00
Saoud Rizwan db463dc1ef Add open from history event 2025-02-25 15:27:34 -08:00
Saoud Rizwan a13aba4bd4 Prepare for release 2025-02-25 15:25:08 -08:00
Saoud Rizwan 568932492b Add optional opt-in telemetry to help fix bugs and improve product 2025-02-25 15:23:38 -08:00
Saoud Rizwan 35a64a3666 Modify MCP marketplace installation instructions 2025-02-24 22:57:02 -08:00
Saoud Rizwan 3d19123e0b Merge branch 'main' of github.com:cline/cline 2025-02-24 18:23:49 -08:00
Saoud Rizwan 4f6605ccdb Update version 2025-02-24 18:21:02 -08:00
Saoud Rizwan 408c0887ac Update default model ID 2025-02-24 18:17:35 -08:00
Dennis Bartlett b4e67afcba Reapply "Add IS_DEV and Hot Reloading to debug. (#1895)" (#1917) (#1942)
* Reapply "Add IS_DEV and Hot Reloading to debug. (#1895)" (#1917)

This reverts commit 25ea46aa8d.

* Update TODO to be more explicit. Update logic for checking IS_DEV

* Update TODO with even more explanation.  (Now with 2x more explanation per explanation
2025-02-24 18:16:15 -08:00
Saoud Rizwan 1c9da770a8 Revert "Temporarily revert default openrouter model until API is fixed"
This reverts commit 45b13dd775.
2025-02-24 18:15:33 -08:00
Saoud Rizwan 5f57c1f2f3 Add new openrouter model IDs 2025-02-24 18:14:58 -08:00
Saoud Rizwan f14542d61f Revert "Everyone Gets a Timeout (#1889)"
This reverts commit 87670a37b8.
2025-02-24 18:13:59 -08:00
Saoud Rizwan 131b245023 Revert "Anthropic: Bump it up! (#1938)"
This reverts commit c2809491b4.
2025-02-24 18:09:51 -08:00
Evan c2809491b4 Anthropic: Bump it up! (#1938)
* Updated Anthropic SDK and fixed types

* adjust export markdown handling

* adjust export markdown handling

* added changeset
2025-02-24 17:02:21 -08:00
Evan 87670a37b8 Everyone Gets a Timeout (#1889)
* timeouts for individual servers

* changeset

* remove logger

* use const and descriptive function for time settings
2025-02-24 13:53:29 -08:00
canvrno 0f1240063c MCP Marketplace server installation prompt adjustment (#1901)
* MCP server isntallation prompt adjustment

* changeset
2025-02-24 12:37:17 -08:00
Saoud Rizwan 45b13dd775 Temporarily revert default openrouter model until API is fixed 2025-02-24 11:14:32 -08:00
Saoud Rizwan a2749782ff Prepare for release 2025-02-24 11:08:14 -08:00
Saoud Rizwan 1127e2a33c Add Claude 3.7 Sonnet 2025-02-24 11:07:23 -08:00
Saoud Rizwan 25ea46aa8d Revert "Add IS_DEV and Hot Reloading to debug. (#1895)" (#1917)
This reverts commit b0446f7bac.
2025-02-23 16:10:37 -08:00
Dennis Bartlett b0446f7bac Add IS_DEV and Hot Reloading to debug. (#1895)
* Add IS_DEV and Hot Reloading to debug.

* Changeset Added

* Clean logs
2025-02-21 00:21:00 -08:00
Deepak Mangla f8e4bfdbeb Fix 'Contributing to cline' URL in docs. (#1891) 2025-02-21 00:05:33 -08:00
pashpashpash 9b30efefbe Merge pull request #1874 from Quentin-M/fix/aws_bedrock_credentials
fix: make AWS Bedrock authentication predictable
2025-02-20 16:34:19 -08:00
Quentin Machu 82f1f79b3e fix: make AWS Bedrock authentication predictable
Anthropic's Bedrock SDK creates an AWS credential provider chain [1]
for each request it needs to sign. By doing so right before actually
having to sign the request, it can utilize any session created after
VSCode launched (i.e. outside of the process), and it can renew the
sessions every time necessary transparently for the user.

Cline, on the other hand, by transforming the provided AWS_PROFILE into
a key / secret / session, as part of client initialization, completely
short-circuits this, making it very difficult for users in companies where
sessions are short-lived. Furthermore, Cline would silently ignore the
provided AWS_PROFILE if there isn't a current/non-expired session at the time
of initialization, pass null keys to the Bedrock SDK, which would then
make use the default profile, which may not be configured or authorized
to use AWS Bedrock (as most AWS SSO hub accounts would). From the
perspective of the user, this would manifest itself as an "supported
country" error or unhelpful errors that are basically impossible to
debug without attaching a debugger to Cline.. and such developers may
end up reaching out to their DevOps/IT teams for help, which also could
turn into a waste of time.

This PR addresses the aforementioned issues by resolving the credentials on
every invocation.

1: https://github.com/anthropics/anthropic-sdk-typescript/blob/61b55599d50d9c93840e4736cb756cb3a62b0696/packages/bedrock-sdk/src/auth.ts#L19
2025-02-20 22:41:29 +08:00
Saoud Rizwan 00506c0fcf Update version 2025-02-19 23:41:16 -08:00
Shawn Smith 45e1f58bbc Feat: MCP Marketplace Setting (#1877)
* feat-mc-marketplace-setting

* add changeset

* Fixes

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2025-02-19 23:40:22 -08:00
Saoud Rizwan cd3f3e2150 Update version 2025-02-19 18:58:03 -08:00
Saoud Rizwan ab8376fee4 Add 'Restore Task' option back to checkpoints 2025-02-19 18:57:16 -08:00
Saoud Rizwan ff7e5c8866 Update package 2025-02-19 18:03:11 -08:00
Saoud Rizwan 26be9469c4 Add delete button to MCP server 2025-02-19 18:02:20 -08:00
Saoud Rizwan a925e672ed Update MCP servers icon 2025-02-19 15:30:02 -08:00
Saoud Rizwan 2491b469e7 Remove downloadCount temporarily 2025-02-19 15:05:33 -08:00
Daniel Steigman 3fd1f16acb Merge pull request #1812 from Nick390/main
Add Arabic language
2025-02-19 12:10:17 -08:00
Saoud Rizwan 0a14f0bad5 Update version 2025-02-19 10:05:52 -08:00
Saoud Rizwan d72127550d Prepare for release 2025-02-19 09:20:44 -08:00
Saoud Rizwan 97fa8cffb9 Remove unnecessary recommended flag 2025-02-19 01:57:07 -08:00
Saoud Rizwan d6f51b4525 Fix tests 2025-02-18 22:17:01 -08:00
Saoud Rizwan c846bc4455 Adjust mermaid diagram colors 2025-02-18 22:14:36 -08:00
Saoud Rizwan efaa0d057e Add instruction about handling MCP servers with unexpected build path 2025-02-18 21:40:38 -08:00
Saoud Rizwan ceeb33fdc5 Fix race condition where MCP servers were sent to webview before it was initialized 2025-02-18 21:38:43 -08:00
Saoud Rizwan f938c40539 Fix MCP prompt to handle dist/index.js convention 2025-02-18 20:54:10 -08:00
Saoud Rizwan 65ac7ec3f7 Fix MCP empty list state 2025-02-18 20:04:17 -08:00
Saoud Rizwan f80552f92d Adjust MCP install prompt to demonstrate 2025-02-18 19:12:35 -08:00
Saoud Rizwan ac4ca51ced Center mermaid diagram 2025-02-18 18:54:30 -08:00
Saoud Rizwan 04f8c294c2 Fix mermaid width 2025-02-18 18:50:24 -08:00
Saoud Rizwan a6e109718e Remove logs 2025-02-18 18:30:36 -08:00
Saoud Rizwan f9025426b8 Toggle to act mode and enable MCP prompt if user one-click installs MCP server 2025-02-18 18:24:08 -08:00
Saoud Rizwan 97abdc500b Show pointer on mermaids 2025-02-18 18:03:08 -08:00
Saoud Rizwan 3d64ab298d Update zod 2025-02-18 17:33:48 -08:00
Saoud Rizwan ccdbe3a4a9 Prepare for release 2025-02-18 17:10:59 -08:00
Saoud Rizwan 000b4af458 Add getMcpServerDisplayName 2025-02-18 15:49:57 -08:00
Saoud Rizwan a01471f469 Show MCP server display name instead of ID 2025-02-18 15:37:42 -08:00
Saoud Rizwan d7788c2fd6 Fix mermaid diagram 2025-02-18 14:48:37 -08:00
Evan d6be431cb8 reopen img pr (#1850) 2025-02-18 14:24:14 -08:00
Evan 4c844cbafa Mermaid system prompt (#1849)
* wip

* install dependencies

* rendering mermaid graphs

* fix render failure on streaming

* fix bouncy screen by debouncing mermaid parsing

* add loading state; clean up styling

* replace tag symbols when rendering code

* better mermaid theme for visibility

* added changeset

* remove rehype-mermaid

* update webview package.lock

* clean up

* fix package-lock

* remove regular markdown background styling for mermaid blocks

* updated system prompt

* Update system.ts

* reopen pr

* revert random change

* random change

* reopen pr

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
Co-authored-by: Evan Fannin <celestial_vault@Evans-MacBook-Pro.local>
2025-02-18 14:17:46 -08:00
Saoud Rizwan 9289d73a81 Prepare for release 2025-02-18 14:00:33 -08:00
nickbaumann98 19e5edbc25 Added raw cline-memory-bank custom instructions file to be used for "Add Memory Bank" functionality (#1832)
* Update README.md

* Create neat-apricots-search.md

* added cline-memory-bank markdown

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2025-02-18 12:35:40 -08:00
Kenji Hikmatullah a20a2444b3 Improve Mode Tooltip (#1839)
* feat: Improve Mode Tooltip

* Update webview-ui/src/components/chat/ChatTextArea.tsx

Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>

* fix: typo

---------

Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>
2025-02-18 12:33:21 -08:00
Evan cb02277b45 More Mermaids (#1833)
* wip

* install dependencies

* rendering mermaid graphs

* fix render failure on streaming

* fix bouncy screen by debouncing mermaid parsing

* add loading state; clean up styling

* replace tag symbols when rendering code

* better mermaid theme for visibility

* added changeset

* remove rehype-mermaid

* update webview package.lock

* clean up

* fix package-lock

* remove regular markdown background styling for mermaid blocks
2025-02-18 12:25:03 -08:00
Saoud Rizwan 220603e530 Update changelog 2025-02-18 10:59:43 -08:00
Saoud Rizwan e40e6ddbcd Refactor 2025-02-18 10:49:37 -08:00
Saoud Rizwan ec3bd56903 FIx outline 2025-02-17 22:15:43 -08:00
Saoud Rizwan c126804bb0 Add warning for community made servers; open GitHub on click 2025-02-17 22:01:26 -08:00
pashpashpash ee50e9710a Merge pull request #1841 from cline/pashpashpash/mcp-marketplace
Add MCP Marketplace
2025-02-17 20:43:12 -08:00
Saoud Rizwan 95acb1e95c Fix overflowing mcp server name 2025-02-17 20:34:29 -08:00
pashpashpash e587115d62 added silent fetching to when mcp view is clicked 2025-02-17 20:33:25 -08:00
pashpashpash 9c840fc89c Update webview-ui/src/components/mcp/marketplace/McpMarketplaceView.tsx
Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>
2025-02-17 20:27:11 -08:00
Saoud Rizwan b7519a3669 Create stupid-mayflies-melt.md 2025-02-17 20:13:57 -08:00
Saoud Rizwan 691cf91a4d Fixes 2025-02-17 20:05:16 -08:00
Saoud Rizwan 24d3bfbccb Adjust styling 2025-02-17 19:50:35 -08:00
Saoud Rizwan 8927879d93 Refactor 2025-02-17 19:31:13 -08:00
pashpashpash 5f57415785 removed promise all 2025-02-17 19:08:29 -08:00
pashpashpash c3f7fa3386 cleaned up unused vars 2025-02-17 17:27:31 -08:00
pashpashpash e95cb30ebd merge conflicts resolved 2025-02-17 17:23:49 -08:00
pashpashpash 15b269c8f2 not using vscodelink just a tag 2025-02-17 16:39:40 -08:00
pashpashpash 31443f2a86 removed top border from submit card 2025-02-17 16:36:18 -08:00
pashpashpash 2d05dfd986 silent prefetching 2025-02-17 16:34:02 -08:00
pashpashpash 7722234761 submit card 2025-02-17 16:21:22 -08:00
pashpashpash 09c4202cb2 added submit card 2025-02-17 16:14:36 -08:00
pashpashpash 31eb212bee faster detail view 2025-02-17 15:03:10 -08:00
Daniel Trugman cc166f4144 Requesty provider improvements (#1829)
* Requesty: Add cline headers to requests

* Requesty: Add support for cache read/write tokens

* Requesty: Remove deepseek reasoner conversions that are handled in-flight by Requesty

* Requesty: Read o3-mini reasoning effort from Cline settings

* Requesty: Add total cost per interaction

* Add changeset
2025-02-17 10:37:23 -08:00
Saoud Rizwan 5dea79c65f Fix Mistral provider server URL 2025-02-17 10:35:21 -08:00
Saoud Rizwan 217162ccba Prepare for release 2025-02-16 21:01:52 -08:00
Saoud Rizwan a98269f6d5 Fix sending feedback with approve 2025-02-16 20:38:31 -08:00
Saoud Rizwan aab26e9a38 Fix empty message bubble after switching plan/act with a message 2025-02-16 15:38:06 -08:00
Saoud Rizwan 796eac558c Fix sending message when toggling plan/act mode 2025-02-16 15:15:27 -08:00
Evan 3873908efb Webpack Single Bundle (#1821)
* formatting

* changeset

---------

Co-authored-by: Evan Fannin <celestial_vault@Evans-MacBook-Pro.local>
2025-02-16 14:20:27 -08:00
Saoud Rizwan a6e812824c Give attribution to Szpadel for reasoning update 2025-02-15 23:42:12 -08:00
pashpashpash a4d1ae1af4 codegenicon renamed to codiconIcon 2025-02-15 19:00:08 -08:00
pashpashpash fa2797051d cleanup 2025-02-15 18:20:10 -08:00
pashpashpash 10315663b2 more cleanup 2025-02-15 17:37:02 -08:00
pashpashpash 338116fbd4 consistent spacing with flexbox gap 2025-02-15 17:23:08 -08:00
pashpashpash 89603d3e92 including llmsInstall + removed borders between items 2025-02-15 16:49:06 -08:00
Saoud Rizwan a3dc4471e3 Fixes 2025-02-15 16:45:54 -08:00
Saoud Rizwan 510bb72431 Disable markdown selectors 2025-02-15 15:31:45 -08:00
Saoud Rizwan 141a302521 Fix colors 2025-02-15 15:28:21 -08:00
Saoud Rizwan faae5f22a3 Open readme preview for mcp servers 2025-02-15 15:23:15 -08:00
Saoud Rizwan 0cbd6b321f update ui to look like vs marketplace 2025-02-15 14:33:58 -08:00
AlwaleedAlwabel c134291ba0 Merge branch 'main' of https://github.com/Nick390/cline 2025-02-15 13:55:20 +03:00
AlwaleedAlwabel 9b66eac9fe Create distance-fire-world.md 2025-02-15 13:54:59 +03:00
AlwaleedAlwabel c756470c51 Update locales/ar-sa/CODE_OF_CONDUCT.md
العرق = Race
الدين = Religion

Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>
2025-02-15 13:43:20 +03:00
AlwaleedAlwabel b059b3ac1e Update README.md
**Fix mistakes in the translation.
2025-02-15 13:41:51 +03:00
AlwaleedAlwabel 7ae437fb94 Add Arabic language
**Add CODE_OF_CONDUCT in ar-sa.
**Add CONTRIBUTING in ar-sa.
**Add README in ar-sa.
2025-02-15 13:33:49 +03:00
Saoud Rizwan d67c7e36da Add git context mention (#1806)
* Add git context mention

* Fix context mention highlight

* Create long-guests-occur.md
2025-02-14 19:03:11 -08:00
Saoud Rizwan 584af64334 Add terminal context mention (#1805)
* Add terminal context mention

* Create fuzzy-moose-punch.md
2025-02-14 18:48:47 -08:00
pashpashpash e3b4b2c816 ui polish 2025-02-14 14:25:58 -08:00
pashpashpash 267b60c09e ui polish 2025-02-14 13:48:26 -08:00
stephen8339 ee06c0811c add all qwen2.5 coder models (#1797)
* add alibaba qwen-max qwen-plus qwen-turbo qwen-coder-plus  stable/latest models

* add alibaba qwen-max qwen-plus qwen-turbo qwen-coder-plus  stable/latest models

* Provide the api line choice for international user

* Remove redundant code

* Copy fixes

* Create dry-socks-talk.md

* fix problem what is when you use Qwen api provider and then you want to change the api provider ,the apiline dropdown will obscure your api provider drop-down options

* feat: add qwen2.5-coder models

Description

Add new models as list:
qwen2.5-coder-32b-instruct
qwen2.5-coder-14b-instruct
qwen2.5-coder-7b-instruct
qwen2.5-coder-3b-instruct

Doc: https://help.aliyun.com/zh/model-studio/getting-started/models#9f8890ce29g5u

* add changeset

* feat: add all alibaba qwen2.5 coder models

---------

Co-authored-by: yaojunWang <gooqle.com.hk@gmail.com>
Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
Co-authored-by: 刘耸 <song.liu@yo-star.com>
2025-02-14 11:10:37 -08:00
pashpashpash b8024497f2 mcp marketplace working 2025-02-13 19:11:08 -08:00
pashpashpash e3cfb405fb added some logging + error handling 2025-02-13 18:22:33 -08:00
wen-jy 8e12bdb00d Add support for qwen vl models (#1776)
* feat: add support for qwen vl models

* feat: add support for qwen vl models

* feat: updated the price of the Qianwen model following the BaiLian platform documentation

---------

Co-authored-by: 执无 <jiyong.wjy@alibaba-inc.com>
2025-02-13 18:03:20 -08:00
pashpashpash 7d67000d1f marketplace wip 2025-02-13 17:23:22 -08:00
Hiroki Nakashima 283d7d6928 fix: adjust litellm default context window settings (#1774) 2025-02-12 23:01:01 -08:00
Saoud Rizwan 83a28001be Fix plan/act response prompt 2025-02-12 20:18:36 -08:00
Gustavo de Oliveira 66d5a320b5 docs: Add translation to portuguese pt-BR (#1713)
* docs: Translate CONTRIBUTING to portuguese

* docs: Translate CODE_OF_CONDUCT to portuguese

* docs: Translate README to portuguese

* fix: typo errors

* fix: ellipses requested changes

* fix: Add changeset

* docs: add portuguese label in README.md
2025-02-12 18:18:46 -08:00
pashpashpash 95e1555361 Merge pull request #1748 from dcbartlett/feature/ENG-107_send_input_on_mode_switch
Feature/ENG_107 Send input on mode switch
2025-02-12 18:02:27 -08:00
Shawn Smith a99648884a Fix: Bedrock Profiles (#1751)
* fix-bedrock-profiles

* fix-bedrock-profiles

* resolve linting issue

* fixes

* resolve using var

* add changeset

* update changeset
2025-02-12 17:56:20 -08:00
brownrw8 0434b5c772 Advanced configuration for OpenAI Compatible Providers (#1737)
* feat: advanced configuration for OpenAI Compatible Providers

* Update .changeset/thirty-eyes-appear.md

Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>

* Update webview-ui/src/components/settings/ApiOptions.tsx

Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>

* dropdown menu

* Show pricing if user entered model info

---------

Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>
Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2025-02-12 14:09:55 -08:00
Sanjaykumar S e534c3dbc7 Add api key for litellm api provider #1766 (#1767)
* Add api key for litellm api provider

* Added Changeset

* Update litellm.ts

* Update ApiOptions.tsx

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2025-02-12 10:56:13 -08:00
pashpashpash add2f85053 Merge pull request #1768 from cline/celestial-vault/eng-198-accept-reject-diff-tool-with-user-feedback
Accept tool use with feedback
2025-02-12 10:21:42 -08:00
Evan 69ff71b051 add changeset 2025-02-12 08:56:29 -08:00
Evan f096d73813 approve with feedback for write_to_file/replace_in_file 2025-02-12 08:46:23 -08:00
Evan 96540ddf1e fix linting error 2025-02-11 13:21:22 -08:00
Evan 7da74bd9d8 add accept feedback 2025-02-11 13:20:54 -08:00
Dennis Bartlett 2a894d8cf1 Add Changeset 2025-02-11 04:34:43 -06:00
Dennis Bartlett 8793c2c6c5 fix: ESLint issue in BrowserSession 2025-02-11 04:29:09 -06:00
Dennis Bartlett 9e76cfd44a feat: Add SendMessage capability during Mode Switch 2025-02-11 04:26:35 -06:00
Saoud Rizwan 9c8254cf92 Revert context progress bar removal 2025-02-10 20:29:41 -08:00
dependabot[bot] 57ae7c05d6 Bump esbuild from 0.21.5 to 0.25.0 in the npm_and_yarn group (#1740)
Bumps the npm_and_yarn group with 1 update: [esbuild](https://github.com/evanw/esbuild).


Updates `esbuild` from 0.21.5 to 0.25.0
- [Release notes](https://github.com/evanw/esbuild/releases)
- [Changelog](https://github.com/evanw/esbuild/blob/main/CHANGELOG-2024.md)
- [Commits](https://github.com/evanw/esbuild/compare/v0.21.5...v0.25.0)

---
updated-dependencies:
- dependency-name: esbuild
  dependency-type: direct:development
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-10 20:04:47 -08:00
Waylon e8a2e88fa0 feat: qwen platform adds deepseek-r1/v3 support (#1729)
Co-authored-by: fine <fine_951111@163.com>
2025-02-10 20:04:16 -08:00
brownrw8 7b6a3d25e5 Advanced Setting to enable browser session (#1736)
* feat: add Advanced Setting to enable browser session

* ensure browser tool is removed from system prompt

* fix test

* Update BrowserSession.ts

* Update BrowserSession.ts

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2025-02-10 20:02:00 -08:00
supermomonga 82eba4d668 Add extension setting for chromium executable path (#1721)
* Add extension setting for chromium executable path

* apply changeset

* Update src/services/browser/BrowserSession.ts

Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>

* format code

---------

Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>
2025-02-10 00:13:48 -08:00
Saoud Rizwan 9812dd40c4 Fixes 2025-02-09 22:51:19 -08:00
Saoud Rizwan c29816c310 Create checkpoint at beginning of task 2025-02-09 22:44:35 -08:00
Saoud Rizwan 8938e02d8e Prepare for release 2025-02-09 03:25:13 -08:00
Saoud Rizwan fce7f1bea3 Use more visual checkpoints tracking 2025-02-09 03:03:09 -08:00
Saoud Rizwan fbd249d387 Adjust generation retry params 2025-02-09 02:14:34 -08:00
Saoud Rizwan 43200007cf Retry openrouter generation endpoint to fix context limit errors 2025-02-08 20:15:38 -08:00
Daniel Steigman 4d406fd989 Merge pull request #1715 from cline/clineignore-intro-markdown 2025-02-08 17:40:13 -08:00
Saoud Rizwan 68ab0cff25 Fix copy for clineignored scenarios 2025-02-08 17:15:42 -08:00
Evan 407788ee64 remove old changeset 2025-02-08 14:57:16 -08:00
Evan 41b2ad2b71 merge conflicts 2025-02-08 14:56:11 -08:00
Evan 1cc1c26585 adding changeset 2025-02-08 14:51:39 -08:00
Evan 18e51dff21 added explanatory markdown 2025-02-08 14:47:50 -08:00
Saoud Rizwan 02e45fed53 Re-arrange providers 2025-02-07 21:37:03 -08:00
Saoud Rizwan fa05f94dfa Fixes 2025-02-07 21:34:47 -08:00
Saoud Rizwan c1a7276867 Prepare for release 2025-02-07 21:22:55 -08:00
Saoud Rizwan 887456ead8 Fix bug where new files won't show up in files dropdown (#1704)
* Fix bug where new files won't show up in files dropdown

* Create small-gifts-count.md
2025-02-07 20:52:51 -08:00
brownrw8 134020d51b Add a keyboard shortcut to switch between plan and act mode (#1626)
* feat: add keyboard shortcut to Plan/Act toggle

* remove Shift

* fix: shortcut now Meta+Shift+a

* ENG-123:  Tooltip

* feat: tooltip and platform detection

* fix:impl suggestions

* fix: add changeset

* Fix style

* remove platform detection - use metaKey detection and os utils

* missed comma

* Fixes

* Fixes

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2025-02-07 19:29:54 -08:00
Evan 19c56c6ec4 Attention Is Not What You Need (#1680)
* initialize class

* wip

* wip

* cleaning up

* update comment

* fire n forget error handling

* files added

* changeset

* duplicate path function

* cleanup

* Use special error type for when cline tries to access clineignore'd file

* Rename LLMFileAccessController to ClineIgnoreController

* Use better changeset

* Remove unnecessary file parsing

* Allow access to files outside cwd

* Use more efficient clineIgnoreExists

* Modify clineignore prompt

* Use LOCK_TEXT_SYMBOL

* Block commands attempting to access clineignored files

* Fix prompt responses

* fix tests; make sure .clineignore is ignored

* Fix clineignore prompt

* Fixes

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2025-02-07 19:28:10 -08:00
Evan acd84e53a8 fix tests; make sure .clineignore is ignored 2025-02-07 15:31:09 -08:00
Evan 427e1edcfd Merge branch 'pass-controller-to-services' of https://github.com/cline/cline into pass-controller-to-services 2025-02-07 15:07:55 -08:00
Evan db3b356bb0 Merge branch 'main' into pass-controller-to-services 2025-02-07 14:38:42 -08:00
Evan 4449b51e2c Let's reason together (#1597)
* let's reason together

* typo

* changeset

* Fix type error

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2025-02-07 14:32:14 -08:00
brownrw8 84f017c98e feat:Add Together API Provider (#1698) 2025-02-07 13:55:48 -08:00
Saoud Rizwan b37f688755 Fix prompt responses 2025-02-07 12:11:14 -08:00
Saoud Rizwan 5585c684e9 Block commands attempting to access clineignored files 2025-02-07 11:46:44 -08:00
Saoud Rizwan 075e3171a8 Use LOCK_TEXT_SYMBOL 2025-02-07 10:52:57 -08:00
Saoud Rizwan 53604c9413 Modify clineignore prompt 2025-02-07 10:51:22 -08:00
Saoud Rizwan 8ae39e24be Use more efficient clineIgnoreExists 2025-02-07 10:33:47 -08:00
Saoud Rizwan 88c75a06f2 Allow access to files outside cwd 2025-02-07 00:58:20 -08:00
Saoud Rizwan 4f2d5b15bf Remove unnecessary file parsing 2025-02-07 00:37:03 -08:00
Saoud Rizwan f9e310f338 Use better changeset 2025-02-07 00:26:42 -08:00
Saoud Rizwan 7f292bc995 Rename LLMFileAccessController to ClineIgnoreController 2025-02-07 00:25:29 -08:00
Saoud Rizwan 88a07de5fb Use special error type for when cline tries to access clineignore'd file 2025-02-06 23:56:02 -08:00
Daniel Steigman 076b1e39e6 Nighttrek/bedrock credential manager (#1667)
* added initial AWS bedrock support

* fixed formatting

* updated to fix persistence

* added changeset
2025-02-06 23:36:38 -08:00
aicc 99bba4af27 fix problem what qwen api line drop-down options occlusion api provider drop-down options (#1688)
* add alibaba qwen-max qwen-plus qwen-turbo qwen-coder-plus  stable/latest models

* add alibaba qwen-max qwen-plus qwen-turbo qwen-coder-plus  stable/latest models

* Provide the api line choice for international user

* Remove redundant code

* Copy fixes

* Create dry-socks-talk.md

* fix problem what is when you use Qwen api provider and then you want to change the api provider ,the apiline dropdown will obscure your api provider drop-down options

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2025-02-06 23:04:43 -08:00
brownrw8 3cacd57949 Add dedicated Requesty provider (#1677)
* feat: Add dedicated Requesty provider

* Update ExtensionStateContext.tsx

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2025-02-06 20:45:52 -08:00
Evan fc60f6e55e cleanup 2025-02-06 18:49:11 -08:00
Saoud Rizwan 9bddd9a846 Change default OpenRouter model to non-moderated (#1683)
* Change default OR model to non-moderated

* Create breezy-bobcats-change.md
2025-02-06 18:43:38 -08:00
Evan 6cfdd4fb90 duplicate path function 2025-02-06 18:28:42 -08:00
Evan a31b394282 changeset 2025-02-06 18:09:20 -08:00
Evan 3866ae3449 merge conflicts 2025-02-06 18:05:29 -08:00
Evan 2a0558de80 files added 2025-02-06 14:51:43 -08:00
Matt Rubens 10b8ee4f54 Fix bug when shell profile is not found (#1671) 2025-02-06 12:12:25 -08:00
aicc 548338d39e Add alibaba qwen models plus/max/coder-plus/turbo both stable and latest to use. (#1648)
* add alibaba qwen-max qwen-plus qwen-turbo qwen-coder-plus  stable/latest models

* add alibaba qwen-max qwen-plus qwen-turbo qwen-coder-plus  stable/latest models

* Provide the api line choice for international user

* Remove redundant code

* Copy fixes

* Create dry-socks-talk.md

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2025-02-05 23:23:33 -08:00
akfoster c88b5c67d5 Fix Changeset Test (#1657)
* should only pass if the PR has a new changeset

* improvements to docs-only check

* add changeset
2025-02-05 23:15:52 -08:00
nickbaumann98 1935aab2d5 Update README.md to include Getting Started (#1660)
* Update README.md

* Create neat-apricots-search.md

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2025-02-05 23:14:00 -08:00
Michael bd5eb8fcae feat: add retry decorator with rate limit handling (#1605)
* fix: improve retry decorator with smart rate limit handling

- Add handling of rate limit (429) errors
- Implement retry timing based on response headers
- Add exponential backoff when no headers present
- Add a few unit tests

Fixes #713

* Create modern-knives-tan.md

* Improve readability in retry.ts

---------

Co-authored-by: Michael Overhorst <m.overhorst@spotonmedics.nl>
Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2025-02-05 23:11:01 -08:00
Saoud Rizwan 0795b046e1 Prepare for release 2025-02-05 12:49:58 -08:00
Saoud Rizwan b2e4623559 Add new gemini models (#1656)
* Update gemini models

* Update changeset
2025-02-05 10:49:55 -08:00
omercelik 0b7fac0c9d Add Gemini 2.0 Pro (#1655) 2025-02-05 10:43:55 -08:00
Daniel Steigman 3fd0106816 Merge pull request #1643 from cline/dependabot/npm_and_yarn/webview-ui/npm_and_yarn-d9a8b47fa5
Bump vitest from 2.1.8 to 2.1.9 in /webview-ui in the npm_and_yarn group
2025-02-05 01:40:39 -08:00
Daniel Steigman b4dff4ec14 Merge pull request #1650 from cline/double_underscore_fix
Double underscore fix
2025-02-05 01:30:16 -08:00
Dennis Bartlett 9640a5f496 Merge branch 'main' of https://github.com/cline/cline into double_underscore_fix 2025-02-05 03:25:44 -06:00
Daniel Steigman f108f20466 updated the model list to remove the embedding model (#1646) 2025-02-05 01:11:08 -08:00
Daniel Steigman 905327db4e Merge pull request #1610 from ViezeVingertjes/feat/1609-add-mistral-models
feat: Add support for all available Mistral API models
2025-02-04 23:52:10 -08:00
dependabot[bot] 4d311ffb43 Bump vitest from 2.1.8 to 2.1.9 in /webview-ui in the npm_and_yarn group
Bumps the npm_and_yarn group in /webview-ui with 1 update: [vitest](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest).


Updates `vitest` from 2.1.8 to 2.1.9
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Commits](https://github.com/vitest-dev/vitest/commits/v2.1.9/packages/vitest)

---
updated-dependencies:
- dependency-name: vitest
  dependency-type: direct:development
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-02-05 06:18:51 +00:00
Daniel Steigman 012e073fb9 Merge pull request #1641 from cline/ocasta181/ENG-182
ENG-182 | Add Changesets
2025-02-04 22:17:03 -08:00
Ocasta 6937823b69 use sys.exit, again 2025-02-04 22:05:03 -08:00
Ocasta 1415c8ce7b use sys.exit 2025-02-04 21:56:53 -08:00
Michael Overhorst fd676ef047 Removed /v1 from the url and tested several models. 2025-02-05 06:51:02 +01:00
Ocasta 9fbd8318f0 error handling when notes_start_index is invalid 2025-02-04 21:49:41 -08:00
Ocasta 15c147cf93 fix check changeset git action 2025-02-04 21:45:15 -08:00
Michael Overhorst 2cc0fa906c Updated the mistral url. 2025-02-05 06:41:00 +01:00
Ocasta 42c6dc7e94 update based on automated feedback 2025-02-04 19:20:18 -08:00
Ocasta 5db9ad2585 revert to older config 2025-02-04 19:06:34 -08:00
Ocasta 36d40afb4e changelogs 2025-02-04 18:58:51 -08:00
phadtrapong 01b1c8c0dd Fix installing-dev-essentials.md (#1600)
* Fix installing-dev-essentials.md

Fix installing-dev-essentials.md

* Fix link
2025-02-04 16:56:04 -08:00
Hiroki Nakashima 180fbd5995 feat: add LiteLLM API provider support (#1618) 2025-02-04 14:46:13 -08:00
watany 42924c971f chore: reduse cost openai (#1629) 2025-02-04 09:41:39 -08:00
Michael e41ff78770 feat:show api error status code (#1635)
* Show status code / message when API request error occurs.

* Moved logic to a helper method.

* Different error message format.

* Removed old comment.

---------

Co-authored-by: Michael Overhorst <m.overhorst@spotonmedics.nl>
2025-02-04 09:40:57 -08:00
Ofek Lev 6508549584 Update copyright year (#1637)
* Update copyright year

* Update LICENSE
2025-02-04 09:22:45 -08:00
Evan fa67db75e0 Add clineignore class into cline file (#1623) 2025-02-03 16:53:49 -08:00
Evan 989be56f4b fire n forget error handling 2025-02-03 13:56:04 -08:00
Evan 05093529bb update comment 2025-02-03 12:53:42 -08:00
Daniel Steigman c97fb242a9 Merge pull request #1469 from tszhong0411/fix-typos
fix: typos in docs and code
2025-02-03 11:56:56 -08:00
Evan fb613ef5c8 cleaning up 2025-02-03 08:47:57 -08:00
Michael Overhorst 466b1982f2 feat: Add support for all available Mistral API models
- Add all available Mistral models with specific version numbers
- Include Premier models (Mistral Large, Pixtral, Ministral, etc.)
- Include Free models (Mistral Small, Pixtral 12B, etc.)
- Set correct token limits and pricing for each model

Fixes #1609
2025-02-03 13:34:16 +01:00
Evan f6e64ccb93 wip 2025-02-02 14:19:12 -08:00
Saoud Rizwan 162cd3b9c5 Prepare for release 2025-02-01 18:32:48 -08:00
canvrno 5fd60b7000 Refactor Shell Detection to Use VS Code Terminal Profiles and Fallback Hierarchy (#1543)
* Provide explicit command chaining instructions

* Added shell detection for powershell
The default-shell library being used only returns cmd for windows users. This change will utilize VS Code API calls to determine the user's shell/terminal settings. MacOS & Linux will, for now, continue to use the existing method. Still working on tests.

* Replaced default-shell, added tests
Replaced default-shell with local code that replicates the old behavior on macOS & Linux
Windows shell detection uses VS Code settings to get the user's default terminal profile
Adjusted prompt change

* One small change

* Removed & attributed old package + typo

* Added VSC load for other OSes, refactor, better tests

* Fixed system.ts explicit git lines

* Added back changes for terminal-command-chaining

* One minor, but important change
2025-02-01 16:32:06 -08:00
Daniel Trugman a087f5e583 Fix reasoning_content check for openai & deepseek streams (#1594) 2025-02-01 16:15:57 -08:00
Evan 8e4ab703c8 wip 2025-01-31 16:57:24 -08:00
Saoud Rizwan 5eb8086b42 Add o3-mini support to OpenAI 2025-01-31 15:21:42 -08:00
Evan ef8d49c768 initialize class 2025-01-31 15:15:31 -08:00
Evan Fannin 2a078fee77 Class Implemented (#1577)
* wip

* LLMFileAccessController and tests

* added class and tests

* cleaning up

* formatting

* removing some defaults

* package json and remove defaults list
2025-01-31 12:09:12 -08:00
Saoud Rizwan 3df5e533b7 Prepare for release 2025-01-31 03:14:43 -08:00
Saoud Rizwan 03f07b7626 Fix model switching between plan/act; enable toggle and model switcher during generation 2025-01-31 03:08:24 -08:00
Saoud Rizwan 6500b7c210 Fixes 2025-01-31 02:38:27 -08:00
Saoud Rizwan 50f3791b98 Prepare for release 2025-01-31 02:37:13 -08:00
Saoud Rizwan 68ac266463 Add better support for r1 + show reasoning tokens 2025-01-31 02:35:06 -08:00
brownrw8 57bb43bb6f Basic unit testing for frontend /webview-ui (#1522)
* feat: very basic unit testing

* move dep -> devDeps

* correct other deps

* resync after i18n revert on main

* update package-lock.json
2025-01-30 21:48:19 -08:00
Saoud Rizwan 3b5d39a21b Prepare for release 2025-01-30 18:57:18 -08:00
Saoud Rizwan 4a8f3339fb Fixes 2025-01-30 18:55:49 -08:00
Saoud Rizwan 64718667ff Show context progress bar always 2025-01-30 18:55:03 -08:00
Saoud Rizwan 4f93437797 Fix creating github release 2025-01-30 18:49:39 -08:00
tszhong0411 6bd6c830fe fix: typos in docs and code 2025-01-26 21:06:21 +08:00
Hanzen Shou 1ce57f2203 doc: linked issue to markdown plugin 2024-12-28 21:04:25 -08:00
Hanzen Shou c798df215e fix: double underscored filenames now render correctly 2024-12-28 21:03:19 -08:00
230 changed files with 31043 additions and 18507 deletions
+7
View File
@@ -0,0 +1,7 @@
---
"claude-dev": minor
---
Add Bedrock prompt caching support (optional).
This feature protected under checkbox because it is not yet rolled out to everyone, and if you will try to send cache headers, and its not enabled for you, you will get error.
+5
View File
@@ -0,0 +1,5 @@
---
"claude-dev": patch
---
feat(bedrock): adding Amazon Nova
+5
View File
@@ -0,0 +1,5 @@
---
"claude-dev": patch
---
Improve file handling for NextJS folder naming conventions and increase file listing limits. Fix glob pattern interpretation issues with parentheses in folder names
+5
View File
@@ -0,0 +1,5 @@
---
"claude-dev": patch
---
Handle input too large Anthropic
+5
View File
@@ -0,0 +1,5 @@
---
"claude-dev": patch
---
Fix "See more" not showing up for tasks after task un-fold
+5
View File
@@ -0,0 +1,5 @@
---
"claude-dev": patch
---
Fix gpt-4.5-preview's supportsPromptCache value to true
+5
View File
@@ -0,0 +1,5 @@
---
"claude-dev": minor
---
Added a script to create test tasks in dev mode
+5
View File
@@ -0,0 +1,5 @@
---
"claude-dev": patch
---
updated move context management out of cline
+5
View File
@@ -0,0 +1,5 @@
---
"claude-dev": minor
---
Added support for SambaNova QwQ-32B model
+5
View File
@@ -0,0 +1,5 @@
---
"claude-dev": minor
---
Add OpenAI "dynamic" model chatgpt-4o-latest
+5
View File
@@ -0,0 +1,5 @@
---
"claude-dev": patch
---
DangerButton.tsx to Tailwind
+517
View File
@@ -0,0 +1,517 @@
# Cline Extension Architecture & Development Guide
## Project Overview
Cline is a VSCode extension that provides AI assistance through a combination of a core extension backend and a React-based webview frontend. The extension is built with TypeScript and follows a modular architecture pattern.
## Architecture Overview
```mermaid
graph TB
subgraph VSCode Extension Host
subgraph Core Extension
ExtensionEntry[Extension Entry<br/>src/extension.ts]
ClineProvider[ClineProvider<br/>src/core/webview/ClineProvider.ts]
ClineClass[Cline Class<br/>src/core/Cline.ts]
GlobalState[VSCode Global State]
SecretsStorage[VSCode Secrets Storage]
end
subgraph Webview UI
WebviewApp[React App<br/>webview-ui/src/App.tsx]
ExtStateContext[ExtensionStateContext<br/>webview-ui/src/context/ExtensionStateContext.tsx]
ReactComponents[React Components]
end
subgraph Storage
TaskStorage[Task Storage<br/>Per-Task Files & History]
CheckpointSystem[Git-based Checkpoints]
end
end
%% Core Extension Data Flow
ExtensionEntry --> ClineProvider
ClineProvider --> ClineClass
ClineClass --> GlobalState
ClineClass --> SecretsStorage
ClineClass --> TaskStorage
ClineClass --> CheckpointSystem
%% Webview Data Flow
WebviewApp --> ExtStateContext
ExtStateContext --> ReactComponents
%% Bidirectional Communication
ClineProvider <-->|postMessage| ExtStateContext
style GlobalState fill:#f9f,stroke:#333,stroke-width:2px
style SecretsStorage fill:#f9f,stroke:#333,stroke-width:2px
style ExtStateContext fill:#bbf,stroke:#333,stroke-width:2px
style ClineProvider fill:#bfb,stroke:#333,stroke-width:2px
```
## Definitions
- core extension: Anything inside the src folder starting with the Cline.ts file
- core extension state: Managed by the ClineProvider class in src/core/webview/ClineProvider.ts, which serves as the single source of truth for the extension's state. It manages multiple types of persistent storage (global state, workspace state, and secrets), handles state distribution to both the core extension and webview components, and coordinates state across multiple extension instances. This includes managing API configurations, task history, settings, and MCP configurations.
- webview: Anything inside the webview-ui. All the react or view's seen by the user and user interaction compone
- webview state: Managed by ExtensionStateContext in webview-ui/src/context/ExtensionStateContext.tsx, which provides React components with access to the extension's state through a context provider pattern. It maintains local state for UI components, handles real-time updates through message events, manages partial message updates, and provides methods for state modifications. The context includes extension version, messages, task history, theme, API configurations, MCP servers, marketplace catalog, and workspace file paths. It synchronizes with the core extension through VSCode's message passing system and provides type-safe access to state through a custom hook (useExtensionState).
### Core Extension State
The `ClineProvider` class manages multiple types of persistent storage:
- **Global State:** Stored across all VSCode instances. Used for settings and data that should persist globally.
- **Workspace State:** Specific to the current workspace. Used for task-specific data and settings.
- **Secrets:** Secure storage for sensitive information like API keys.
The `ClineProvider` handles the distribution of state to both the core extension and webview components. It also coordinates state across multiple extension instances, ensuring consistency.
### Webview State
The `ExtensionStateContext` in `webview-ui/src/context/ExtensionStateContext.tsx` provides React components with access to the extension's state. It uses a context provider pattern and maintains local state for UI components. The context includes:
- Extension version
- Messages
- Task history
- Theme
- API configurations
- MCP servers
- Marketplace catalog
- Workspace file paths
It synchronizes with the core extension through VSCode's message passing system and provides type-safe access to the state via a custom hook (`useExtensionState`).
## Core Extension (Cline.ts)
The Cline class is the heart of the extension, managing task execution, state persistence, and tool coordination. Each task runs in its own instance of the Cline class, ensuring isolation and proper state management.
### Task Execution Loop
The core task execution loop follows this pattern:
```typescript
class Cline {
async initiateTaskLoop(userContent: UserContent, isNewTask: boolean) {
while (!this.abort) {
// 1. Make API request and stream response
const stream = this.attemptApiRequest()
// 2. Parse and present content blocks
for await (const chunk of stream) {
switch (chunk.type) {
case "text":
// Parse into content blocks
this.assistantMessageContent = parseAssistantMessage(chunk.text)
// Present blocks to user
await this.presentAssistantMessage()
break
}
}
// 3. Wait for tool execution to complete
await pWaitFor(() => this.userMessageContentReady)
// 4. Continue loop with tool result
const recDidEndLoop = await this.recursivelyMakeClineRequests(
this.userMessageContent
)
}
}
}
```
### Message Streaming System
The streaming system handles real-time updates and partial content:
```typescript
class Cline {
async presentAssistantMessage() {
// Handle streaming locks to prevent race conditions
if (this.presentAssistantMessageLocked) {
this.presentAssistantMessageHasPendingUpdates = true
return
}
this.presentAssistantMessageLocked = true
// Present current content block
const block = this.assistantMessageContent[this.currentStreamingContentIndex]
// Handle different types of content
switch (block.type) {
case "text":
await this.say("text", content, undefined, block.partial)
break
case "tool_use":
// Handle tool execution
break
}
// Move to next block if complete
if (!block.partial) {
this.currentStreamingContentIndex++
}
}
}
```
### Tool Execution Flow
Tools follow a strict execution pattern:
```typescript
class Cline {
async executeToolWithApproval(block: ToolBlock) {
// 1. Check auto-approval settings
if (this.shouldAutoApproveTool(block.name)) {
await this.say("tool", message)
this.consecutiveAutoApprovedRequestsCount++
} else {
// 2. Request user approval
const didApprove = await askApproval("tool", message)
if (!didApprove) {
this.didRejectTool = true
return
}
}
// 3. Execute tool
const result = await this.executeTool(block)
// 4. Save checkpoint
await this.saveCheckpoint()
// 5. Return result to API
return result
}
}
```
### Error Handling & Recovery
The system includes robust error handling:
```typescript
class Cline {
async handleError(action: string, error: Error) {
// 1. Check if task was abandoned
if (this.abandoned) return
// 2. Format error message
const errorString = `Error ${action}: ${error.message}`
// 3. Present error to user
await this.say("error", errorString)
// 4. Add error to tool results
pushToolResult(formatResponse.toolError(errorString))
// 5. Cleanup resources
await this.diffViewProvider.revertChanges()
await this.browserSession.closeBrowser()
}
}
```
### API Request & Token Management
The Cline class handles API requests with built-in retry, streaming, and token management:
```typescript
class Cline {
async *attemptApiRequest(previousApiReqIndex: number): ApiStream {
// 1. Wait for MCP servers to connect
await pWaitFor(() => this.providerRef.deref()?.mcpHub?.isConnecting !== true)
// 2. Manage context window
const previousRequest = this.clineMessages[previousApiReqIndex]
if (previousRequest?.text) {
const { tokensIn, tokensOut } = JSON.parse(previousRequest.text)
const totalTokens = (tokensIn || 0) + (tokensOut || 0)
// Truncate conversation if approaching context limit
if (totalTokens >= maxAllowedSize) {
this.conversationHistoryDeletedRange = getNextTruncationRange(
this.apiConversationHistory,
this.conversationHistoryDeletedRange,
totalTokens / 2 > maxAllowedSize ? "quarter" : "half"
)
}
}
// 3. Handle streaming with automatic retry
try {
this.isWaitingForFirstChunk = true
const firstChunk = await iterator.next()
yield firstChunk.value
this.isWaitingForFirstChunk = false
// Stream remaining chunks
yield* iterator
} catch (error) {
// 4. Error handling with retry
if (isOpenRouter && !this.didAutomaticallyRetryFailedApiRequest) {
await setTimeoutPromise(1000)
this.didAutomaticallyRetryFailedApiRequest = true
yield* this.attemptApiRequest(previousApiReqIndex)
return
}
// 5. Ask user to retry if automatic retry failed
const { response } = await this.ask(
"api_req_failed",
this.formatErrorWithStatusCode(error)
)
if (response === "yesButtonClicked") {
await this.say("api_req_retried")
yield* this.attemptApiRequest(previousApiReqIndex)
return
}
}
}
}
```
Key features:
1. **Context Window Management**
- Tracks token usage across requests
- Automatically truncates conversation when needed
- Preserves important context while freeing space
- Handles different model context sizes
2. **Streaming Architecture**
- Real-time chunk processing
- Partial content handling
- Race condition prevention
- Error recovery during streaming
3. **Error Handling**
- Automatic retry for transient failures
- User-prompted retry for persistent issues
- Detailed error reporting
- State cleanup on failure
4. **Token Tracking**
- Per-request token counting
- Cumulative usage tracking
- Cost calculation
- Cache hit monitoring
### Task State & Resumption
The Cline class provides robust task state management and resumption capabilities:
```typescript
class Cline {
async resumeTaskFromHistory() {
// 1. Load saved state
this.clineMessages = await this.getSavedClineMessages()
this.apiConversationHistory = await this.getSavedApiConversationHistory()
// 2. Handle interrupted tool executions
const lastMessage = this.apiConversationHistory[this.apiConversationHistory.length - 1]
if (lastMessage.role === "assistant") {
const toolUseBlocks = content.filter(block => block.type === "tool_use")
if (toolUseBlocks.length > 0) {
// Add interrupted tool responses
const toolResponses = toolUseBlocks.map(block => ({
type: "tool_result",
tool_use_id: block.id,
content: "Task was interrupted before this tool call could be completed."
}))
modifiedOldUserContent = [...toolResponses]
}
}
// 3. Notify about interruption
const agoText = this.getTimeAgoText(lastMessage?.ts)
newUserContent.push({
type: "text",
text: `[TASK RESUMPTION] This task was interrupted ${agoText}. It may or may not be complete, so please reassess the task context.`
})
// 4. Resume task execution
await this.initiateTaskLoop(newUserContent, false)
}
private async saveTaskState() {
// Save conversation history
await this.saveApiConversationHistory()
await this.saveClineMessages()
// Create checkpoint
const commitHash = await this.checkpointTracker?.commit()
// Update task history
await this.providerRef.deref()?.updateTaskHistory({
id: this.taskId,
ts: lastMessage.ts,
task: taskMessage.text,
// ... other metadata
})
}
}
```
Key aspects of task state management:
1. **Task Persistence**
- Each task has a unique ID and dedicated storage directory
- Conversation history is saved after each message
- File changes are tracked through Git-based checkpoints
- Terminal output and browser state are preserved
2. **State Recovery**
- Tasks can be resumed from any point
- Interrupted tool executions are handled gracefully
- File changes can be restored from checkpoints
- Context is preserved across VSCode sessions
3. **Workspace Synchronization**
- File changes are tracked through Git
- Checkpoints are created after tool executions
- State can be restored to any checkpoint
- Changes can be compared between checkpoints
4. **Error Recovery**
- Failed API requests can be retried
- Interrupted tool executions are marked
- Resources are cleaned up properly
- User is notified of state changes
## Data Flow & State Management
### Core Extension Role
The core extension (ClineProvider) acts as the single source of truth for all persistent state. It:
- Manages VSCode global state and secrets storage
- Coordinates state updates between components
- Ensures state consistency across webview reloads
- Handles task-specific state persistence
- Manages checkpoint creation and restoration
### Terminal Management
The Cline class manages terminal instances and command execution:
```typescript
class Cline {
async executeCommandTool(command: string): Promise<[boolean, ToolResponse]> {
// 1. Get or create terminal
const terminalInfo = await this.terminalManager.getOrCreateTerminal(cwd)
terminalInfo.terminal.show()
// 2. Execute command with output streaming
const process = this.terminalManager.runCommand(terminalInfo, command)
// 3. Handle real-time output
let result = ""
process.on("line", (line) => {
result += line + "\n"
if (!didContinue) {
sendCommandOutput(line)
} else {
this.say("command_output", line)
}
})
// 4. Wait for completion or user feedback
let completed = false
process.once("completed", () => {
completed = true
})
await process
// 5. Return result
if (completed) {
return [false, `Command executed.\n${result}`]
} else {
return [
false,
`Command is still running in the user's terminal.\n${result}\n\nYou will be updated on the terminal status and new output in the future.`
]
}
}
}
```
Key features:
1. **Terminal Instance Management**
- Multiple terminal support
- Terminal state tracking (busy/inactive)
- Process cooldown monitoring
- Output history per terminal
2. **Command Execution**
- Real-time output streaming
- User feedback handling
- Process state monitoring
- Error recovery
### Browser Session Management
The Cline class handles browser automation through Puppeteer:
```typescript
class Cline {
async executeBrowserAction(action: BrowserAction): Promise<BrowserActionResult> {
switch (action) {
case "launch":
// 1. Launch browser with fixed resolution
await this.browserSession.launchBrowser()
return await this.browserSession.navigateToUrl(url)
case "click":
// 2. Handle click actions with coordinates
return await this.browserSession.click(coordinate)
case "type":
// 3. Handle keyboard input
return await this.browserSession.type(text)
case "close":
// 4. Clean up resources
return await this.browserSession.closeBrowser()
}
}
}
```
Key aspects:
1. **Browser Control**
- Fixed 900x600 resolution window
- Single instance per task lifecycle
- Automatic cleanup on task completion
- Console log capture
2. **Interaction Handling**
- Coordinate-based clicking
- Keyboard input simulation
- Screenshot capture
- Error recovery
## Conclusion
This guide provides a comprehensive overview of the Cline extension architecture, with special focus on state management, data persistence, and code organization. Following these patterns ensures robust feature implementation with proper state handling across the extension's components.
Remember:
- Always persist important state in the extension
- The core extension exists in the src/ folder
- Use proper typing for all state and messages
- Handle errors and edge cases
- Test state persistence across webview reloads
- Follow the established patterns for consistency
- Place new code in appropriate directories
- Maintain clear separation of concerns
- Install dependencies in correct package.json
## Contributing
Contributions to the Cline extension are welcome! Please follow these guidelines:
When adding new tools or API providers, follow the existing patterns in the `src/integrations/` and `src/api/providers/` directories, respectively. Ensure that your code is well-documented and includes appropriate error handling.
The `.clineignore` file allows users to specify files and directories that Cline should not access. When implementing new features, respect the `.clineignore` rules and ensure that your code does not attempt to read or modify ignored files.
+1 -1
View File
@@ -1 +1 @@
* @saoudrizwan @ocasta181 @NightTrek @pashpashpash
* @saoudrizwan @ocasta181 @NightTrek @pashpashpash @dcbartlett @saito-sv
-3
View File
@@ -28,10 +28,7 @@ updates:
patterns:
- "*"
ignore:
# Ignore CRA and related packages that often have false positives
- dependency-name: "react-scripts"
- dependency-name: "@testing-library/*"
- dependency-name: "web-vitals"
- dependency-name: "*"
update-types:
- "version-update:semver-major"
+1
View File
@@ -21,6 +21,7 @@
- [ ] Changes are limited to a single feature, bugfix or chore (split larger changes into separate PRs)
- [ ] Tests are passing (`npm test`) and code is formatted and linted (`npm run format && npm run lint`)
- [ ] I have created a changeset using `npm run changeset` (required for user-facing changes)
- [ ] I have reviewed [contributor guidelines](https://github.com/cline/cline/blob/main/CONTRIBUTING.md)
### Screenshots
@@ -0,0 +1,103 @@
"""
This script updates a specific version's release notes section in CHANGELOG.md with new content
or reformats existing content.
The script:
1. Takes a version number, changelog path, and optionally new content as input from environment variables
2. Finds the section in the changelog for the specified version
3. Either:
a) Replaces the content with new content if provided, or
b) Reformats existing content by:
- Removing the first two lines of the changeset format
- Ensuring version numbers are wrapped in square brackets
4. Writes the updated changelog back to the file
Environment Variables:
CHANGELOG_PATH: Path to the changelog file (defaults to 'CHANGELOG.md')
VERSION: The version number to update/format
PREV_VERSION: The previous version number (used to locate section boundaries)
NEW_CONTENT: Optional new content to insert for this version
"""
#!/usr/bin/env python3
import os
import sys
CHANGELOG_PATH = os.environ.get("CHANGELOG_PATH", "CHANGELOG.md")
VERSION = os.environ['VERSION']
PREV_VERSION = os.environ.get("PREV_VERSION", "")
NEW_CONTENT = os.environ.get("NEW_CONTENT", "")
def overwrite_changelog_section(changelog_text: str, new_content: str):
# Find the section for the specified version
version_pattern = f"## {VERSION}\n"
bracketed_version_pattern = f"## [{VERSION}]\n"
prev_version_pattern = f"## [{PREV_VERSION}]\n"
print(f"latest version: {VERSION}")
print(f"prev_version: {PREV_VERSION}")
# Try both unbracketed and bracketed version patterns
version_index = changelog_text.find(version_pattern)
if version_index == -1:
version_index = changelog_text.find(bracketed_version_pattern)
if version_index == -1:
# If version not found, add it at the top (after the first line)
first_newline = changelog_text.find('\n')
if first_newline == -1:
# If no newline found, just prepend
return f"## [{VERSION}]\n\n{changelog_text}"
return f"{changelog_text[:first_newline + 1]}## [{VERSION}]\n\n{changelog_text[first_newline + 1:]}"
else:
# Using bracketed version
version_pattern = bracketed_version_pattern
notes_start_index = version_index + len(version_pattern)
notes_end_index = changelog_text.find(prev_version_pattern, notes_start_index) if PREV_VERSION and prev_version_pattern in changelog_text else len(changelog_text)
if new_content:
return changelog_text[:notes_start_index] + f"{new_content}\n" + changelog_text[notes_end_index:]
else:
changeset_lines = changelog_text[notes_start_index:notes_end_index].split("\n")
# Ensure we have at least 2 lines before removing them
if len(changeset_lines) < 2:
print("Warning: Changeset content has fewer than 2 lines")
parsed_lines = "\n".join(changeset_lines)
else:
# Remove the first two lines from the regular changeset format, ex: \n### Patch Changes
parsed_lines = "\n".join(changeset_lines[2:])
updated_changelog = changelog_text[:notes_start_index] + parsed_lines + changelog_text[notes_end_index:]
# Ensure version number is bracketed
updated_changelog = updated_changelog.replace(f"## {VERSION}", f"## [{VERSION}]")
return updated_changelog
try:
print(f"Reading changelog from: {CHANGELOG_PATH}")
with open(CHANGELOG_PATH, 'r') as f:
changelog_content = f.read()
print(f"Changelog content length: {len(changelog_content)} characters")
print("First 200 characters of changelog:")
print(changelog_content[:200])
print("----------------------------------------------------------------------------------")
new_changelog = overwrite_changelog_section(changelog_content, NEW_CONTENT)
print("New changelog content:")
print("----------------------------------------------------------------------------------")
print(new_changelog)
print("----------------------------------------------------------------------------------")
print(f"Writing updated changelog back to: {CHANGELOG_PATH}")
with open(CHANGELOG_PATH, 'w') as f:
f.write(new_changelog)
print(f"{CHANGELOG_PATH} updated successfully!")
except FileNotFoundError:
print(f"Error: Changelog file not found at {CHANGELOG_PATH}")
sys.exit(1)
except Exception as e:
print(f"Error updating changelog: {str(e)}")
print(f"Current working directory: {os.getcwd()}")
sys.exit(1)
+117
View File
@@ -0,0 +1,117 @@
name: Check Changeset
run-name: Check for Changeset in PR
permissions:
contents: read
pull-requests: write
on:
pull_request:
branches:
- main
types: [opened, synchronize, reopened, ready_for_review]
jobs:
check-changeset:
# Skip draft PRs and dependabot PRs
if: github.event.pull_request.draft == false && github.actor != 'dependabot[bot]'
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
with:
fetch-depth: 0
ref: ${{ github.event.pull_request.head.sha }}
- name: Check for changeset
id: check-changeset
run: |
# Debug info
echo "Current directory: $(pwd)"
echo "PR Base Ref: ${{ github.event.pull_request.base.ref }}"
echo "PR Head Ref: ${{ github.event.pull_request.head.ref }}"
echo "PR Head SHA: ${{ github.event.pull_request.head.sha }}"
echo "Git status:"
git status
# Get list of changed files
git fetch origin ${{ github.event.pull_request.base.ref }}
CHANGED_FILES=$(git diff --name-only origin/${{ github.event.pull_request.base.ref }} HEAD)
echo "Changed files:"
echo "$CHANGED_FILES"
# Check if any of the changed files are in docs/ or .github/
echo "Checking if changes are docs-only..."
DOCS_ONLY=true
while IFS= read -r file; do
if [[ ! "$file" =~ ^(docs/|.github/) ]]; then
echo "Found non-docs change: $file"
DOCS_ONLY=false
break
fi
done <<< "$CHANGED_FILES"
# If changes are docs-only, skip changeset check
if [ "$DOCS_ONLY" = true ]; then
echo "All changes are in docs/ or .github/, skipping changeset check"
exit 0
else
echo "Changes include non-docs files, checking for changeset..."
fi
# Check if any changeset files are in the changed files
echo "Checking for changeset files in changed files..."
CHANGESET_IN_PR=false
while IFS= read -r file; do
if [[ "$file" =~ ^\.changeset/.*\.md$ && "$file" != ".changeset/README.md" && "$file" != ".changeset/config.json" ]]; then
echo "Found changeset file in PR: $file"
CHANGESET_IN_PR=true
break
fi
done <<< "$CHANGED_FILES"
if [ "$CHANGESET_IN_PR" = false ]; then
echo "No changeset files found in changed files. Changed files in .changeset/:"
echo "$CHANGED_FILES" | grep "^\.changeset/" || true
echo "::error::No changeset file found in PR changes. Please run 'npm run changeset' to create one."
exit 1
fi
- name: Comment on PR
if: failure()
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7
with:
script: |
const message = `This PR requires a changeset since it includes user-facing changes. Please:
1. Run \`npm run changeset\` locally
2. Choose the appropriate version bump:
- \`major\` for breaking changes (1.0.0 → 2.0.0)
- \`minor\` for new features (1.0.0 → 1.1.0)
- \`patch\` for bug fixes (1.0.0 → 1.0.1)
3. Write a clear description of your changes
4. Commit the generated changeset file
Note: Documentation-only changes do not require a changeset.`;
// Get existing comments
const comments = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number
});
// Check if we already commented
const botComment = comments.data.find(comment =>
comment.user.login === 'github-actions[bot]' &&
comment.body.includes('This PR requires a changeset')
);
if (!botComment) {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: message
});
}
+3
View File
@@ -70,8 +70,10 @@ jobs:
echo "version=$VERSION" >> $GITHUB_OUTPUT
- name: Create Git Tag
id: create_tag
run: |
VERSION=v${{ steps.get_version.outputs.version }}
echo "tag=$VERSION" >> $GITHUB_OUTPUT
echo "Tagging with $VERSION"
git tag "$VERSION"
git push origin "$VERSION"
@@ -104,6 +106,7 @@ jobs:
- name: Create GitHub Release
uses: softprops/action-gh-release@v1
with:
tag_name: ${{ steps.create_tag.outputs.tag }}
files: "*.vsix"
# body: ${{ steps.changelog.outputs.content }}
generate_release_notes: true
+3 -1
View File
@@ -7,4 +7,6 @@ tmp
.DS_Store
pnpm-lock.yaml
pnpm-lock.yaml
.clineignore
+5 -1
View File
@@ -11,7 +11,11 @@
"request": "launch",
"args": ["--extensionDevelopmentPath=${workspaceFolder}"],
"outFiles": ["${workspaceFolder}/dist/**/*.js"],
"preLaunchTask": "${defaultBuildTask}"
"preLaunchTask": "${defaultBuildTask}",
"env": {
"IS_DEV": "true",
"DEV_WORKSPACE_FOLDER": "${workspaceFolder}"
}
}
]
}
+59 -4
View File
@@ -5,7 +5,7 @@
"tasks": [
{
"label": "watch",
"dependsOn": ["npm: build:webview", "npm: watch:tsc", "npm: watch:esbuild"],
"dependsOn": ["npm: build:webview", "npm: dev:webview", "npm: watch:tsc", "npm: watch:esbuild"],
"presentation": {
"reveal": "never"
},
@@ -23,7 +23,47 @@
"label": "npm: build:webview",
"presentation": {
"group": "watch",
"reveal": "never"
"reveal": "never",
"close": true
},
"options": {
"env": {
"IS_DEV": "true"
}
}
},
{
"type": "npm",
"script": "dev:webview",
"group": "build",
"problemMatcher": [
{
"pattern": [
{
"regexp": ".",
"file": 1,
"location": 2,
"message": 3
}
],
"background": {
"activeOnStart": true,
"beginsPattern": ".",
"endsPattern": "."
}
}
],
"isBackground": true,
"label": "npm: dev:webview",
"presentation": {
"group": "watch",
"reveal": "never",
"close": true
},
"options": {
"env": {
"IS_DEV": "true"
}
}
},
{
@@ -35,7 +75,8 @@
"label": "npm: watch:esbuild",
"presentation": {
"group": "watch",
"reveal": "never"
"reveal": "never",
"close": true
}
},
{
@@ -47,7 +88,8 @@
"label": "npm: watch:tsc",
"presentation": {
"group": "watch",
"reveal": "never"
"reveal": "never",
"close": true
}
},
{
@@ -65,6 +107,19 @@
"label": "tasks: watch-tests",
"dependsOn": ["npm: watch", "npm: watch-tests"],
"problemMatcher": []
},
{
"label": "stop",
"command": "echo ${input:terminate}",
"type": "shell"
}
],
"inputs": [
{
"id": "terminate",
"type": "command",
"command": "workbench.action.tasks.terminate",
"args": "terminateAll"
}
]
}
-1
View File
@@ -23,7 +23,6 @@ demo.gif
# Ignore all webview-ui files except the build directory (https://github.com/microsoft/vscode-webview-ui-toolkit-samples/blob/main/frameworks/hello-world-react-cra/.vscodeignore)
webview-ui/src/**
webview-ui/public/**
webview-ui/scripts/**
webview-ui/index.html
webview-ui/README.md
webview-ui/package.json
+266 -130
View File
@@ -1,344 +1,480 @@
# Changelog
## [3.8.2]
- Fix bug where switching to plan/act would result in VS Code LM/OpenRouter model being reset
## [3.8.0]
- Add 'Add to Cline' as an option when you right-click in a file or the terminal, making it easier to add context to your current task
- Add 'Fix with Cline' code action - when you see a lightbulb icon in your editor, you can now select 'Fix with Cline' to send the code and associated errors for Cline to fix. (Cursor users can also use the 'Quick Fix (CMD + .)' menu to see this option)
- Add Account view to display billing and usage history for Cline account users. You can now keep track of credits used and transaction history right in the extension!
- Add 'Sort underling provider routing' setting to Cline/OpenRouter allowing you to sort provider used by throughput, price, latency, or the default (combination of price and uptime)
- Improve rich MCP display with dynamic image loading and support for GIFs
- Add 'Documentation' menu item to easily access Cline's docs
- Add OpenRouter's new usage_details feature for more reliable cost reporting
- Display total space Cline takes on disk next to 'Delete all Tasks' button in History view
- Fix 'Context Window Exceeded' error for OpenRouter/Cline Accounts (additional support coming soon)
- Fix bug where OpenRouter model ID would be set to invalid value
- Add button to delete MCP servers in a failure state
## [3.7.1]
- Fix issue with 'See more' button in task header not showing when starting new tasks
- Fix issue with checkpoints using local git commit hooks
## [3.7.0]
- Cline now displays selectable options when asking questions or presenting a plan, saving you from having to type out responses!
- Add support for a `.clinerules/` directory to load multiple files at once (thanks @ryo-ma!)
- Prevent Cline from reading extremely large files into context that would overload context window
- Improve checkpoints loading performance and display warning for large projects not suited for checkpoints
- Add SambaNova API provider (thanks @saad-noodleseed!)
- Add VPC endpoint option for AWS Bedrock profiles (thanks @minorunara!)
- Add DeepSeek-R1 to AWS Bedrock (thanks @watany-dev!)
## [3.6.5]
- Add 'Delete all Task History' button to History view
- Add toggle to disable model switching between Plan/Act modes in Settings (new users default to disabled)
- Add temperature option to OpenAI Compatible
- Add Kotlin support to tree-sitter parser (thanks @fumiya-kume!)
## [3.6.3]
- Improve QwQ support for Alibaba (thanks @meglinge!) and OpenRouter
- Improve diff edit prompting to prevent immediately reverting to write_to_file when a model uses search patterns that don't match anything in the file
- Fix bug where new checkpoints system would revert file changes when switching between tasks
- Fix issue with incorrect token count for some OpenAI compatible providers
## [3.6.0]
- Add Cline API as a provider option, allowing new users to sign up and get started with Cline for free
- Optimize checkpoints with branch-per-task strategy, reducing storage required and first task load times
- Fix problem with Plan/Act toggle keyboard shortcut not working in Windows (thanks @yt3trees!)
- Add new Gemini models to GCP Vertex (thanks @shohei-ihaya!) and Claude models AskSage (thanks @swhite24!)
- Improve OpenRouter/Cline error reporting
## [3.5.1]
- Add timeout option to MCP servers
- Add Gemini Flash models to Vertex provider (thanks @jpaodev!)
- Add prompt caching support for AWS Bedrock provider (thanks @buger!)
- Add AskSage provider (thanks @swhite24!)
## [3.5.0]
- Add 'Enable extended thinking' option for Claude 3.7 Sonnet, with ability to set different budgets for Plan and Act modes
- Add support for rich MCP responses with automatic image previews, website thumbnails, and WolframAlpha visualizations
- Add language preference option in Advanced Settings
- Add xAI Provider Integration with support for all Grok models (thanks @andrewmonostate!)
- Fix issue with Linux XDG pointing to incorrect path for Document folder (thanks @jonatkinson!)
## [3.4.10]
- Add support for GPT-4.5 preview model
## [3.4.9]
- Add toggle to let users opt-in to anonymous telemetry and error reporting
## [3.4.6]
- Add support for Claude 3.7 Sonnet
## [3.4.0]
- Introducing MCP Marketplace! You can now discover and install the best MCP servers right from within the extension, with new servers added regularly
- Add mermaid diagram support in Plan mode! You can now see visual representations of mermaid code blocks in chat, and click on them to see an expanded view
- Use more visual checkpoints indicators after editing files & running commands
- Create a checkpoint at the beginning of each task to easily revert to the initial state
- Add 'Terminal' context mention to reference the active terminal's contents
- Add 'Git Commits' context mention to reference current working changes or specific commits (thanks @mrubens!)
- Send current textfield contents as additional feedback when toggling from Plan to Act Mode, or when hitting 'Approve' button
- Add advanced configuration options for OpenAI Compatible (context window, max output, pricing, etc.)
- Add Alibaba Qwen 2.5 coder models, VL models, and DeepSeek-R1/V3 support
- Improve support for AWS Bedrock Profiles
- Fix Mistral provider support for non-codestral models
- Add advanced setting to disable browser tool
- Add advanced setting to set chromium executable path for browser tool
## [3.3.2]
- Fix bug where OpenRouter requests would periodically not return cost/token stats, leading to context window limit errors
- Make checkpoints more visible and keep track of restored checkpoints
## [3.3.0]
- Add .clineignore to block Cline from accessing specified file patterns
- Add keyboard shortcut + tooltips for Plan/Act toggle
- Fix bug where new files won't show up in files dropdown
- Add automatic retry for rate limited requests (thanks @ViezeVingertjes!)
- Adding reasoning_effort support for o3-mini in Advanced Settings
- Added support for AWS provider profiles using the AWS CLI to make the profile, enabling long lived connections to AWS bedrock
- Adding Requesty API provider
- Add Together API provider
- Add Alibaba Qwen API provider (thanks @aicccode!)
## [3.2.13]
- Add new gemini models gemini-2.0-flash-lite-preview-02-05 and gemini-2.0-flash-001
- Add all available Mistral API models (thanks @ViezeVingertjes!)
- Add LiteLLM API provider support (thanks @him0!)
## [3.2.12]
- Fix command chaining for Windows users
- Fix reasoning_content error for OpenAI providers
## [3.2.11]
- Add OpenAI o3-mini model
## [3.2.10]
- Improve support for DeepSeek-R1 (deepseek-reasoner) model for OpenRouter, OpenAI-compatible, and DeepSeek direct (thanks @Szpadel!)
- Show Reasoning tokens for models that support it
- Fix issues with switching models between Plan/Act modes
## [3.2.6]
- Save last used API/model when switching between Plan and Act, for users that like to use different models for each mode
- New Context Window progress bar in the task header to understand increased cost/generation degradation as the context increases
- Localize READMEs and add language selector for English, Spanish, German, Chinese, and Japanese
- Add Advanced Settings to remove MCP prompts from requests to save tokens, enable/disable checkpoints for users that don't use git (more coming soon!)
- Add Gemini 2.0 Flash Thinking experimental model
- Allow new users to subscribe to mailing list to get notified when new Accounts option is available
- Save last used API/model when switching between Plan and Act, for users that like to use different models for each mode
- New Context Window progress bar in the task header to understand increased cost/generation degradation as the context increases
- Localize READMEs and add language selector for English, Spanish, German, Chinese, and Japanese
- Add Advanced Settings to remove MCP prompts from requests to save tokens, enable/disable checkpoints for users that don't use git (more coming soon!)
- Add Gemini 2.0 Flash Thinking experimental model
- Allow new users to subscribe to mailing list to get notified when new Accounts option is available
## [3.2.5]
- Use yellow textfield outline in Plan mode to better distinguish from Act mode
- Use yellow textfield outline in Plan mode to better distinguish from Act mode
## [3.2.3]
- Add DeepSeek-R1 (deepseek-reasoner) model support with proper parameter handling (thanks @slavakurilyak!)
- Add DeepSeek-R1 (deepseek-reasoner) model support with proper parameter handling (thanks @slavakurilyak!)
## [3.2.0]
- Add Plan/Act mode toggle to let you plan tasks with Cline before letting him get to work
- Easily switch between API providers and models using a new popup menu under the chat field
- Add VS Code LM API provider to run models provided by other VS Code extensions (e.g. GitHub Copilot). Shoutout to @julesmons, @RaySinner, and @MrUbens for putting this together!
- Add on/off toggle for MCP servers to disable them when not in use. Thanks @MrUbens!
- Add Auto-approve option for individual tools in MCP servers. Thanks @MrUbens!
- Add Plan/Act mode toggle to let you plan tasks with Cline before letting him get to work
- Easily switch between API providers and models using a new popup menu under the chat field
- Add VS Code LM API provider to run models provided by other VS Code extensions (e.g. GitHub Copilot). Shoutout to @julesmons, @RaySinner, and @MrUbens for putting this together!
- Add on/off toggle for MCP servers to disable them when not in use. Thanks @MrUbens!
- Add Auto-approve option for individual tools in MCP servers. Thanks @MrUbens!
## [3.1.10]
- New icon!
- New icon!
## [3.1.9]
- Add Mistral API provider with codestral-latest model
- Add Mistral API provider with codestral-latest model
## [3.1.7]
- Add ability to change viewport size and headless mode when Cline asks to launch the browser
- Add ability to change viewport size and headless mode when Cline asks to launch the browser
## [3.1.6]
- Fix bug where filepaths with Chinese characters would not show up in context mention menu (thanks @chi-chat!)
- Update Anthropic model prices (thanks @timoteostewart!)
- Fix bug where filepaths with Chinese characters would not show up in context mention menu (thanks @chi-chat!)
- Update Anthropic model prices (thanks @timoteostewart!)
## [3.1.5]
- Fix bug where Cline couldn't read "@/" import path aliases from tool results
- Fix bug where Cline couldn't read "@/" import path aliases from tool results
## [3.1.4]
- Fix issue where checkpoints would not work for users with git commit signing enabled globally
- Fix issue where checkpoints would not work for users with git commit signing enabled globally
## [3.1.2]
- Fix issue where LFS files would be not be ignored when creating checkpoints
- Fix issue where LFS files would be not be ignored when creating checkpoints
## [3.1.0]
- Added checkpoints: Snapshots of workspace are automatically created whenever Cline uses a tool
- Compare changes: Hover over any tool use to see a diff between the snapshot and current workspace state
- Restore options: Choose to restore just the task state, just the workspace files, or both
- New 'See new changes' button appears after task completion, providing an overview of all workspace changes
- Task header now shows disk space usage with a delete button to help manage snapshot storage
- Added checkpoints: Snapshots of workspace are automatically created whenever Cline uses a tool
- Compare changes: Hover over any tool use to see a diff between the snapshot and current workspace state
- Restore options: Choose to restore just the task state, just the workspace files, or both
- New 'See new changes' button appears after task completion, providing an overview of all workspace changes
- Task header now shows disk space usage with a delete button to help manage snapshot storage
## [3.0.12]
- Fix DeepSeek API cost reporting (input price is 0 since it's all either a cache read or write, different than how Anthropic reports cache usage)
- Fix DeepSeek API cost reporting (input price is 0 since it's all either a cache read or write, different than how Anthropic reports cache usage)
## [3.0.11]
- Emphasize auto-formatting done by the editor in file edit responses for more reliable diff editing
- Emphasize auto-formatting done by the editor in file edit responses for more reliable diff editing
## [3.0.10]
- Add DeepSeek provider to API Provider options
- Fix context window limit errors for DeepSeek v3
- Add DeepSeek provider to API Provider options
- Fix context window limit errors for DeepSeek v3
## [3.0.9]
- Fix bug where DeepSeek v3 would incorrectly escape HTML entities in diff edits
- Fix bug where DeepSeek v3 would incorrectly escape HTML entities in diff edits
## [3.0.8]
- Mitigate DeepSeek v3 diff edit errors by adding 'auto-formatting considerations' to system prompt, encouraging model to use updated file contents as reference point for SEARCH blocks
- Mitigate DeepSeek v3 diff edit errors by adding 'auto-formatting considerations' to system prompt, encouraging model to use updated file contents as reference point for SEARCH blocks
## [3.0.7]
- Revert to using batched file watcher to fix crash when many files would be created at once
- Revert to using batched file watcher to fix crash when many files would be created at once
## [3.0.6]
- Fix bug where some files would be missing in the `@` context mention menu
- Add Bedrock support in additional regions
- Diff edit improvements
- Add OpenRouter's middle-out transform for models that don't use prompt caching (prevents context window limit errors, but cannot be applied to models like Claude since it would continuously break the cache)
- Fix bug where some files would be missing in the `@` context mention menu
- Add Bedrock support in additional regions
- Diff edit improvements
- Add OpenRouter's middle-out transform for models that don't use prompt caching (prevents context window limit errors, but cannot be applied to models like Claude since it would continuously break the cache)
## [3.0.4]
- Fix bug where gemini models would add code block artifacts to the end of text content
- Fix context mention menu visual issues on light themes
- Fix bug where gemini models would add code block artifacts to the end of text content
- Fix context mention menu visual issues on light themes
## [3.0.2]
- Adds block anchor matching for more reliable diff edits (if 3+ lines, first and last line are used as anchors to search for)
- Add instruction to system prompt to use complete lines in diff edits to work properly with fallback strategies
- Improves diff edit error handling
- Adds new Gemini models
- Adds block anchor matching for more reliable diff edits (if 3+ lines, first and last line are used as anchors to search for)
- Add instruction to system prompt to use complete lines in diff edits to work properly with fallback strategies
- Improves diff edit error handling
- Adds new Gemini models
## [3.0.0]
- Cline now uses a search & replace diff based approach when editing large files to prevent code deletion issues.
- Adds support for a more comprehensive auto-approve configuration, allowing you to specify which tools require approval and which don't.
- Adds ability to enable system notifications for when Cline needs approval or completes a task.
- Adds support for a root-level `.clinerules` file that can be used to specify custom instructions for the project.
- Cline now uses a search & replace diff based approach when editing large files to prevent code deletion issues.
- Adds support for a more comprehensive auto-approve configuration, allowing you to specify which tools require approval and which don't.
- Adds ability to enable system notifications for when Cline needs approval or completes a task.
- Adds support for a root-level `.clinerules` file that can be used to specify custom instructions for the project.
## [2.2.0]
- Add support for Model Context Protocol (MCP), enabling Cline to use custom tools like web-search tool or GitHub tool
- Add MCP server management tab accessible via the server icon in the menu bar
- Add ability for Cline to dynamically create new MCP servers based on user requests (e.g., "add a tool that gets the latest npm docs")
- Add support for Model Context Protocol (MCP), enabling Cline to use custom tools like web-search tool or GitHub tool
- Add MCP server management tab accessible via the server icon in the menu bar
- Add ability for Cline to dynamically create new MCP servers based on user requests (e.g., "add a tool that gets the latest npm docs")
## [2.1.6]
- Add LM Studio as an API provider option (make sure to start the LM Studio server to use it with the extension!)
- Add LM Studio as an API provider option (make sure to start the LM Studio server to use it with the extension!)
## [2.1.5]
- Add support for prompt caching for new Claude model IDs on OpenRouter (e.g. `anthropic/claude-3.5-sonnet-20240620`)
- Add support for prompt caching for new Claude model IDs on OpenRouter (e.g. `anthropic/claude-3.5-sonnet-20240620`)
## [2.1.4]
- AWS Bedrock fixes (add missing regions, support for cross-region inference, and older Sonnet model for regions where new model is not available)
- AWS Bedrock fixes (add missing regions, support for cross-region inference, and older Sonnet model for regions where new model is not available)
## [2.1.3]
- Add support for Claude 3.5 Haiku, 66% cheaper than Sonnet with similar intelligence
- Add support for Claude 3.5 Haiku, 66% cheaper than Sonnet with similar intelligence
## [2.1.2]
- Misc. bug fixes
- Update README with new browser feature
- Misc. bug fixes
- Update README with new browser feature
## [2.1.1]
- Add stricter prompt to prevent Cline from editing files during a browser session without first closing the browser
- Add stricter prompt to prevent Cline from editing files during a browser session without first closing the browser
## [2.1.0]
- Cline now uses Anthropic's new "Computer Use" feature to launch a browser, click, type, and scroll. This gives him more autonomy in runtime debugging, end-to-end testing, and even general web use. Try asking "Look up the weather in Colorado" to see it in action! (Available with Claude 3.5 Sonnet v2)
- Cline now uses Anthropic's new "Computer Use" feature to launch a browser, click, type, and scroll. This gives him more autonomy in runtime debugging, end-to-end testing, and even general web use. Try asking "Look up the weather in Colorado" to see it in action! (Available with Claude 3.5 Sonnet v2)
## [2.0.19]
- Fix model info for Claude 3.5 Sonnet v1 on OpenRouter
- Fix model info for Claude 3.5 Sonnet v1 on OpenRouter
## [2.0.18]
- Add support for both v1 and v2 of Claude 3.5 Sonnet for GCP Vertex and AWS Bedrock (for cases where the new model is not enabled yet or unavailable in your region)
- Add support for both v1 and v2 of Claude 3.5 Sonnet for GCP Vertex and AWS Bedrock (for cases where the new model is not enabled yet or unavailable in your region)
## [2.0.17]
- Update Anthropic model IDs
- Update Anthropic model IDs
## [2.0.16]
- Adjustments to system prompt
- Adjustments to system prompt
## [2.0.15]
- Fix bug where modifying Cline's edits would lead him to try to re-apply the edits
- Fix bug where weaker models would display file contents before using the write_to_file tool
- Fix o1-mini and o1-preview errors when using OpenAI native
- Fix bug where modifying Cline's edits would lead him to try to re-apply the edits
- Fix bug where weaker models would display file contents before using the write_to_file tool
- Fix o1-mini and o1-preview errors when using OpenAI native
## [2.0.14]
- Gracefully cancel requests while stream could be hanging
- Gracefully cancel requests while stream could be hanging
## [2.0.13]
- Detect code omission and show warning with troubleshooting link
- Detect code omission and show warning with troubleshooting link
## [2.0.12]
- Keep cursor out of the way during file edit streaming animation
- Keep cursor out of the way during file edit streaming animation
## [2.0.11]
- Adjust prompts around read_file to prevent re-reading files unnecessarily
- Adjust prompts around read_file to prevent re-reading files unnecessarily
## [2.0.10]
- More adjustments to system prompt to prevent lazy coding
- More adjustments to system prompt to prevent lazy coding
## [2.0.9]
- Update system prompt to try to prevent Cline from lazy coding (`// rest of code here...`)
- Update system prompt to try to prevent Cline from lazy coding (`// rest of code here...`)
## [2.0.8]
- Fix o1-mini and o1-preview for OpenAI
- Fix diff editor not opening sometimes in slow environments like project idx
- Fix o1-mini and o1-preview for OpenAI
- Fix diff editor not opening sometimes in slow environments like project idx
## [2.0.7]
- Misc. bug fixes
- Misc. bug fixes
## [2.0.6]
- Update URLs to https://github.com/cline/cline
- Update URLs to https://github.com/cline/cline
## [2.0.5]
- Fixed bug where Cline's edits would stream into the active tab when switching tabs during a write_to_file
- Added explanation in task continuation prompt that an interrupted write_to_file reverts the file to its original contents, preventing unnecessary re-reads
- Fixed non-first chunk error handling in case stream fails mid-way through
- Fixed bug where Cline's edits would stream into the active tab when switching tabs during a write_to_file
- Added explanation in task continuation prompt that an interrupted write_to_file reverts the file to its original contents, preventing unnecessary re-reads
- Fixed non-first chunk error handling in case stream fails mid-way through
## [2.0.0]
- New name! Meet Cline, an AI assistant that can use your CLI and Editor
- Responses are now streamed with a yellow text decoration animation to keep track of Cline's progress as he edits files
- New Cancel button to give Cline feedback if he goes off in the wrong direction, giving you more control over tasks
- Re-imagined tool calling prompt resulting in ~40% fewer requests to accomplish tasks + better performance with other models
- Search and use any model with OpenRouter
- New name! Meet Cline, an AI assistant that can use your CLI and Editor
- Responses are now streamed with a yellow text decoration animation to keep track of Cline's progress as he edits files
- New Cancel button to give Cline feedback if he goes off in the wrong direction, giving you more control over tasks
- Re-imagined tool calling prompt resulting in ~40% fewer requests to accomplish tasks + better performance with other models
- Search and use any model with OpenRouter
## [1.9.7]
- Only auto-include error diagnostics after file edits, removed warnings to keep Claude from getting distracted in projects with strict linting rules
- Only auto-include error diagnostics after file edits, removed warnings to keep Claude from getting distracted in projects with strict linting rules
## [1.9.6]
- Added support for new Google Gemini models `gemini-1.5-flash-002` and `gemini-1.5-pro-002`
- Updated system prompt to be more lenient when terminal output doesn't stream back properly
- Adjusted system prompt to prevent overuse of the inspect_site tool
- Increased global line height for improved readability
- Added support for new Google Gemini models `gemini-1.5-flash-002` and `gemini-1.5-pro-002`
- Updated system prompt to be more lenient when terminal output doesn't stream back properly
- Adjusted system prompt to prevent overuse of the inspect_site tool
- Increased global line height for improved readability
## [1.9.0]
- Claude can now use a browser! This update adds a new `inspect_site` tool that captures screenshots and console logs from websites (including localhost), making it easier for Claude to troubleshoot issues on his own.
- Improved automatic linter/compiler debugging by only sending Claude new errors that result from his edits, rather than reporting all workspace problems.
- Claude can now use a browser! This update adds a new `inspect_site` tool that captures screenshots and console logs from websites (including localhost), making it easier for Claude to troubleshoot issues on his own.
- Improved automatic linter/compiler debugging by only sending Claude new errors that result from his edits, rather than reporting all workspace problems.
## [1.8.0]
- You can now use '@' in the textarea to add context!
- @url: Paste in a URL for the extension to fetch and convert to markdown, useful when you want to give Claude the latest docs!
- @problems: Add workspace errors and warnings for Claude to fix, no more back-and-forth about debugging
- @file: Adds a file's contents so you don't have to waste API requests approving read file (+ type to search files)
- @folder: Adds folder's files all at once to speed up your workflow even more
- You can now use '@' in the textarea to add context!
- @url: Paste in a URL for the extension to fetch and convert to markdown, useful when you want to give Claude the latest docs!
- @problems: Add workspace errors and warnings for Claude to fix, no more back-and-forth about debugging
- @file: Adds a file's contents so you don't have to waste API requests approving read file (+ type to search files)
- @folder: Adds folder's files all at once to speed up your workflow even more
## [1.7.0]
- Adds problems monitoring to keep Claude updated on linter/compiler/build issues, letting him proactively fix errors on his own! (adding missing imports, fixing type errors, etc.)
- Adds problems monitoring to keep Claude updated on linter/compiler/build issues, letting him proactively fix errors on his own! (adding missing imports, fixing type errors, etc.)
## [1.6.5]
- Adds support for OpenAI o1, Azure OpenAI, and Google Gemini (free for up to 15 requests per minute!)
- Task header can now be collapsed to provide more space for viewing conversations
- Adds fuzzy search and sorting to Task History, making it easier to find specific tasks
- Adds support for OpenAI o1, Azure OpenAI, and Google Gemini (free for up to 15 requests per minute!)
- Task header can now be collapsed to provide more space for viewing conversations
- Adds fuzzy search and sorting to Task History, making it easier to find specific tasks
## [1.6.0]
- Commands now run directly in your terminal thanks to VSCode 1.93's new shell integration updates! Plus a new 'Proceed While Running' button to let Claude continue working while commands run, sending him new output along the way (i.e. letting him react to server errors as he edits files)
- Commands now run directly in your terminal thanks to VSCode 1.93's new shell integration updates! Plus a new 'Proceed While Running' button to let Claude continue working while commands run, sending him new output along the way (i.e. letting him react to server errors as he edits files)
## [1.5.27]
- Claude's changes now appear in your file's Timeline, allowing you to easily view a diff of each edit. This is especially helpful if you want to revert to a previous version. No need for git—everything is tracked by VSCode's local history!
- Updated system prompt to keep Claude from re-reading files unnecessarily
- Claude's changes now appear in your file's Timeline, allowing you to easily view a diff of each edit. This is especially helpful if you want to revert to a previous version. No need for git—everything is tracked by VSCode's local history!
- Updated system prompt to keep Claude from re-reading files unnecessarily
## [1.5.19]
- Adds support for OpenAI compatible API providers (e.g. Ollama!)
- Adds support for OpenAI compatible API providers (e.g. Ollama!)
## [1.5.13]
- New terminal emulator! When Claude runs commands, you can now type directly in the terminal (+ support for Python environments)
- Adds search to Task History
- New terminal emulator! When Claude runs commands, you can now type directly in the terminal (+ support for Python environments)
- Adds search to Task History
## [1.5.6]
- You can now edit Claude's changes before accepting! When he edits or creates a file, you can modify his changes directly in the right side of the diff view (+ hover over the 'Revert Block' arrow button in the center to undo `// rest of code here` shenanigans)
- You can now edit Claude's changes before accepting! When he edits or creates a file, you can modify his changes directly in the right side of the diff view (+ hover over the 'Revert Block' arrow button in the center to undo `// rest of code here` shenanigans)
## [1.5.4]
- Adds support for reading .pdf and .docx files (try "turn my business_plan.docx into a company website")
- Adds support for reading .pdf and .docx files (try "turn my business_plan.docx into a company website")
## [1.5.0]
- Adds new `search_files` tool that lets Claude perform regex searches in your project, making it easy for him to refactor code, address TODOs and FIXMEs, remove dead code, and more!
- Adds new `search_files` tool that lets Claude perform regex searches in your project, making it easy for him to refactor code, address TODOs and FIXMEs, remove dead code, and more!
## [1.4.0]
- Adds "Always allow read-only operations" setting to let Claude read files and view directories without needing approval (off by default)
- Implement sliding window context management to keep tasks going past 200k tokens
- Adds Google Cloud Vertex AI support and updates Claude 3.5 Sonnet max output to 8192 tokens for all providers.
- Improves system prompt to gaurd against lazy edits (less "//rest of code here")
- Adds "Always allow read-only operations" setting to let Claude read files and view directories without needing approval (off by default)
- Implement sliding window context management to keep tasks going past 200k tokens
- Adds Google Cloud Vertex AI support and updates Claude 3.5 Sonnet max output to 8192 tokens for all providers.
- Improves system prompt to gaurd against lazy edits (less "//rest of code here")
## [1.3.0]
- Adds task history
- Adds task history
## [1.2.0]
- Adds support for Prompt Caching to significantly reduce costs and response times (currently only available through Anthropic API for Claude 3.5 Sonnet and Claude 3.0 Haiku)
- Adds support for Prompt Caching to significantly reduce costs and response times (currently only available through Anthropic API for Claude 3.5 Sonnet and Claude 3.0 Haiku)
## [1.1.1]
- Adds option to choose other Claude models (+ GPT-4o, DeepSeek, and Mistral if you use OpenRouter)
- Adds option to add custom instructions to the end of the system prompt
- Adds option to choose other Claude models (+ GPT-4o, DeepSeek, and Mistral if you use OpenRouter)
- Adds option to add custom instructions to the end of the system prompt
## [1.1.0]
- Paste images in chat to use Claude's vision capabilities and turn mockups into fully functional applications or fix bugs with screenshots
- Paste images in chat to use Claude's vision capabilities and turn mockups into fully functional applications or fix bugs with screenshots
## [1.0.9]
- Add support for OpenRouter and AWS Bedrock
- Add support for OpenRouter and AWS Bedrock
## [1.0.8]
- Shows diff view of new or edited files right in the editor
- Shows diff view of new or edited files right in the editor
## [1.0.7]
- Replace `list_files` and `analyze_project` with more explicit `list_files_top_level`, `list_files_recursive`, and `view_source_code_definitions_top_level` to get source code definitions only for files relevant to the task
- Replace `list_files` and `analyze_project` with more explicit `list_files_top_level`, `list_files_recursive`, and `view_source_code_definitions_top_level` to get source code definitions only for files relevant to the task
## [1.0.6]
- Interact with CLI commands by sending messages to stdin and terminating long-running processes like servers
- Export tasks to markdown files (useful as context for future tasks)
- Interact with CLI commands by sending messages to stdin and terminating long-running processes like servers
- Export tasks to markdown files (useful as context for future tasks)
## [1.0.5]
- Claude now has context about vscode's visible editors and opened tabs
- Claude now has context about vscode's visible editors and opened tabs
## [1.0.4]
- Open in the editor (using menu bar or `Claude Dev: Open In New Tab` in command palette) to see how Claude updates your workspace more clearly
- New `analyze_project` tool to help Claude get a comprehensive overview of your project's source code definitions and file structure
- Provide feedback to tool use like terminal commands and file edits
- Updated max output tokens to 8192 so less lazy coding (`// rest of code here...`)
- Added ability to retry failed API requests (helpful for rate limits)
- Quality of life improvements like markdown rendering, memory optimizations, better theme support
- Open in the editor (using menu bar or `Claude Dev: Open In New Tab` in command palette) to see how Claude updates your workspace more clearly
- New `analyze_project` tool to help Claude get a comprehensive overview of your project's source code definitions and file structure
- Provide feedback to tool use like terminal commands and file edits
- Updated max output tokens to 8192 so less lazy coding (`// rest of code here...`)
- Added ability to retry failed API requests (helpful for rate limits)
- Quality of life improvements like markdown rendering, memory optimizations, better theme support
## [0.0.6]
- Initial release
- Initial release
+13 -3
View File
@@ -56,20 +56,30 @@ Anyone can contribute code to Cline, but we ask that you follow these guidelines
- Update existing tests if your changes affect them
- Include both unit tests and integration tests where appropriate
4. **Commit Guidelines**
4. **Version Management with Changesets**
- Create a changeset for any user-facing changes using `npm run changeset`
- Choose the appropriate version bump:
- `major` for breaking changes (1.0.0 → 2.0.0)
- `minor` for new features (1.0.0 → 1.1.0)
- `patch` for bug fixes (1.0.0 → 1.0.1)
- Write clear, descriptive changeset messages that explain the impact
- Documentation-only changes don't require changesets
5. **Commit Guidelines**
- Write clear, descriptive commit messages
- Use conventional commit format (e.g., "feat:", "fix:", "docs:")
- Reference relevant issues in commits using #issue-number
5. **Before Submitting**
6. **Before Submitting**
- Rebase your branch on the latest main
- Ensure your branch builds successfully
- Double-check all tests are passing
- Review your changes for any debugging code or console logs
6. **Pull Request Description**
7. **Pull Request Description**
- Clearly describe what your changes do
- Include steps to test the changes
- List any breaking changes
+2 -2
View File
@@ -186,7 +186,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2024 Cline Bot Inc.
Copyright 2025 Cline Bot Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -198,4 +198,4 @@
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
limitations under the License.
+4 -4
View File
@@ -1,5 +1,5 @@
<div align="center"><sub>
English | <a href="https://github.com/cline/cline/blob/main/locales/es/README.md" target="_blank">Español</a> | <a href="https://github.com/cline/cline/blob/main/locales/de/README.md" target="_blank">Deutsch</a> | <a href="https://github.com/cline/cline/blob/main/locales/ja/README.md" target="_blank">日本語</a> | <a href="https://github.com/cline/cline/blob/main/locales/zh-cn/README.md" target="_blank">简体中文</a> | <a href="https://github.com/cline/cline/blob/main/locales/zh-tw/README.md" target="_blank">繁體中文</a>
English | <a href="https://github.com/cline/cline/blob/main/locales/es/README.md" target="_blank">Español</a> | <a href="https://github.com/cline/cline/blob/main/locales/de/README.md" target="_blank">Deutsch</a> | <a href="https://github.com/cline/cline/blob/main/locales/ja/README.md" target="_blank">日本語</a> | <a href="https://github.com/cline/cline/blob/main/locales/zh-cn/README.md" target="_blank">简体中文</a> | <a href="https://github.com/cline/cline/blob/main/locales/zh-tw/README.md" target="_blank">繁體中文</a> | <a href="https://github.com/cline/cline/blob/main/locales/ko/README.md" target="_blank">한국어</a>
</sub></div>
# Cline \#1 on OpenRouter
@@ -24,7 +24,7 @@ English | <a href="https://github.com/cline/cline/blob/main/locales/es/README.md
<a href="https://github.com/cline/cline/discussions/categories/feature-requests?discussions_q=is%3Aopen+category%3A%22Feature+Requests%22+sort%3Atop" target="_blank"><strong>Feature Requests</strong></a>
</td>
<td align="center">
<a href="https://cline.bot/join-us" target="_blank"><strong>We're Hiring!</strong></a>
<a href="https://docs.cline.bot/getting-started/getting-started-new-coders" target="_blank"><strong>Getting Started</strong></a>
</td>
</tbody>
</table>
@@ -32,7 +32,7 @@ English | <a href="https://github.com/cline/cline/blob/main/locales/es/README.md
Meet Cline, an AI assistant that can use your **CLI** a**N**d **E**ditor.
Thanks to [Claude 3.5 Sonnet's agentic coding capabilities](https://www-cdn.anthropic.com/fed9cc193a14b84131812372d8d5857f8f304c52/Model_Card_Claude_3_Addendum.pdf), Cline can handle complex software development tasks step-by-step. With tools that let him create & edit files, explore large projects, use the browser, and execute terminal commands (after you grant permission), he can assist you in ways that go beyond code completion or tech support. Cline can even use the Model Context Protocol (MCP) to create new tools and extend his own capabilities. While autonomous AI scripts traditionally run in sandboxed environments, this extension provides a human-in-the-loop GUI to approve every file change and terminal command, providing a safe and accessible way to explore the potential of agentic AI.
Thanks to [Claude 3.7 Sonnet's agentic coding capabilities](https://www.anthropic.com/claude/sonnet), Cline can handle complex software development tasks step-by-step. With tools that let him create & edit files, explore large projects, use the browser, and execute terminal commands (after you grant permission), he can assist you in ways that go beyond code completion or tech support. Cline can even use the Model Context Protocol (MCP) to create new tools and extend his own capabilities. While autonomous AI scripts traditionally run in sandboxed environments, this extension provides a human-in-the-loop GUI to approve every file change and terminal command, providing a safe and accessible way to explore the potential of agentic AI.
1. Enter your task and add images to convert mockups into functional apps or fix bugs with screenshots.
2. Cline starts by analyzing your file structure & source code ASTs, running regex searches, and reading relevant files to get up to speed in existing projects. By carefully managing what information is added to context, Cline can provide valuable assistance even for large, complex projects without overwhelming the context window.
@@ -187,4 +187,4 @@ To contribute to the project, start with our [Contributing Guide](CONTRIBUTING.m
## License
[Apache 2.0 © 2024 Cline Bot Inc.](./LICENSE)
[Apache 2.0 © 2025 Cline Bot Inc.](./LICENSE)
+1 -99
View File
@@ -1,99 +1 @@
# Cline Privacy Policy
Cline Bot Inc. ("Cline," "we," "our," and/or "us") values the privacy of individuals who use our VS Code extension and related services (collectively, our "Services"). This privacy policy explains how we collect, use, and disclose information from users of our Services.
## Key Points
- Cline operates entirely client-side as a VS Code extension
- No code or data is collected, stored, or transmitted to Cline's servers
- Your data is only sent to your chosen AI provider (e.g., Anthropic, OpenAI) when you explicitly request assistance
- All processing happens locally on your machine
- API keys are stored securely in VS Code's built-in settings storage
## Information We Process
### A. Information You Provide
- **API Keys**: When you choose to use certain AI model providers (OpenRouter, Anthropic, OpenAI, etc.), you provide API keys. These are stored securely and locally in your VS Code settings.
- **Communications**: If you contact us directly (e.g., via Discord or email), we may receive information like your name, email address, and message contents.
### B. Information Processing
Cline functions solely as a client-side VS Code extension that facilitates communication between your editor and your chosen AI model provider:
1. **File Contents**:
- Only sent to your chosen AI provider when you explicitly request assistance
- Never stored or transmitted to Cline's servers
- Only the specific files/content you select are included
2. **Terminal Commands**:
- Processed entirely locally on your machine
- Require explicit user confirmation before execution
- No command history is transmitted to Cline
3. **Browser Integration**:
- Screenshots and console logs are processed locally
- Temporary data is cleared after task completion
## Data Security
1. **Local-Only Processing**:
- All operations happen on your local machine
- No central servers or data collection
- No telemetry or usage statistics gathered
- No account creation required
2. **API Key Security**:
- Stored using VS Code's secure settings storage system
- Never transmitted to Cline's servers
- You can remove/modify keys at any time
3. **User Control**:
- Explicit approval required for file changes
- Terminal commands require confirmation
- Browser actions need explicit permission
- You control which AI provider to use
## Communication with AI Providers
When you request assistance:
1. Selected content is sent directly to your chosen AI provider
2. No data passes through Cline's servers
3. Provider's own privacy policy applies to this communication:
- [Anthropic Privacy Policy](https://www.anthropic.com/privacy)
- [OpenAI Privacy Policy](https://openai.com/privacy)
- [OpenRouter Privacy Policy](https://openrouter.ai/privacy)
## Error Handling & Debugging
- Error logs are processed locally
- No automatic error reporting to Cline
- You control what information to include when reporting issues
## Children's Privacy
We do not knowingly collect, maintain, or use personal information from children under 18 years of age, and no part of our Service(s) is directed to children. If you learn that a child has provided us with personal information in violation of this Privacy Policy, then you may alert us at support@cline.bot.
## Changes to Privacy Policy
We will post any changes to this policy on our GitHub repository. Significant changes will be announced in our Discord community.
## Security Concerns & Auditing
- Cline is open source and available for security audit
- Our client-side architecture ensures no central point of data collection
- You can inspect exactly what data is being sent to AI providers
- Enterprise users can implement additional access controls through VS Code
## Contact Us
For privacy-related questions or concerns:
- Open an issue on our [GitHub repository](https://github.com/cline/cline)
- Join our [Discord community](https://discord.gg/cline)
- Email: support@cline.bot
See [https://cline.bot/privacy](https://cline.bot/privacy) for our privacy policy.
+1 -1
View File
@@ -28,7 +28,7 @@ Welcome to the Cline documentation - your comprehensive guide to using and exten
- **Interested in contributing?** We welcome your input:
- Feel free to submit a pull request
- [Contribution Guidelines](CONTRIBUTING.md)
- [Contribution Guidelines](../CONTRIBUTING.md)
## Additional Resources
+43
View File
@@ -0,0 +1,43 @@
# Cline Extension Architecture
This directory contains architectural documentation for the Cline VSCode extension.
## Extension Architecture Diagram
The [extension-architecture.mmd](./extension-architecture.mmd) file contains a Mermaid diagram showing the high-level architecture of the Cline extension. The diagram illustrates:
1. **Core Extension**
- Extension entry point and main classes
- State management through VSCode's global state and secrets storage
- Core business logic in the Cline class
2. **Webview UI**
- React-based user interface
- State management through ExtensionStateContext
- Component hierarchy
3. **Storage**
- Task-specific storage for history and state
- Git-based checkpoint system for file changes
4. **Data Flow**
- Core extension data flow between components
- Webview UI data flow
- Bidirectional communication between core and webview
## Viewing the Diagram
To view the diagram:
1. Install a Mermaid diagram viewer extension in VSCode
2. Open extension-architecture.mmd
3. Use the extension's preview feature to render the diagram
You can also view the diagram on GitHub, which has built-in Mermaid rendering support.
## Color Scheme
The diagram uses a high-contrast color scheme for better visibility:
- Pink (#ff0066): Global state and secrets storage components
- Blue (#0066ff): Extension state context
- Green (#00cc66): Cline provider
- All components use white text for maximum readability
@@ -0,0 +1,41 @@
graph TB
subgraph VSCode Extension Host
subgraph Core Extension
ExtensionEntry[Extension Entry<br/>src/extension.ts]
ClineProvider[ClineProvider<br/>src/core/webview/ClineProvider.ts]
ClineClass[Cline Class<br/>src/core/Cline.ts]
GlobalState[VSCode Global State]
SecretsStorage[VSCode Secrets Storage]
end
subgraph Webview UI
WebviewApp[React App<br/>webview-ui/src/App.tsx]
ExtStateContext[ExtensionStateContext<br/>webview-ui/src/context/ExtensionStateContext.tsx]
ReactComponents[React Components]
end
subgraph Storage
TaskStorage[Task Storage<br/>Per-Task Files & History]
CheckpointSystem[Git-based Checkpoints]
end
end
%% Core Extension Data Flow
ExtensionEntry --> ClineProvider
ClineProvider --> ClineClass
ClineClass --> GlobalState
ClineClass --> SecretsStorage
ClineClass --> TaskStorage
ClineClass --> CheckpointSystem
%% Webview Data Flow
WebviewApp --> ExtStateContext
ExtStateContext --> ReactComponents
%% Bidirectional Communication
ClineProvider <-->|postMessage| ExtStateContext
style GlobalState fill:#ff0066,stroke:#333,stroke-width:2px,color:#ffffff
style SecretsStorage fill:#ff0066,stroke:#333,stroke-width:2px,color:#ffffff
style ExtStateContext fill:#0066ff,stroke:#333,stroke-width:2px,color:#ffffff
style ClineProvider fill:#00cc66,stroke:#333,stroke-width:2px,color:#ffffff
+54
View File
@@ -0,0 +1,54 @@
### .clineignore Support
To give you more control over which files are accessible to Cline, we've implemented `.clineignore` functionality, similar to `.gitignore`. This allows you to specify files and directories that Cline should **not** access or process. This is useful for:
* **Privacy:** Preventing Cline from accessing sensitive or private files in your workspace.
* **Performance:** Excluding large directories or files that are irrelevant to your tasks, potentially improving the efficiency of Cline.
* **Context Management:** Focusing Cline's attention on the relevant parts of your project.
**How to use `.clineignore`**
1. **Create a `.clineignore` file:** In the root directory of your workspace (the same level as your `.vscode` folder, or the top level folder you opened in VS Code), create a new file named `.clineignore`.
2. **Define ignore patterns:** Open the `.clineignore` file and specify the patterns for files and directories you want Cline to ignore. The syntax is the same as `.gitignore`:
* Each line in the file represents a pattern.
* **Standard glob patterns are supported:**
* `*` matches zero or more characters
* `?` matches one character
* `[]` matches a character range
* `**` matches any number of directories and subdirectories.
* **Directory patterns:** Append `/` to the end of a pattern to specify a directory.
* **Negation patterns:** Start a pattern with `!` to negate (un-ignore) a previously ignored pattern.
* **Comments:** Start a line with `#` to add comments.
**Example `.clineignore` file:**
```
# Ignore log files
*.log
# Ignore the entire 'node_modules' directory
node_modules/
# Ignore all files in the 'temp' directory and its subdirectories
temp/**
# But DO NOT ignore 'important.log' even if it's in the root
!important.log
# Ignore any file named 'secret.txt' in any subdirectory
**/secret.txt
```
3. **Cline respects your `.clineignore`:** Once you save the `.clineignore` file, Cline will automatically recognize and apply these rules.
* **File Access Control:** Cline will not be able to read the content of ignored files using tools like `read_file`. If you attempt to use a tool on an ignored file, Cline will inform you that access is blocked due to `.clineignore` settings.
* **File Listing:** When you ask Cline to list files in a directory (e.g., using `list_files`), ignored files and directories will still be listed, but they will be marked with a **🔒** symbol next to their name to indicate that they are ignored. This helps you understand which files Cline can and cannot interact with.
4. **Dynamic Updates:** Cline monitors your `.clineignore` file for changes. If you modify, create, or delete your `.clineignore` file, Cline will automatically update its ignore rules without needing to restart VS Code or the extension.
**In Summary**
The `.clineignore` file provides a powerful and flexible way to control Cline's access to your workspace files, enhancing privacy, performance, and context management. By leveraging familiar `.gitignore` syntax, you can easily tailor Cline's focus to the most relevant parts of your projects.
@@ -102,4 +102,4 @@ The **Problems** section in VS Code shows any errors or warnings in your code. Y
## Next Steps
After installing these tools, you'll be ready to start coding! Return to the [Getting Started with Cline for New Coders](getting-started-new-coders.md) guide to continue your journey.
After installing these tools, you'll be ready to start coding! Return to the [Getting Started with Cline for New Coders](../getting-started-new-coders/README.md) guide to continue your journey.
+2 -2
View File
@@ -38,7 +38,7 @@ STOP! Before proceeding, you MUST verify these requirements:
<img src="https://github.com/user-attachments/assets/abf908b1-be98-4894-8dc7-ef3d27943a47" alt="MCP Server Panel" width="400" />
1. The MCP settings files should be display in a tab in VS Code.
1. Replce the file's contents with this code:
1. Replace the file's contents with this code:
For Windows:
@@ -96,7 +96,7 @@ You should witness Cline:
1. Update the mcp setting json file
1. Start the server and start the server
The mcp seetings file should now look like this:
The mcp settings file should now look like this:
_For a Windows machine:_
+21
View File
@@ -128,6 +128,27 @@ Cline's system prompt, on the other hand, is not user-editable ([here's where yo
- Focus on Desired Outcomes: Describe the results you want, not the specific steps.
- Test and Iterate: Experiment to find what works best for your workflow.
### Support for Loading Files from the `.clinerules/` Directory
All files under the `.clinerules/` directory are recursively loaded, and their contents are merged into clineRulesFileInstructions.
#### Example 1:
```
.clinerules/
├── .local-clinerules
└── .project-clinerules
```
#### Example 2:
```
.clinerules/
├── .clinerules-nextjs
├── .clinerules-serverside
└── tests/
├── .pytest-clinerules
└── .jest-clinerules
```
## Prompting Cline 💬
**Prompting is how you communicate your needs for a given task in the back-and-forth chat with Cline.** Cline understands natural language, so write conversationally.
@@ -0,0 +1,153 @@
# Cline's Memory Bank
I am Cline, an expert software engineer with a unique characteristic: my memory resets completely between sessions. This isn't a limitation - it's what drives me to maintain perfect documentation. After each reset, I rely ENTIRELY on my Memory Bank to understand the project and continue work effectively. I MUST read ALL memory bank files at the start of EVERY task - this is not optional.
## Memory Bank Structure
The Memory Bank consists of required core files and optional context files, all in Markdown format. Files build upon each other in a clear hierarchy:
```mermaid
flowchart TD
PB[projectbrief.md] --> PC[productContext.md]
PB --> SP[systemPatterns.md]
PB --> TC[techContext.md]
PC --> AC[activeContext.md]
SP --> AC
TC --> AC
AC --> P[progress.md]
```
### Core Files (Required)
1. `projectbrief.md`
- Foundation document that shapes all other files
- Created at project start if it doesn't exist
- Defines core requirements and goals
- Source of truth for project scope
2. `productContext.md`
- Why this project exists
- Problems it solves
- How it should work
- User experience goals
3. `activeContext.md`
- Current work focus
- Recent changes
- Next steps
- Active decisions and considerations
4. `systemPatterns.md`
- System architecture
- Key technical decisions
- Design patterns in use
- Component relationships
5. `techContext.md`
- Technologies used
- Development setup
- Technical constraints
- Dependencies
6. `progress.md`
- What works
- What's left to build
- Current status
- Known issues
### Additional Context
Create additional files/folders within memory-bank/ when they help organize:
- Complex feature documentation
- Integration specifications
- API documentation
- Testing strategies
- Deployment procedures
## Core Workflows
### Plan Mode
```mermaid
flowchart TD
Start[Start] --> ReadFiles[Read Memory Bank]
ReadFiles --> CheckFiles{Files Complete?}
CheckFiles -->|No| Plan[Create Plan]
Plan --> Document[Document in Chat]
CheckFiles -->|Yes| Verify[Verify Context]
Verify --> Strategy[Develop Strategy]
Strategy --> Present[Present Approach]
```
### Act Mode
```mermaid
flowchart TD
Start[Start] --> Context[Check Memory Bank]
Context --> Update[Update Documentation]
Update --> Rules[Update .clinerules if needed]
Rules --> Execute[Execute Task]
Execute --> Document[Document Changes]
```
## Documentation Updates
Memory Bank updates occur when:
1. Discovering new project patterns
2. After implementing significant changes
3. When user requests with **update memory bank** (MUST review ALL files)
4. When context needs clarification
```mermaid
flowchart TD
Start[Update Process]
subgraph Process
P1[Review ALL Files]
P2[Document Current State]
P3[Clarify Next Steps]
P4[Update .clinerules]
P1 --> P2 --> P3 --> P4
end
Start --> Process
```
Note: When triggered by **update memory bank**, I MUST review every memory bank file, even if some don't require updates. Focus particularly on activeContext.md and progress.md as they track current state.
## Project Intelligence (.clinerules)
The .clinerules file is my learning journal for each project. It captures important patterns, preferences, and project intelligence that help me work more effectively. As I work with you and the project, I'll discover and document key insights that aren't obvious from the code alone.
```mermaid
flowchart TD
Start{Discover New Pattern}
subgraph Learn [Learning Process]
D1[Identify Pattern]
D2[Validate with User]
D3[Document in .clinerules]
end
subgraph Apply [Usage]
A1[Read .clinerules]
A2[Apply Learned Patterns]
A3[Improve Future Work]
end
Start --> Learn
Learn --> Apply
```
### What to Capture
- Critical implementation paths
- User preferences and workflow
- Project-specific patterns
- Known challenges
- Evolution of project decisions
- Tool usage patterns
The format is flexible - focus on capturing valuable insights that help me work more effectively with you and the project. Think of .clinerules as a living document that grows smarter as we work together.
REMEMBER: After every memory reset, I begin completely fresh. The Memory Bank is my only link to previous work. It must be maintained with precision and clarity, as my effectiveness depends entirely on its accuracy.
+1
View File
@@ -52,6 +52,7 @@ const copyWasmFiles = {
"java",
"php",
"swift",
"kotlin",
]
languages.forEach((lang) => {
+47
View File
@@ -0,0 +1,47 @@
# ميثاق المساهمين
## تعهدنا
نحن المساهمون والقائمون على هذا المشروع، نتعهد بتوفير بيئة مفتوحة ومرحبة، ونجعل المشاركة في مشروعنا ومجتمعنا تجربة خالية من التحرش للجميع، بغض النظر عن العمر، أو حجم الجسم، أو الإعاقة، أو العرق، أو الخصائص الجنسية، أو الهوية الجنسية والتعبير عنها، أو مستوى الخبرة، أو التعليم، أو الوضع الاجتماعي والاقتصادي، أو الجنسية، أو المظهر الشخصي، أو الدين، أو الهوية الجنسية والتوجه الجنسي.
## معاييرنا
أمثلة على السلوك الذي يساهم في خلق بيئة إيجابية تشمل:
- استخدام لغة ترحيبية وشاملة
- احترام وجهات النظر والخبرات المختلفة
- تقبل النقد البناء برحابة صدر
- التركيز على ما هو الأفضل للمجتمع
- إظهار التعاطف تجاه أعضاء المجتمع الآخرين
أمثلة على السلوك غير المقبول من قبل المشاركين تشمل:
- استخدام لغة أو صور جنسية والاهتمام الجنسي غير المرغوب فيه أو التحرش الجنسي
- التصيد، والتعليقات المهينة/المسيئة، والهجمات الشخصية أو السياسية
- التحرش العلني أو الخاص
- نشر معلومات الآخرين الخاصة، مثل العنوان الفعلي أو الإلكتروني، دون إذن صريح
- أي سلوك آخر يمكن اعتباره غير لائق في بيئة مهنية
## مسؤولياتنا
يتحمل القائمون على المشروع مسؤولية توضيح معايير السلوك المقبول، ومن المتوقع أن يتخذوا إجراءات تصحيحية مناسبة وعادلة استجابة لأي حالات سلوك غير مقبول.
يحق للقائمين على المشروع إزالة أو تعديل أو رفض التعليقات والالتزامات والتعليمات البرمجية وتعديلات wiki والمشكلات والمساهمات الأخرى التي لا تتماشى مع مدونة قواعد السلوك هذه، أو حظر أي مساهم بشكل مؤقت أو دائم بسبب سلوكيات أخرى يعتبرونها غير لائقة أو مهددة أو مسيئة أو ضارة، كما أنهم يتحملون مسؤولية ذلك.
## النطاق
تنطبق مدونة قواعد السلوك هذه داخل مساحات المشروع وفي الأماكن العامة عندما يمثل الفرد المشروع أو مجتمعه. تتضمن أمثلة تمثيل مشروع أو مجتمع استخدام عنوان بريد إلكتروني رسمي للمشروع، أو النشر عبر حساب رسمي على وسائل التواصل الاجتماعي، أو العمل كممثل معين في حدث عبر الإنترنت أو خارجه. يمكن للقائمين على المشروع تحديد وتوضيح تمثيل المشروع بشكل أكبر.
## التنفيذ
يمكن الإبلاغ عن حالات السلوك المسيء أو التحرش أو السلوك غير المقبول عن طريق الاتصال بفريق المشروع على hi@cline.bot. ستتم مراجعة جميع الشكاوى والتحقيق فيها وستؤدي إلى استجابة تعتبر ضرورية ومناسبة للظروف. يلتزم فريق المشروع بالحفاظ على السرية فيما يتعلق بالمبلغ عن الحادث. يمكن نشر مزيد من التفاصيل حول سياسات التنفيذ المحددة بشكل منفصل.
قد يواجه القائمون على المشروع الذين لا يتبعون أو يفرضون مدونة قواعد السلوك بحسن نية تداعيات مؤقتة أو دائمة على النحو الذي يحدده الأعضاء الآخرون في قيادة المشروع.
## الإسناد
تم اقتباس مدونة قواعد السلوك هذه من [تعهد المساهم][homepage]، الإصدار 1.4، متاح على https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
[homepage]: https://www.contributor-covenant.org
للحصول على إجابات للأسئلة الشائعة حول مدونة قواعد السلوك هذه، راجع https://www.contributor-covenant.org/faq
+93
View File
@@ -0,0 +1,93 @@
# المساهمة في Cline
نحن سعداء لاهتمامك بالمساهمة في Cline. سواء كنت تصلح خطأً أو تضيف ميزة أو تحسن الوثائق لدينا، فإن كل مساهمة تجعل Cline أذكى! للحفاظ على مجتمعنا نابضًا بالحياة وترحيبيًا، يجب على جميع الأعضاء الالتزام بـ [مدونة قواعد السلوك](CODE_OF_CONDUCT.md) لدينا.
## الإبلاغ عن الأخطاء أو المشكلات
تساعد تقارير الأخطاء على جعل Cline أفضل للجميع! قبل إنشاء مشكلة جديدة، يرجى [البحث عن المشكلات الموجودة](https://github.com/cline/cline/issues) لتجنب الازدواجية. عندما تكون جاهزًا للإبلاغ عن خطأ، انتقل إلى [صفحة المشكلات](https://github.com/cline/cline/issues/new/choose) حيث ستجد قالبًا لمساعدتك في ملء المعلومات ذات الصلة.
<blockquote class='warning-note'>
🔐 <b>مهم:</b> إذا اكتشفت ثغرة أمنية، فيرجى استخدام <a href="https://github.com/cline/cline/security/advisories/new">أداة الأمان على Github للإبلاغ عنها بشكل خاص</a>.
</blockquote>
## تحديد ما يجب العمل عليه
تبحث عن مساهمة أولى جيدة؟ تحقق من المشكلات المميزة بـ ["good first issue"](https://github.com/cline/cline/labels/good%20first%20issue) أو ["help wanted"](https://github.com/cline/cline/labels/help%20wanted). تم تحديد هذه المشكلات خصيصًا للمساهمين الجدد والمجالات التي نرحب فيها بالمساعدة!
نرحب أيضًا بالمساهمات في [الوثائق](https://github.com/cline/cline/tree/main/docs) لدينا! سواء كان تصحيح أخطاء إملائية، أو تحسين الأدلة الحالية، أو إنشاء محتوى تعليمي جديد - نود بناء مستودع موارد مدفوع من المجتمع يساعد الجميع على الاستفادة القصوى من Cline. يمكنك البدء بالغوص في `/docs` والبحث عن مجالات تحتاج إلى تحسين.
إذا كنت تخطط للعمل على ميزة أكبر، فيرجى إنشاء [طلب ميزة](https://github.com/cline/cline/discussions/categories/feature-requests?discussions_q=is%3Aopen+category%3A%22Feature+Requests%22+sort%3Atop) أولاً حتى نتمكن من مناقشة ما إذا كان ذلك يتماشى مع رؤية Cline.
## إعداد التطوير
1. **إضافات VS Code**
- عند فتح المشروع، سيطالبك VS Code بتثبيت الإضافات الموصى بها
- هذه الإضافات مطلوبة للتطوير - يرجى قبول جميع مطالبات التثبيت
- إذا تجاهلت المطالبات، يمكنك تثبيتها يدويًا من لوحة الإضافات
2. **التطوير المحلي**
- قم بتشغيل `npm run install:all` لتثبيت التبعيات
- قم بتشغيل `npm run test` لتشغيل الاختبارات محليًا
- قبل تقديم طلب السحب، قم بتشغيل `npm run format:fix` لتنسيق التعليمات البرمجية الخاصة بك
## كتابة وتقديم التعليمات البرمجية
يمكن لأي شخص المساهمة بالتعليمات البرمجية في Cline، لكننا نطلب منك اتباع هذه الإرشادات لضمان دمج مساهماتك بسلاسة:
1. **احتفظ بطلبات السحب مركزة**
- قيد طلبات السحب بميزة واحدة أو إصلاح خطأ
- قسم التغييرات الأكبر إلى طلبات سحب أصغر ومتصلة
- قسم التغييرات إلى التزامات منطقية يمكن مراجعتها بشكل مستقل
2. **جودة التعليمات البرمجية**
- قم بتشغيل `npm run lint` للتحقق من نمط التعليمات البرمجية
- قم بتشغيل `npm run format` لتنسيق التعليمات البرمجية تلقائيًا
- يجب أن تجتاز جميع طلبات السحب عمليات التحقق المستمر التي تشمل كلاً من التنضيد والتنسيق
- تعامل مع أي تحذيرات أو أخطاء ESLint قبل التقديم
- اتبع أفضل ممارسات TypeScript والحفاظ على سلامة النوع
3. **الاختبار**
- أضف اختبارات للميزات الجديدة
- قم بتشغيل `npm test` للتأكد من اجتياز جميع الاختبارات
- قم بتحديث الاختبارات الحالية إذا كانت تغييراتك تؤثر عليها
- تضمين كل من اختبارات الوحدة واختبارات التكامل حيثما كان ذلك مناسبًا
4. **إدارة الإصدار مع Changesets**
- أنشئ changeset لأي تغييرات واجهة المستخدم باستخدام `npm run changeset`
- اختر زيادة الإصدار المناسبة:
- `major` للتغييرات الكبيرة (1.0.0 → 2.0.0)
- `minor` للميزات الجديدة (1.0.0 → 1.1.0)
- `patch` لإصلاحات الأخطاء (1.0.0 → 1.0.1)
- اكتب رسائل changeset واضحة ووصفية تشرح التأثير
- لا تتطلب التغييرات في الوثائق فقط changesets
5. **إرشادات الالتزام (Commit Guidelines)**
- اكتب رسائل التزام واضحة وواصفة
- استخدم تنسيق الالتزام التقليدي (مثل: "feat:", "fix:", "docs:")
- أشر إلى القضايا ذات الصلة في الالتزامات باستخدام #رقم-القضية
6. **قبل الإرسال**
- قم بإعادة دمج فرعك مع أحدث إصدار من الفرع الرئيسي
- تأكد من أن الفرع الخاص بك يُبنى بنجاح
- تحقق من اجتياز جميع الاختبارات
- راجع التغييرات الخاصة بك للتأكد من عدم وجود تعليمات تصحيح الأخطاء أو سجلات وحدة التحكم
7. **وصف طلب السحب (Pull Request Description)**
- صف بوضوح ما تقوم به التغييرات
- قم بتضمين خطوات لاختبار التغييرات
- أدرج أي تغييرات غير متوافقة
- أضف لقطات شاشة للتغييرات في واجهة المستخدم
## اتفاقية المساهمة
من خلال إرسال طلب سحب، فإنك توافق على أن مساهماتك سيتم ترخيصها بنفس ترخيص المشروع ([Apache 2.0](LICENSE)).
تذكر: المساهمة في Cline لا تقتصر فقط على كتابة الكود - إنها تتعلق بأن تكون جزءًا من مجتمع يُشكل مستقبل التطوير بمساعدة الذكاء الاصطناعي. لنبنِ شيئًا رائعًا معًا! 🚀
+189
View File
@@ -0,0 +1,189 @@
<div align="center"><sub>
العربية | <a href="https://github.com/cline/cline/blob/main/locales/es/README.md" target="_blank">الإسبانية</a> | <a href="https://github.com/cline/cline/blob/main/locales/de/README.md" target="_blank">الألمانية</a> | <a href="https://github.com/cline/cline/blob/main/locales/ja/README.md" target="_blank">اليابانية</a> | <a href="https://github.com/cline/cline/blob/main/locales/zh-cn/README.md" target="_blank">الصينية المبسطة</a> | <a href="https://github.com/cline/cline/blob/main/locales/zh-tw/README.md" target="_blank">الصينية التقليدية</a> | <a href="https://github.com/cline/cline/blob/main/locales/pt-BR/README.md" target="_blank">البرتغالية</a>
</sub></div>
# Cline \#1 على OpenRouter
<p align="center">
<img src="https://media.githubusercontent.com/media/cline/cline/main/assets/docs/demo.gif" width="100%" />
</p>
<div align="center">
<table>
<tbody>
<td align="center">
<a href="https://marketplace.visualstudio.com/items?itemName=saoudrizwan.claude-dev" target="_blank"><strong>تنزيل من متجر VS</strong></a>
</td>
<td align="center">
<a href="https://discord.gg/cline" target="_blank"><strong>Discord</strong></a>
</td>
<td align="center">
<a href="https://www.reddit.com/r/cline/" target="_blank"><strong>r/cline</strong></a>
</td>
<td align="center">
<a href="https://github.com/cline/cline/discussions/categories/feature-requests?discussions_q=is%3Aopen+category%3A%22Feature+Requests%22+sort%3Atop" target="_blank"><strong>طلبات الميزات</strong></a>
</td>
<td align="center">
<a href="https://docs.cline.bot/getting-started/getting-started-new-coders" target="_blank"><strong>البدء</strong></a>
</td>
</tbody>
</table>
</div>
التقى Cline، مساعد الذكاء الاصطناعي الذي يمكنه استخدام **سطر الأوامر** و **محرر النصوص** الخاص بك.
بفضل [قدرات Claude 3.7 Sonnet على التعليمات البرمجية الوكيلة](https://www.anthropic.com/claude/sonnet)، يمكن لـ Cline التعامل مع مهام تطوير البرامج المعقدة خطوة بخطوة. مع الأدوات التي تسمح له بإنشاء وتعديل الملفات، واستكشاف المشاريع الكبيرة، واستخدام المتصفح، وتنفيذ أوامر الطرفية (بعد منحك الإذن)، يمكنه مساعدتك بطرق تتجاوز إكمال الكود أو الدعم الفني. يمكن لـ Cline أيضًا استخدام بروتوكول سياق النموذج (MCP) لإنشاء أدوات جديدة وتوسيع قدراته الخاصة. في حين تعمل النصوص البرمجية الآلية المستقلة تقليديًا في بيئات محاصرة، توفر هذه الإضافة واجهة رسومية لموافقة المستخدم على كل تغيير في الملف وأمر طرفية، مما يوفر طريقة آمنة وسهلة الاستخدام لاستكشاف إمكانات الذكاء الاصطناعي الوكيل.
1. أدخل مهمتك وأضف الصور لتحويل المحاكاة إلى تطبيقات وظيفية أو إصلاح الأخطاء مع لقطات الشاشة.
2. يبدأ Cline بتحليل هيكل الملفات الخاصة بك وشجرة التعريف المصدرية، وإجراء عمليات بحث regex، وقراءة الملفات ذات الصلة للاطلاع على المشاريع الحالية. من خلال إدارة المعلومات التي يتم إضافتها إلى السياق بعناية، يمكن لـ Cline تقديم مساعدة قيمة حتى للمشاريع الكبيرة والمعقدة دون إرهاق نافذة السياق.
3. بمجرد حصول Cline على المعلومات التي يحتاجها، يمكنه:
- إنشاء وتعديل الملفات + مراقبة أخطاء Linter/Compiler أثناء السير، مما يسمح له بإصلاح المشكلات مثل الواردات المفقودة وأخطاء البناء النحوي بمفرده.
- تنفيذ الأوامر مباشرة في الطرفية الخاصة بك ومراقبة إخراجها أثناء العمل، مما يسمح له على سبيل المثال بالاستجابة لمشكلات خادم التطوير بعد تعديل ملف.
- بالنسبة لمهام تطوير الويب، يمكن لـ Cline إطلاق الموقع في متصفح بلا رأس، والنقر، وكتابة النص، والتمرير، والتقاط لقطات الشاشة + سجلات وحدة التحكم، مما يسمح له بإصلاح أخطاء وقت التشغيل والأخطاء البصرية.
4. عند اكتمال المهمة، سيقدم Cline النتيجة لك مع أمر طرفية مثل `open -a "Google Chrome" index.html`، والذي تقوم بتشغيله بنقرة زر.
> [!TIP]
> استخدم اختصار `CMD/CTRL + Shift + P` لفتح لوحة الأوامر واكتب "Cline: Open In New Tab" لفتح الإضافة كعلامة تبويب في محرر النصوص الخاص بك. يتيح لك هذا استخدام Cline جنبًا إلى جنب مع مستكشف الملفات الخاص بك، ورؤية كيف يغير مساحة العمل الخاصة بك بوضوح أكبر.
---
<img align="right" width="340" src="https://github.com/user-attachments/assets/3cf21e04-7ce9-4d22-a7b9-ba2c595e88a4">
### استخدم أي واجهة برمجة تطبيقات ونموذج
يدعم Cline مقدمي واجهات برمجة التطبيقات مثل OpenRouter و Anthropic و OpenAI و Google Gemini و AWS Bedrock و Azure و GCP Vertex. يمكنك أيضًا تكوين أي واجهة برمجة تطبيقات متوافقة مع OpenAI، أو استخدام نموذج محلي من خلال LM Studio/Ollama. إذا كنت تستخدم OpenRouter، فستقوم الإضافة بجلب قائمة النماذج الأحدث الخاصة بهم، مما يسمح لك باستخدام أحدث النماذج بمجرد توفرها.
تتتبع الإضافة أيضًا إجمالي الرموز والاستخدام الخاص بواجهة برمجة التطبيقات لدورة المهمة بأكملها وطلبات فردية، مما يبقيك على اطلاع بالإنفاق في كل خطوة.
<!-- Transparent pixel to create line break after floating image -->
<img width="2000" height="0" src="https://github.com/user-attachments/assets/ee14e6f7-20b8-4391-9091-8e8e25561929"><br>
<img align="left" width="370" src="https://github.com/user-attachments/assets/81be79a8-1fdb-4028-9129-5fe055e01e76">
### تشغيل الأوامر في الطرفية
بفضل [تحديثات تكامل الشل الجديدة في VSCode v1.93](https://code.visualstudio.com/updates/v1_93#_terminal-shell-integration-api)، يمكن لـ Cline تنفيذ الأوامر مباشرة في الطرفية الخاصة بك وتلقي الإخراج. يسمح له هذا بأداء مجموعة واسعة من المهام، من تثبيت الحزم وتشغيل سكربتات البناء إلى نشر التطبيقات، وإدارة قواعد البيانات، وتنفيذ الاختبارات، وذلك بالتكيف مع بيئة التطوير الخاصة بك وسلسلة الأدوات للقيام بالعمل على النحو الصحيح.
بالنسبة للعمليات الطويلة المدى مثل خوادم التطوير، استخدم زر "المتابعة أثناء التشغيل" للسماح لـ Cline بالاستمرار في المهمة بينما يعمل الأمر في الخلفية. أثناء عمل Cline، سيتم إخباره بأي إخراج طرفية جديد على الطريق، مما يسمح له بالاستجابة للمشكلات التي قد تنشأ، مثل أخطاء وقت الإنشاء عند تعديل الملفات.
<!-- Transparent pixel to create line break after floating image -->
<img width="2000" height="0" src="https://github.com/user-attachments/assets/ee14e6f7-20b8-4391-9091-8e8e25561929"><br>
<img align="right" width="400" src="https://github.com/user-attachments/assets/c5977833-d9b8-491e-90f9-05f9cd38c588">
### إنشاء وتعديل الملفات
يمكن لـ Cline إنشاء وتعديل الملفات مباشرة في محرر النصوص الخاص بك، وعرض الاختلافات. يمكنك تعديل أو إلغاء تغييرات Cline مباشرة في محرر الاختلافات، أو تقديم ملاحظات في الدردشة حتى تكون راضيًا عن النتيجة. يراقب Cline أيضًا أخطاء Linter/Compiler (الواردات المفقودة، أخطاء البناء النحوي، إلخ) حتى يتمكن من إصلاح المشكلات التي تنشأ أثناء السير بمفرده.
يتم تسجيل جميع التغييرات التي أجراها Cline في جدول زمني للملف، مما يوفر طريقة سهلة لتتبع وإلغاء التعديلات إذا لزم الأمر.
<!-- Transparent pixel to create line break after floating image -->
<img width="2000" height="0" src="https://github.com/user-attachments/assets/ee14e6f7-20b8-4391-9091-8e8e25561929"><br>
<img align="left" width="370" src="https://github.com/user-attachments/assets/bc2e85ba-dfeb-4fe6-9942-7cfc4703cbe5">
### استخدم المتصفح
مع قدرة [استخدام الكمبيوتر](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)
<!-- Transparent pixel to create line break after floating image -->
<img width="2000" height="0" src="https://github.com/user-attachments/assets/ee14e6f7-20b8-4391-9091-8e8e25561929"><br>
<img align="right" width="350" src="https://github.com/user-attachments/assets/ac0efa14-5c1f-4c26-a42d-9d7c56f5fadd">
### "إضافة أداة التي..."
شكراً لـ [بروتوكول سياق النموذج](https://github.com/modelcontextprotocol)، يمكن لـ Cline توسيع قدراته من خلال الأدوات المخصصة. بينما يمكنك استخدام [الخوادم التي أنشأها المجتمع](https://github.com/modelcontextprotocol/servers)، يمكن لـ Cline بدلاً من ذلك إنشاء أدوات وتثبيتها مصممة خصيصًا لتناسب سير عملك. ما عليك سوى أن تطلب من Cline "إضافة أداة"، وسيتولى كل شيء، من إنشاء خادم MCP جديد إلى تثبيته في الامتداد. تصبح هذه الأدوات المخصصة بعد ذلك جزءًا من مجموعة أدوات Cline، جاهزة للاستخدام في المهام المستقبلية.
- **"أضف أداة تجلب تذاكر Jira"**: استرجع تذاكر AC وقم بتشغيل Cline
- **"أضف أداة تدير AWS EC2s"**: تحقق من مقاييس الخادم وقم بتوسيع أو تقليص عدد الحالات
- **"أضف أداة تجلب أحدث حوادث PagerDuty"**: استرجع التفاصيل واطلب من Cline إصلاح الأخطاء
<!-- بكسل شفاف لإنشاء فاصل سطر بعد الصورة العائمة -->
<img width="2000" height="0" src="https://github.com/user-attachments/assets/ee14e6f7-20b8-4391-9091-8e8e25561929"><br>
<img align="left" width="360" src="https://github.com/user-attachments/assets/7fdf41e6-281a-4b4b-ac19-020b838b6970">
### إضافة السياق
**`@url`**: الصق رابط 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>
<img align="right" width="350" src="https://github.com/user-attachments/assets/140c8606-d3bf-41b9-9a1f-4dbf0d4c90cb">
### نقاط التحقق: المقارنة والاستعادة
أثناء عمل Cline على مهمة، يأخذ الامتداد لقطة من بيئة العمل في كل خطوة. يمكنك استخدام زر "Compare" لرؤية الفرق بين اللقطة وبيئة العمل الحالية، وزر "Restore" للعودة إلى تلك النقطة.
على سبيل المثال، عند العمل مع خادم ويب محلي، يمكنك استخدام "استعادة بيئة العمل فقط" لاختبار إصدارات مختلفة من تطبيقك بسرعة، ثم استخدام "استعادة المهمة وبيئة العمل" عندما تجد الإصدار الذي تريد المتابعة منه. يتيح لك ذلك استكشاف أساليب مختلفة بأمان دون فقدان التقدم.
<!-- بكسل شفاف لإنشاء فاصل سطر بعد الصورة العائمة -->
<img width="2000" height="0" src="https://github.com/user-attachments/assets/ee14e6f7-20b8-4391-9091-8e8e25561929"><br>
## المساهمة
للمساهمة في المشروع، ابدأ بـ [دليل المساهمة](CONTRIBUTING.md) لتعلم الأساسيات. يمكنك أيضًا الانضمام إلى [خادم Discord](https://discord.gg/cline) للدردشة مع المساهمين الآخرين في قناة `#contributors`. إذا كنت تبحث عن عمل بدوام كامل، تحقق من الوظائف المتاحة على [صفحة التوظيف](https://cline.bot/join-us)!
<details>
<summary>تعليمات التطوير المحلي</summary>
1. استنساخ المستودع _(يتطلب [git-lfs](https://git-lfs.com/))_:
```bash
git clone https://github.com/cline/cline.git
```
2. افتح المشروع في VSCode:
```bash
code cline
```
3. قم بتثبيت التبعيات اللازمة للامتداد وواجهة الويب:
```bash
npm run install:all
```
4. قم بالتشغيل بالضغط على `F5` (أو من `Run` -> `Start Debugging`) لفتح نافذة VSCode جديدة مع تحميل الامتداد. (قد تحتاج إلى تثبيت [إضافة esbuild problem matchers](https://marketplace.visualstudio.com/items?itemName=connor4312.esbuild-problem-matchers) إذا واجهت مشكلات في بناء المشروع.)
</details>
<details>
<summary>إنشاء طلب سحب (Pull Request)</summary>
1. قبل إنشاء PR، قم بإنشاء إدخال للتغييرات:
```bash
npm run changeset
```
سيطلب منك تحديد:
- نوع التغيير (رئيسي، ثانوي، إصلاح)
- `رئيسي` → تغييرات غير متوافقة (1.0.0 → 2.0.0)
- `ثانوي` → ميزات جديدة (1.0.0 → 1.1.0)
- `إصلاح` → إصلاحات للأخطاء (1.0.0 → 1.0.1)
- وصف التغييرات التي قمت بها
2. قم بحفظ التغييرات وملف `.changeset` الذي تم إنشاؤه
3. ادفع فرعك وأنشئ PR على GitHub. سيقوم CI بـ:
- تشغيل الاختبارات والفحوصات
- سيقوم Changesetbot بإنشاء تعليق يوضح تأثير الإصدار
- عند الدمج مع الفرع الرئيسي، سيقوم Changesetbot بإنشاء PR لحزم الإصدار
- عند دمج PR لحزم الإصدار، سيتم نشر إصدار جديد
</details>
## الرخصة
[Apache 2.0 © 2025 Cline Bot Inc.](./LICENSE)
+2 -2
View File
@@ -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 3.5 Sonnet](https://www-cdn.anthropic.com/fed9cc193a14b84131812372d8d5857f8f304c52/Model_Card_Claude_3_Addendum.pdf) 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.
@@ -158,5 +158,5 @@ Um zum Projekt beizutragen, beginnen Sie mit unserem [Beitragsleitfaden](CONTRIB
## Lizenz
[Apache 2.0 © 2024 Cline Bot Inc.](./LICENSE)
[Apache 2.0 © 2025 Cline Bot Inc.](./LICENSE)
+2 -2
View File
@@ -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 3.5 Sonnet](https://www-cdn.anthropic.com/fed9cc193a14b84131812372d8d5857f8f304c52/Model_Card_Claude_3_Addendum.pdf), 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.
@@ -158,4 +158,4 @@ Para contribuir al proyecto, comience con nuestra [guía de contribución](CONTR
## Licencia
[Apache 2.0 © 2024 Cline Bot Inc.](./LICENSE)
[Apache 2.0 © 2025 Cline Bot Inc.](./LICENSE)
+2 -2
View File
@@ -28,7 +28,7 @@
Clineは、**CLI**と**エディター**を使用できるAIアシスタントです。
[Claude 3.5 Sonnetのエージェント的コーディング機能](https://www-cdn.anthropic.com/fed9cc193a14b84131812372d8d5857f8f304c52/Model_Card_Claude_3_Addendum.pdf)のおかげで、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の分析、正規表現検索の実行、関連ファイルの読み取りから始め、既存プロジェクトに精通します。コンテキストに追加される情報を慎重に管理することで、大規模で複雑なプロジェクトでもコンテキストウィンドウを圧倒することなく貴重な支援を提供できます。
@@ -158,4 +158,4 @@ Clineがタスクを進める中で、拡張機能は各ステップでワーク
## ライセンス
[Apache 2.0 © 2024 Cline Bot Inc.](./LICENSE)
[Apache 2.0 © 2025 Cline Bot Inc.](./LICENSE)
+47
View File
@@ -0,0 +1,47 @@
# 기여자 행동 강령
## 서약
우리는 개방적이고 환영하는 환경을 조성하기 위해 노력하며, 기여자 및 유지 관리자로서 모든 사람이 차별과 괴롭힘 없이 프로젝트와 커뮤니티에 참여할 수 있도록 최선을 다할 것을 서약합니다. 이는 연령, 체형, 장애, 민족성, 성적 특성, 성 정체성 및 표현, 경험 수준, 교육 수준, 사회·경제적 지위, 국적, 외모, 인종, 종교, 성 정체성과 성적 지향에 관계없이 모든 사람에게 적용됩니다.
## 행동 기준
긍정적인 환경을 조성하기 위한 바람직한 행동의 예시:
- 환영하고 포용적인 언어 사용하기
- 서로 다른 관점과 경험을 존중하기
- 건설적인 비판을 우아하게 수용하기
- 커뮤니티에 최선이 되는 것에 집중하기
- 다른 커뮤니티 구성원들에 대한 공감 보여주기
참여자가 해서는 안 되는 행동의 예시:
- 성적인 언어와 이미지 사용, 원치 않는 성적 관심이나 접근
- 트롤링, 모욕적/경멸적인 댓글, 개인적 또는 정치적 공격
- 공개적 또는 사적인 괴롭힘
- 상대방의 동의 없이 개인정보(실제 주소나 전자 주소 등) 공개하기
- 전문적 환경에서 부적절하다고 여겨질 수 있는 기타 행위
## 책임
프로젝트 유지 관리자는 허용 가능한 행동 기준을 명확히 설명할 책임이 있으며, 부적절한 행동이 발생할 경우 적절하고 공정한 시정 조치를 취해야 합니다.
프로젝트 유지 관리자는 본 행동 강령에 부합하지 않는 댓글, 커밋, 코드, 위키 수정, 이슈 및 기타 기여를 삭제, 수정 또는 거부할 권리와 책임이 있으며, 부적절하다고 판단되는 행동(위협적이거나, 공격적이거나, 해로운 행위 등)을 한 기여자를 일시적 또는 영구적으로 차단할 권리를 가집니다.
## 범위
이 행동 강령은 프로젝트 공간과 개인이 프로젝트나 커뮤니티를 대표하는 공개 공간에서 모두 적용됩니다. 프로젝트 또는 커뮤니티를 대표하는 예로는 공식 프로젝트 이메일 주소 사용, 공식 소셜 미디어 계정을 통한 게시, 온라인 또는 오프라인 행사에서 지정된 대표자로 활동하는 경우 등이 포함됩니다. 프로젝트의 대표성은 프로젝트 유지 관리자가 추가로 정의하고 명확히 할 수 있습니다.
## 집행
학대, 괴롭힘 또는 기타 용납할 수 없는 행동은 프로젝트 팀에 hi@cline.bot을 통해 신고 할 수 있습니다. 모든 신고는 검토 및 조사되며, 상황에 따라 필요하고 적절한 조치가 취해질 것입니다. 프로젝트 팀은 사건 신고자의 신원을 보호할 의무가 있습니다. 특정 시행 정책에 대한 추가 세부 사항은 별도로 게시될 수 있습니다.
행동 강령을 성실히 준수하거나 집행하지 않는 프로젝트 유지관리자는 프로젝트 리더십의 구성원에 의해 일시적 또는 영구적인 제재를 받을 수 있습니다.
## 출처
이 행동 강령은 [Contributor Covenant][homepage] 버전 1.4에서 수정되었으며, https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 에서 확인할 수 있습니다.
[homepage]: https://www.contributor-covenant.org
이 행동 강령에 대한 일반적인 질문에 대한 답변은 https://www.contributor-covenant.org/faq 를 참조하시기 바랍니다.
+92
View File
@@ -0,0 +1,92 @@
# Cline에 기여하기
Cline에 기여하는 것에 관심을 가져주셔서 감사합니다! 버그 수정, 기능 추가, 문서 개선 등 모든 기여는 Cline을 더욱 스마트하게 만드는 데 기여합니다. 활기차고 환영하는 커뮤니티를 유지하기 위해 모든 구성원은 [행동 강령](CODE_OF_CONDUCT.md)을 준수해야 합니다.
## 버그와 문제 보고
버그 보고는 Cline을 모두에게 더 나은 것으로 만드는 데 도움이 됩니다! 새로운 이슈를 생성하기 전에, 중복을 피하기 위해 [기존 이슈를 검색](https://github.com/cline/cline/issues)해 주세요. 버그를 보고할 준비가 되었다면, [이슈 페이지](https://github.com/cline/cline/issues/new/choose)로 이동하여 관련 정보를 작성하기 위한 템플릿을 사용해 주세요.
<blockquote class='warning-note'>
🔐 <b>중요:</b> 보안 취약점을 발견한 경우, <a href="https://github.com/cline/cline/security/advisories/new">GitHub 보안 도구를 사용하여 비공개로 보고</a>해 주세요.
</blockquote>
## 작업 내용 결정하기
첫 기여를 찾고 계신가요? ["good first issue"](https://github.com/cline/cline/labels/good%20first%20issue)나 ["help wanted"](https://github.com/cline/cline/labels/help%20wanted) 라벨이 붙은 이슈를 확인해 보세요. 이러한 이슈들은 새로운 기여자를 위해 특별히 선정된 작업으로, 도움이 필요한 영역이 표시되어 있습니다!
또한, [문서](https://github.com/cline/cline/tree/main/docs)에 대한 기여도 환영합니다! 오타 수정, 기존 가이드 개선, 새로운 교육 콘텐츠 작성 등, 커뮤니티 주도의 리소스 저장소를 구축하는 데 여러분의 도움이 필요합니다. `/docs`를 살펴보고 개선이 필요한 부분을 찾아보세요.
큰 기능에 대해 작업할 계획이 있다면, 먼저 [기능 요청](https://github.com/cline/cline/discussions/categories/feature-requests?discussions_q=is%3Aopen+category%3A%22Feature+Requests%22+sort%3Atop)을 생성하여 이것이 Cline의 비전과 부합하는지 논의하는 것이 좋습니다.
## 개발 환경 설정
1. **VS Code 확장 프로그램**
- 프로젝트를 열면 VS Code가 권장 확장 프로그램 설치를 안내합니다
- 개발을 위해 이 확장 프로그램들이 필요하므로, 설치 안내를 수락해 주세요.
- 프롬프트를 닫은 경우 확장 프로그램 패널에서 수동으로 설치할 수 있습니다
2. **로컬 개발**
- `npm run install:all`을 실행하여 의존성을 설치합니다
- `npm run test`를 실행하여 로컬에서 테스트를 실행합니다
- PR을 제출하기 전에 `npm run format:fix`를 실행하여 코드를 포맷팅합니다
## 코드 작성과 제출
누구나 Cline에 코드를 기여할 수 있지만, 기여가 원활하게 통합되도록 다음 가이드라인을 따라주세요:
1. **Pull Request 집중하기**
- PR은 단일 기능 또는 버그 수정으로 제한해 주세요
- 큰 변경사항은 작은 관련 PR로 분할해 주세요
- 논리적으로 독립적인 커밋 단위로 나누어 리뷰가 용이하도록 구성하세요.
2. **코드 품질**
- `npm run lint`를 실행하여 코드 스타일을 체크합니다
- `npm run format`을 실행하여 코드를 자동으로 포맷팅합니다
- 모든 PR은 린팅과 포맷팅을 포함한 CI 체크를 통과해야 합니다
- 제출 전에 ESLint 경고나 에러를 모두 해결해 주세요
- TypeScript 모범 사례를 따르고, 타입 안전성을 유지해 주세요
3. **테스트**
- 새로운 기능에는 테스트를 추가해 주세요
- `npm test`를 실행하여 모든 테스트가 통과하는지 확인해 주세요
- 변경사항이 기존 테스트에 영향을 미치는 경우 해당 테스트를 업데이트해 주세요
- 적절한 경우 단위 테스트와 통합 테스트를 모두 포함해 주세요
4. **Changesets를 활용한 버전 관리**
- 사용자에게 영향을 미치는 변경 사항이 있는 경우, `npm run changeset`을 실행하여 changeset을 생성해 주세요
- 적절한 버전 증가 옵션을 선택하세요:
- `major` 호환되지 않는 변경 (1.0.0 → 2.0.0)
- `minor` 새로운 기능 추가 (1.0.0 → 1.1.0)
- `patch` 버그 수정 (1.0.0 → 1.0.1)
- 영향을 설명하는 명확한 변경사항 메시지를 작성해 주세요
- 문서 변경만 있는 경우 changeset이 필요하지 않습니다
5. **커밋 가이드라인**
- 명확하고 설명적인 커밋 메시지를 작성해 주세요
- 컨벤셔널 커밋 형식(예: "feat:", "fix:", "docs:")을 사용해 주세요
- 커밋에서 관련 이슈를 #issue-number를 사용하여 참조해 주세요
6. **제출 전 확인사항**
- 최신 main에 브랜치를 리베이스해 주세요
- 브랜치가 정상적으로 빌드되는지 확인해 주세요
- 모든 테스트가 통과하는지 다시 확인해 주세요
- 디버그 코드나 콘솔 로그가 없는지 변경사항을 확인해 주세요
7. **Pull Request 설명**
- 변경 내용을 명확하게 설명해 주세요
- 변경사항을 테스트하는 방법을 포함해 주세요
- 호환되지 않는 변경 사항이 있다면 목록으로 작성해주세요
- UI 변경이 있는 경우, 스크린샷을 추가해 주세요
## 기여 동의서
Pull Request를 제출함으로써, 귀하의 기여가 프로젝트와 동일한 라이선스([Apache 2.0](/LICENSE)) 에 따라 제공됨에 동의하는 것입니다.
기억하세요: Cline에 기여하는 것은 코드를 작성하는 것뿐만 아니라, AI 지원 개발의 미래를 형성하는 커뮤니티의 일원이 되는 것입니다. 함께 멋진 것을 만들어봅시다! 🚀
+172
View File
@@ -0,0 +1,172 @@
# Cline - 최고의 OpenRouter
<p align="center">
<img src="https://media.githubusercontent.com/media/cline/cline/main/assets/docs/demo.gif" width="100%" />
</p>
<div align="center">
<table>
<tbody>
<td align="center">
<a href="https://marketplace.visualstudio.com/items?itemName=saoudrizwan.claude-dev" target="_blank"><strong>VS Marketplace에서 다운로드</strong></a>
</td>
<td align="center">
<a href="https://discord.gg/cline" target="_blank"><strong>Discord</strong></a>
</td>
<td align="center">
<a href="https://www.reddit.com/r/cline/" target="_blank"><strong>r/cline</strong></a>
</td>
<td align="center">
<a href="https://github.com/cline/cline/discussions/categories/feature-requests?discussions_q=is%3Aopen+category%3A%22Feature+Requests%22+sort%3Atop" target="_blank"><strong>기능 요청</strong></a>
</td>
<td align="center">
<a href="https://cline.bot/join-us" target="_blank"><strong>채용 정보</strong></a>
</td>
</tbody>
</table>
</div>
Cline을 만나보세요, **CLI** 및 **에디터**를 활용할 수 있는 AI 어시스턴트입니다.
[Claude 3.7 Sonnet의 에이전트형 코딩 기능](https://www.anthropic.com/claude/sonnet) 덕분에, Cline은 복잡한 소프트웨어 개발 작업을 단계별로 처리할 수 있습니다. 파일 생성과 편집, 대규모 프로젝트 탐색, 브라우저 사용, 터미널 명령 실행(권한 허가 필요) 등의 도구를 사용하여 단순 코드 완성이나 기술 지원을 넘어서는 도움을 제공합니다. Cline은 Model Context Protocol(MCP)를 사용하여 새로운 도구를 만들고 자신의 기능을 확장할 수도 있습니다. 자율적인 AI 스크립트는 일반적으로 샌드박스 환경에서 실행되지만, 이 확장 프로그램은 모든 파일 변경 및 터미널 명령을 승인할 수 있는 사람이 개입가능한 GUI를 제공하여, 에이전트형 AI의 잠재력을 보다 안전하고 쉽게 탐색할 수 있도록 합니다.
1. 작업을 입력하고, 목업을 기능하는 앱으로 변환하거나 스크린샷으로 버그를 수정합니다.
2. Cline은 파일 구조와 소스코드 AST의 분석, 정규식 검색 실행, 관련 파일 읽기부터 시작하여 기존 프로젝트를 파악합니다. 또한, 어떤 정보를 컨텍스트에 추가할지를 신중하게 관리하여, 대규모 복잡한 프로젝트에서도 컨텍스트 윈도우를 과부하시키지 않으면서도 효과적인 지원을 제공합니다.
3. Cline이 필요한 정보를 얻은 후 다음과 같은 작업을 할 수 있습니다:
- 파일 생성과 편집 + 린터/컴파일러 오류 모니터링을 수행하여 누락된 임포트나 구문 오류 등의 문제를 자동으로 수정합니다.
- 터미널에서 명령을 직접 실행하고 작업 중에 출력을 모니터링합니다. 이를 통해 파일 편집 후 개발 서버의 문제에 대응할 수 있습니다.
- 웹 개발 작업에서는 헤드리스 브라우저로 사이트를 실행하고, 클릭, 입력, 스크롤, 스크린샷과 콘솔 로그 캡처를 수행하여 런타임 오류나 시각적 버그를 수정합니다.
4. 작업이 완료되면 Cline은 `open -a "Google Chrome" index.html`과 같은 터미널 명령을 제공하여 버튼 클릭 한 번으로 결과를 확인할 수 있도록 합니다.
> [!TIP]
> `CMD/CTRL + Shift + P` 단축키를 사용하여 명령 팔레트를 열고 "Cline: Open In New Tab"을 입력하여 에디터의 탭으로 확장 프로그램을 엽니다. 이를 통해 파일 탐색기와 병행하여 Cline을 사용하고 워크스페이스의 변경을 더 명확하게 확인할 수 있습니다.
---
<img align="right" width="340" src="https://github.com/user-attachments/assets/3cf21e04-7ce9-4d22-a7b9-ba2c595e88a4">
### 어떤 API나 모델이든 사용 가능
Cline은 OpenRouter, Anthropic, OpenAI, Google Gemini, AWS Bedrock, Azure, GCP Vertex 등의 API 제공자를 지원합니다. 또한 OpenAI 호환 API를 설정하거나 LM Studio/Ollama를 통해 로컬 모델을 사용할 수도 있습니다. OpenRouter를 사용하는 경우, 확장 프로그램에서 최신 모델 목록을 가져와 바로 최신 모델을 사용할 수 있게 합니다.
또한, Cline은 전체 작업 루프와 개별 요청별로 토큰 사용량과 API 비용을 추적하여, 진행 중인 작업의 비용을 실시간으로 확인할 수 있도록 도와줍니다.
<img width="2000" height="0" src="https://github.com/user-attachments/assets/ee14e6f7-20b8-4391-9091-8e8e25561929"><br>
<img align="left" width="370" src="https://github.com/user-attachments/assets/81be79a8-1fdb-4028-9129-5fe055e01e76">
### 터미널에서 명령 실행
VSCode v1.93의 새로운 [셸 통합 업데이트](https://code.visualstudio.com/updates/v1_93#_terminal-shell-integration-api) 덕분에, Cline은 터미널에서 명령을 직접 실행하고 출력을 받을 수 있습니다. 이를 통해 패키지 설치나 빌드 스크립트 실행부터 애플리케이션 배포, 데이터베이스 관리, 테스트 실행까지 광범위한 작업을 수행할 수 있습니다. Cline은 개발 환경과 도구 체인에 맞추어 정확하게 작업을 실행합니다.
개발 서버와 같은 오래 실행되는 프로세스의 경우, "실행 중 계속"(Proceed While Running) 버튼을 사용하여 명령이 백그라운드에서 실행되는 동안 Cline이 작업을 계속할 수 있게 합니다. 작업이 진행되는 동안 Cline은 새로운 터미널 출력을 실시간으로 확인하여, 파일 편집 시 발생하는 컴파일 오류와 같은 문제에 즉시 대응할 수 있습니다.
<img width="2000" height="0" src="https://github.com/user-attachments/assets/ee14e6f7-20b8-4391-9091-8e8e25561929"><br>
<img align="right" width="400" src="https://github.com/user-attachments/assets/c5977833-d9b8-491e-90f9-05f9cd38c588">
### 파일 생성과 편집
Cline은 에디터 내에서 파일을 생성 및 편집하고 변경의 Diff 뷰로 표시합니다. Diff 뷰 에디터에서 Cline의 변경을 직접 편집하거나 되돌릴 수 있으며, 채팅에서 피드백을 제공하여 만족할 때까지 개선 요청할 수 있습니다. Cline은 린터/컴파일러 오류(누락된 임포트, 구문 오류 등)도 모니터링하고 발생한 문제를 자동으로 수정합니다.
Cline에 의한 모든 변경은 파일의 타임라인에 기록되어 필요할 때 변경을 추적하고 되돌릴 수 있는 간단한 방법을 제공합니다.
<img width="2000" height="0" src="https://github.com/user-attachments/assets/ee14e6f7-20b8-4391-9091-8e8e25561929"><br>
<img align="left" width="370" src="https://github.com/user-attachments/assets/bc2e85ba-dfeb-4fe6-9942-7cfc4703cbe5">
### 브라우저 사용
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)
<img width="2000" height="0" src="https://github.com/user-attachments/assets/ee14e6f7-20b8-4391-9091-8e8e25561929"><br>
<img align="right" width="350" src="https://github.com/user-attachments/assets/ac0efa14-5c1f-4c26-a42d-9d7c56f5fadd">
### "도구를 추가 해주세요."
Cline은 [Model Context Protocol](https://github.com/modelcontextprotocol)을 활용하여 커스텀 도구를 생성하고 기능을 확장할 수 있습니다. 기존의 [커뮤니티 서버](https://github.com/modelcontextprotocol/servers)를 사용할 수도 있지만, Cline은 사용자의 워크플로우에 최적화된 도구를 직접 제작하고 설치할 수도 있습니다. "~ 도구를 추가해주세요."라고 요청만 하면, Cline은 새로운 MCP 서버 생성부터 확장 프로그램 내 설치까지 모두 자동으로 처리합니다. 이러한 커스텀 도구는 Cline의 툴키트의 일부가 되어 향후 작업에서 사용할 수 있게 됩니다.
- "Jira 티켓을 가져오는 도구를 추가해주세요": 티켓 AC를 가져와 Cline에게 작업을 요청
- "AWS EC2를 관리하는 도구를 추가해주세요": 서버 메트릭을 확인하고 인스턴스를 확장 또는 축소
- "최신 PagerDuty 인시던트를 가져오는 도구를 추가해주세요": 최신 장애 정보를 가져와 Cline에게 버그 수정 요청
<img width="2000" height="0" src="https://github.com/user-attachments/assets/ee14e6f7-20b8-4391-9091-8e8e25561929"><br>
<img align="left" width="360" src="https://github.com/user-attachments/assets/7fdf41e6-281a-4b4b-ac19-020b838b6970">
### 컨텍스트 추가
**`@url`:** URL을 붙여넣으면 확장이 해당 페이지를 가져와 Markdown으로 변환합니다. 최신 문서를 Cline에게 제공할 때 유용합니다.
**`@problems`:** Cline이 수정할 워크스페이스 오류와 경고(Problems' panel)를 추가합니다.
**`@file`:** 파일의 내용을 추가하여, 파일을 읽는 데 API 요청을 허비하지 않고도 Cline이 접근할 수 있도록 합니다. (+ 파일 검색 가능)
**`@folder`:** 폴더 내 모든 파일을 한 번에 추가하여 워크플로우를 더욱 빠르게 진행할 수 있습니다.
<img width="2000" height="0" src="https://github.com/user-attachments/assets/ee14e6f7-20b8-4391-9091-8e8e25561929"><br>
<img align="right" width="350" src="https://github.com/user-attachments/assets/140c8606-d3bf-41b9-9a1f-4dbf0d4c90cb">
### 체크포인트: 비교 및 복원
Cline이 작업을 진행하는 동안 확장 프로그램은 각 단계에서 워크스페이스의 스냅샷을 저장합니다. “Compare” 버튼을 사용하여 스냅샷과 현재 워크스페이스의 차이를 확인하고, “Restore” 버튼을 사용하여 해당 시점으로 롤백할 수 있습니다.
예를 들어, 로컬 웹 서버에서 작업 중일 때 “Restore Workspace Only”을 사용하여 서로 다른 버전의 앱을 신속하게 테스트하고, “Restore Task and Workspace”을 사용하여 계속 진행할 버전을 찾을 수 있습니다. 이를 통해 진행 상황을 잃지 않고 안전하게 다양한 접근 방식을 실험할 수 있습니다.
<img width="2000" height="0" src="https://github.com/user-attachments/assets/ee14e6f7-20b8-4391-9091-8e8e25561929"><br>
## 기여
프로젝트에 기여하려면, [기여 가이드](CONTRIBUTING.md)에서 기본 사항을 익히세요. 또한, [Discord](https://discord.gg/cline)에 참여하여 `#contributors` 채널에서 다른 기여자들과 이야기할 수 있습니다. 풀타임 직업을 찾고 있다면, [채용 페이지](https://cline.bot/join-us)에서 열려있는 포지션을 확인하세요.
<details>
<summary>로컬 개발 방법</summary>
1. 리포지토리를 클론합니다 _(Requires [git-lfs](https://git-lfs.com/))_
```bash
git clone https://github.com/cline/cline.git
```
2. 프로젝트를 VSCode에서 엽니다:
```bash
code cline
```
3. 확장 프로그램과 webview-gui의 필요한 의존성을 설치합니다:
```bash
npm run install:all
```
4. `F5`를 눌러(또는 `Run`->`Start Debugging`), 확장 프로그램이 로드된 새로운 VSCode 창을 엽니다. (프로젝트 빌드에 문제가 있는 경우, [esbuild problem matchers extension](https://marketplace.visualstudio.com/items?itemName=connor4312.esbuild-problem-matchers)을 설치해야 할 수도 있습니다.)
</details>
<details>
<summary>Pull Request 생성 방법</summary>
1. PR을 만들기 전, 변경 사항을 기록하는 changeset 항목을 생성:
```bash
npm run changeset
```
이후 프롬프트에서 다음 정보를 입력하세요:
- 변경 유형 (major, minor, patch)
- `major` → 호환되지 않는 변경 (1.0.0 → 2.0.0)
- `minor` → 새로운 기능 추가 (1.0.0 → 1.1.0)
- `patch` → 버그 수정 (1.0.0 → 1.0.1)
- 변경 사항 설명 입력
2. 변경 사항과 생성된 `.changeset` 파일을 커밋 후 브랜치를 푸시하고 GitHub에서 PR을 생성하세요.
3. 브랜치를 푸시하고 GitHub에서 PR을 생성하세요. CI가 다음과 같은 작업을 수행합니다:
- 테스트 및 코드 검증 실행
- Changesetbot이 버전 변경 영향을 보여주는 코멘트를 생성
- 브랜치가 메인에 머지되면, Changesetbot이 버전 패키지 PR을 생성
- 버전 패키지 PR이 머지되면, 새로운 릴리즈가 게시됨
</details>
## 라이센스
[Apache 2.0 © 2025 Cline Bot Inc.](/LICENSE)
+51
View File
@@ -0,0 +1,51 @@
# Código de Conduta para Contribuidores
## Nosso Compromisso
Com o objetivo de promover um ambiente aberto e acolhedor, nós, como contribuidores e mantenedores, nos comprometemos a tornar a participação em nosso projeto e comunidade uma experiência livre de assédio para todos, independentemente de idade, tamanho corporal, deficiência, etnia, características sexuais, identidade e expressão de gênero, nível de experiência, educação, status socioeconômico, nacionalidade, aparência pessoal, raça, religião ou orientação sexual.
## Nossos Padrões
Exemplos de comportamentos que contribuem para criar um ambiente positivo incluem:
- Uso de linguagem acolhedora e inclusiva
- Respeito por diferentes pontos de vista e experiências
- Aceitar críticas de maneira construtiva
- Foco no que é melhor para a comunidade
- Ser empático com outros membros da comunidade
Exemplos de comportamentos inaceitáveis por parte dos participantes incluem:
- Uso de linguagem ou imagens sexualizadas e atenção ou avanços sexuais indesejados
- Trollar, insultar, fazer comentários depreciativos, ataques pessoais ou políticos
- Assédio público ou privado
- Divulgar informações privadas sem autorização, como endereços físicos ou eletrônicos, sem permissão explícita
- Outras condutas que poderiam ser consideradas inadequadas em um ambiente profissional
## Nossas Responsabilidades
Os mantenedores do projeto são responsáveis por esclarecer os padrões de comportamento aceitáveis e devem tomar ações corretivas apropriadas e justas em resposta a qualquer instância de comportamento inaceitável.
Os mantenedores têm o direito e a responsabilidade de remover, editar ou rejeitar comentários, commits, códigos, edições no wiki, issues e outras contribuições que não estejam alinhadas com este Código de Conduta. Também podem banir temporária ou permanentemente qualquer colaborador cujo comportamento seja considerado inapropriado, ameaçador, ofensivo ou prejudicial.
## Escopo
Este Código de Conduta se aplica tanto aos espaços do projeto quanto aos espaços públicos
quando uma pessoa representa o projeto ou sua comunidade. Exemplos de
representação de um projeto ou comunidade incluem o uso de um endereço de e-mail oficial do projeto,
publicar em uma conta oficial de mídia social ou atuar como representante designado
em um evento online ou offline. A representação de um projeto pode
ser mais especificamente definido e esclarecido pelos mantenedores do projeto.
## Aplicação
Casos de comportamento abusivo, assediador ou inaceitáveis podem ser reportados entrando em contato com a equipe do projeto pelo email hi@cline.bot. Todas as queixas serão revisadas e investigadas confidencialmente. Mais detalhes sobre políticas específicas podem ser publicados separadamente.
Os mantenedores que não seguirem ou aplicarem este Código de Conduta de boa fé podem enfrentar repercussões temporárias ou permanentes determinadas por outros membros da liderança do projeto.
## Atribuição
Este Código de Conduta é adaptado do [Contributor Covenant](https://www.contributor-covenant.org), versão 1.4, disponível em https://www.contributor-covenant.org/version/1/4/code-of-conduct.html.
+83
View File
@@ -0,0 +1,83 @@
# Contribuir para o Cline
Estamos felizes por você estar interessado em contribuir com o Cline. Seja corrigindo um erro, adicionando uma funcionalidade ou melhorando nossa documentação, cada contribuição torna o Cline mais inteligente! Para manter nossa comunidade viva e acolhedora, todos os membros devem cumprir nosso Código de Conduta [Código de Conduta](CODE_OF_CONDUCT.md).
## Relatar erros ou problemas
Relatar erros ajuda a melhorar o Cline para todos! Antes de criar um novo issue, revise as [issues existentes](https://github.com/cline/cline/issues) para evitar duplicações. Quando estiver pronto para relatar um erro, vá até nossa [página de Issues](https://github.com/cline/cline/issues/new/choose), onde você encontrará um modelo que ajudará a preencher as informações relevantes.
<blockquote class='warning-note'>
🔐 <b>Importante:</b> Se você descobrir uma vulnerabilidade de segurança, utilize a <a href="https://github.com/cline/cline/security/advisories/new">ferramenta de segurança do GitHub</a> para relatá-la de forma privada.
</blockquote>
## Escolher no que trabalhar
Procurando uma boa primeira contribuição? Consulte os problemas marcados com ["good first issue"](https://github.com/cline/cline/labels/good%20first%20issue) ou ["help wanted"](https://github.com/cline/cline/labels/help%20wanted). Estes foram especialmente selecionados para novos colaboradores e são áreas em que adoraríamos receber ajuda!
Também damos boas-vindas a contribuições para nossa [documentação](https://github.com/cline/cline/tree/main/docs). Seja corrigindo erros de digitação, melhorando guias existentes ou criando novos conteúdos educativos, queremos construir um repositório de recursos gerido pela comunidade que ajude todos a tirar o máximo proveito do Cline. Você pode começar explorando `/docs` e procurando áreas que precisam de melhorias.
Se planeja trabalhar em uma funcionalidade maior, crie primeiro uma [solicitação de funcionalidade](https://github.com/cline/cline/discussions/categories/feature-requests?discussions_q=is%3Aopen+category%3A%22Feature+Requests%22+sort%3Atop) para que possamos discutir se ela se alinha à visão do Cline.
## Configurar o ambiente de desenvolvimento
1. **Extensões do VS Code**
- Ao abrir o projeto, o VS Code solicitará que você instale as extensões recomendadas.
- Essas extensões são necessárias para o desenvolvimento aceite todas as solicitações de instalação.
- Caso tenha rejeitado as solicitações, você pode instalá-las manualmente na seção de extensões.
2. **Desenvolvimento local**
- Execute `npm run install:all` para instalar as dependências.
- Execute `npm run test` para rodar os testes localmente.
- Antes de enviar um PR, execute `npm run format:fix` para formatar seu código.
## Escrever e enviar código
Qualquer pessoa pode contribuir com código para o Cline, mas pedimos que siga estas diretrizes para garantir que suas contribuições sejam integradas sem problemas:
1. **Mantenha os Pull Requests focados**
- Limite os PRs a uma única funcionalidade ou correção de erro.
- Divida alterações maiores em PRs menores e coerentes.
- Divida as alterações em commits lógicos que possam ser revisados independentemente.
2. **Qualidade do código**
- Execute `npm run lint` para verificar o estilo do código.
- Execute `npm run format` para formatar automaticamente o código.
- Todos os PRs devem passar nas verificações do CI, que incluem linting e formatação.
- Resolva todos os avisos ou erros do ESLint antes de enviar.
- Siga as melhores práticas para TypeScript e mantenha a segurança dos tipos.
3. **Testes**
- Adicione testes para novas funcionalidades.
- Execute `npm test` para garantir que todos os testes passem.
- Atualize testes existentes caso suas alterações os afetem.
- Inclua tanto testes unitários quanto de integração onde for apropriado.
4. **Diretrizes de commits**
- Escreva mensagens de commit claras e descritivas.
- Use o formato convencional (por exemplo, "feat:", "fix:", "docs:").
- Faça referência aos issues relevantes nos commits usando #número-do-issue.
5. **Antes de enviar**
- Faça rebase com sua branch com a última versão da branch principal (main).
- Certifique-se de que sua branch seja construída corretamente.
- Verifique se todos os testes passam.
- Revise suas alterações para remover qualquer código de depuração ou logs desnecessários.
6. **Descrição do Pull Request**
- Descreva claramente o que suas alterações fazem.
- Inclua passos para testar as alterações.
- Liste quaisquer mudanças importantes.
- Adicione capturas de tela para mudanças na interface do usuário.
## Acordo de contribuição
Ao enviar um Pull Request, você concorda que suas contribuições serão licenciadas sob a mesma licença do projeto ([Apache 2.0](LICENSE)).
Lembre-se: Contribuir com o Cline não é apenas escrever código é fazer parte de uma comunidade que está moldando o futuro do desenvolvimento assistido por IA. Vamos criar algo incrível juntos! 🚀
+161
View File
@@ -0,0 +1,161 @@
# Cline #1 no OpenRouter
<p align="center">
<img src="https://media.githubusercontent.com/media/cline/cline/main/assets/docs/demo.gif" width="100%" />
</p>
<div align="center">
<table>
<tbody>
<td align="center">
<a href="https://marketplace.visualstudio.com/items?itemName=saoudrizwan.claude-dev" target="_blank"><strong>Baixar no VS Marketplace</strong></a>
</td>
<td align="center">
<a href="https://discord.gg/cline" target="_blank"><strong>Discord</strong></a>
</td>
<td align="center">
<a href="https://www.reddit.com/r/cline/" target="_blank"><strong>r/cline</strong></a>
</td>
<td align="center">
<a href="https://github.com/cline/cline/discussions/categories/feature-requests?discussions_q=is%3Aopen+category%3A%22Feature+Requests%22+sort%3Atop" target="_blank"><strong>Solicitação de Funcionalidades</strong></a>
</td>
<td align="center">
<a href="https://cline.bot/join-us" target="_blank"><strong>Estamos Contratando!</strong></a>
</td>
</tbody>
</table>
</div>
Conheça o Cline: um assistente de IA que pode usar seu **CLI** e **Editor**.
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.
2. O Cline começará analisando a estrutura do seu arquivo e os ASTs do código-fonte, fazendo pesquisas com Regex e lendo arquivos relevantes para se orientar em projetos existentes. Ao gerenciar cuidadosamente as informações agregadas, o Cline pode fornecer assistência valiosa mesmo em projetos grandes e complexos, sem sobrecarregar a janela de contexto.
3. Assim que ele tiver as informações necessárias, o Cline poderá:
- Criar e editar arquivos + monitorar erros de Linter/Compilador, para que você possa corrigir proativamente problemas como importações ausentes e erros de sintaxe.
- Executar comandos diretamente no terminal e monitorar o resultado, para que você possa responder a problemas do servidor de desenvolvimento após editar um arquivo.
- Para tarefas de desenvolvimento web, o Cline pode iniciar o site em um navegador headless, clicar, digitar, fazer scroll e capturar capturas de tela + registros de console, para que você possa corrigir erros em tempo de execução e erros visuais.
> [!TIP]
> Use o atalho de teclado `CMD/CTRL + Shift + P` para abrir a lista de comandos possiveis e digite "Cline: Abrir em nova aba" para abrir a extensão como uma aba no seu editor. Dessa forma, você pode usar o Cline junto com seu explorador de arquivos e ver mais claramente como seu espaço de trabalho muda.
---
<img align="right" width="340" src="https://github.com/user-attachments/assets/3cf21e04-7ce9-4d22-a7b9-ba2c595e88a4">
### Use qualquer API ou modelo
O Cline oferece suporte a provedores de API como OpenRouter, Anthropic, OpenAI, Google Gemini, AWS Bedrock, Azure e GCP Vertex. Você também pode configurar qualquer API compatível com OpenAI ou usar um modelo local via LM Studio/Ollama. Se você usar o OpenRouter, a extensão recuperará sua lista de modelos mais recentes, para que você possa usar os modelos mais novos assim que estiverem disponíveis.
A extensão também rastreia o uso total de tokens e os custos da API para todo o ciclo de tarefas e solicitações individuais, para que você seja informado sobre as despesas em cada etapa.
<!-- Pixel transparente para criar uma quebra de linha após a imagem flutuante -->
<img width="2000" height="0" src="https://github.com/user-attachments/assets/ee14e6f7-20b8-4391-9091-8e8e25561929"><br>
<img align="left" width="370" src="https://github.com/user-attachments/assets/81be79a8-1fdb-4028-9129-5fe055e01e76">
### Executar comandos no terminal
Graças às novas [atualizações de integração do Shell no VSCode v1.93](https://code.visualstudio.com/updates/v1_93#_terminal-shell-integration-api), o Cline pode executar comandos diretamente no seu terminal e receber o resultado. Isso permite que você execute uma variedade de tarefas, desde instalar pacotes e executar build scripts para fazer deploy de aplicações, gerenciar bancos de dados e executar testes, adaptando-se ao seu ambiente de desenvolvimento e ferramentas para fazer o trabalho corretamente.
Para processos de longa duração, como servidores de desenvolvimento, use o botão "Continuar durante a execução" para permitir que o Cline continue a tarefa enquanto o comando é executado em segundo plano. Enquanto Cline trabalha, você será notificado sobre novas saídas do terminal, para que possa responder a problemas que possam surgir, como erros de compilação ao editar arquivos.
<!-- Pixel transparente para criar uma quebra de linha após a imagem flutuante -->
<img width="2000" height="0" src="https://github.com/user-attachments/assets/ee14e6f7-20b8-4391-9091-8e8e25561929"><br>
<img align="right" width="400" src="https://github.com/user-attachments/assets/c5977833-d9b8-491e-90f9-05f9cd38c588">
### Criar e editar arquivos
Cline pode criar e editar arquivos diretamente no seu editor, apresentando um diff com as alterações. Você pode editar ou reverter as alterações do Cline diretamente no editor de diff ou fornecer feedback no chat até ficar satisfeito com o resultado. Cline também monitora erros de linter/compilador (importações ausentes, erros de sintaxe, etc.) para que possa corrigir problemas que surgem ao longo do caminho por conta própria.
Todas as alterações feitas pelo Cline são registradas na Linha do tempo do arquivo, fornecendo uma maneira fácil de rastrear e reverter modificações, caso seja necessário.
<!-- Pixel transparente para criar uma quebra de linha após a imagem flutuante -->
<img width="2000" height="0" src="https://github.com/user-attachments/assets/ee14e6f7-20b8-4391-9091-8e8e25561929"><br>
<img align="left" width="370" src="https://github.com/user-attachments/assets/bc2e85ba-dfeb-4fe6-9942-7cfc4703cbe5">
### 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 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)
<!-- Pixel transparente para crear un salto de línea después de la imagen flotante -->
<img width="2000" height="0" src="https://github.com/user-attachments/assets/ee14e6f7-20b8-4391-9091-8e8e25561929"><br>
<img align="right" width="350" src="https://github.com/user-attachments/assets/ac0efa14-5c1f-4c26-a42d-9d7c56f5fadd">
### "adicione uma ferramenta que..."
Graças ao [Model Context Protocol](https://github.com/modelcontextprotocol), o Cline pode expandir seus recursos por meio de ferramentas personalizadas. Embora você possa usar [servidores criados pela comunidade](https://github.com/modelcontextprotocol/servers), Cline pode criar e instalar ferramentas especificamente para seu fluxo de trabalho. Basta pedir ao Cline para "adicionar uma ferramenta" e ele cuidará de tudo, desde a criação de um novo servidor MCP até a instalação na extensão. Essas ferramentas personalizadas se tornam parte do conjunto de ferramentas da Cline e estão prontas para serem usadas em tarefas futuras.
- "adicione uma ferramenta que recupere tickets do Jira": Recupere ACs de tickets e coloque Cline para trabalhar
- "adicione uma ferramenta que gerencie AWS EC2s": verifique as métricas do servidor e aumente ou diminua as instâncias
- "adicione uma ferramenta para recuperar os últimos incidentes do PagerDuty": Recupere detalhes e peça ao Cline para corrigir erros
<!-- Pixel transparente para crear un salto de línea después de la imagen flotante -->
<img width="2000" height="0" src="https://github.com/user-attachments/assets/ee14e6f7-20b8-4391-9091-8e8e25561929"><br>
<img align="left" width="360" src="https://github.com/user-attachments/assets/7fdf41e6-281a-4b4b-ac19-020b838b6970">
### Adicione contexto
**`@url`:** Insira uma URL para a extensão recuperar e converter para Markdown, que é útil quando você deseja fornecer ao Cline documentos mais recentes
**`@problems`:** Adicionar erros e avisos do espaço de trabalho (painel 'Problemas') que o Cline deve corrigir
**`@file`:** Adicione o conteúdo de um arquivo para que você não precise desperdiçar solicitações de API para aprovar a leitura do arquivo (+ para pesquisar arquivos)
**`@folder`:** Adicione arquivos de uma pasta por vez para acelerar ainda mais seu fluxo de trabalho
<!-- Pixel transparente para criar uma quebra de linha após a imagem flutuante -->
<img width="2000" height="0" src="https://github.com/user-attachments/assets/ee14e6f7-20b8-4391-9091-8e8e25561929"><br>
<img align="right" width="350" src="https://github.com/user-attachments/assets/140c8606-d3bf-41b9-9a1f-4dbf0d4c90cb">
### Checkpoints: Comparar e Restaurar
Enquanto Cline trabalha em uma tarefa, a extensão cria um instantâneo de seu espaço de trabalho em cada etapa. Você pode usar o botão "Comparar" para ver a diferença entre o instantâneo e seu espaço de trabalho atual, e o botão "Restaurar" para retornar a esse ponto.
Por exemplo, se estiver trabalhando com um servidor web local, você pode usar 'Restaurar somente o espaço de trabalho' para testar rapidamente diferentes versões do seu aplicativo e, em seguida, 'Restaurar tarefa e espaço de trabalho' quando encontrar a versão na qual deseja continuar trabalhando. Isso permite que você explore diferentes abordagens com segurança sem perder o progresso.
<!-- Pixel transparente para crear un salto de línea después de la imagen flotante -->
<img width="2000" height="0" src="https://github.com/user-attachments/assets/ee14e6f7-20b8-4391-9091-8e8e25561929"><br>
## Contribuições
Para contribuir com o projeto, comece com nosso [Guia de Contribuição](CONTRIBUTING.md) para aprender o básico. Você também pode entrar no nosso [Discord](https://discord.gg/cline) para bater papo com outros colaboradores no canal `#contributors`. Se você está procurando um emprego de período integral, confira nossas vagas em aberto na nossa [página de carreiras](https://cline.bot/join-us).
<details>
<summary>Instruções para desenvolvimento local</summary>
1. Clone o repositório _(Necessário [git-lfs](https://git-lfs.com/))_:
```bash
git clone https://github.com/cline/cline.git
```
2. Abra o projeto no VSCode:
```bash
code cline
```
3. Instale as dependências necessárias para a extensão e webview-gui:
```bash
npm run install:all
```
4. Inicie pressionando `F5` (ou `Executar`->`Iniciar Depuração`) para abrir uma nova janela do VSCode com a extensão carregada. (Pode ser necessário instalar a [extensão esbuild problem matchers](https://marketplace.visualstudio.com/items?itemName=connor4312.esbuild-problem-matchers) se você encontrar problemas ao compilar seu projeto.)
</details>
## Licença
[Apache 2.0 © 2025 Cline Bot Inc.](./LICENSE)
+2 -2
View File
@@ -28,7 +28,7 @@
认识 Cline,一个可以使用你的 **CLI****编辑器** 的 AI 助手。
感谢 [Claude 3.5 Sonnet 的代理编码能力](https://www-cdn.anthropic.com/fed9cc193a14b84131812372d8d5857f8f304c52/Model_Card_Claude_3_Addendum.pdf),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,运行正则表达式搜索,并阅读相关文件以了解现有项目。通过仔细管理添加到上下文中的信息,Cline 即使在大型复杂项目中也能提供有价值的帮助,而不会使上下文窗口过载。
@@ -158,5 +158,5 @@ Cline 所做的所有更改都会记录在你的文件时间轴中,提供了
## 许可证
[Apache 2.0 © 2024 Cline Bot Inc.](./LICENSE)
[Apache 2.0 © 2025 Cline Bot Inc.](./LICENSE)
+2 -2
View File
@@ -28,7 +28,7 @@
認識 Cline,一個可以使用你的 **CLI****編輯器** 的 AI 助手。
感謝 [Claude 3.5 Sonnet 的代理編碼能力](https://www-cdn.anthropic.com/fed9cc193a14b84131812372d8d5857f8f304c52/Model_Card_Claude_3_Addendum.pdf),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,運行正則表達式搜索,並閱讀相關文件以了解現有項目。通過仔細管理添加到上下文中的信息,Cline 即使在大型複雜項目中也能提供有價值的幫助,而不會使上下文窗口過載。
@@ -158,4 +158,4 @@ Cline 所做的所有更改都會記錄在你的文件時間軸中,提供了
## 許可證
[Apache 2.0 © 2024 Cline Bot Inc.](./LICENSE)
[Apache 2.0 © 2025 Cline Bot Inc.](./LICENSE)
+4448 -290
View File
File diff suppressed because it is too large Load Diff
+135 -20
View File
@@ -2,12 +2,8 @@
"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.2.8",
"version": "3.8.2",
"icon": "assets/icons/icon.png",
"galleryBanner": {
"color": "#617A91",
"theme": "dark"
},
"engines": {
"vscode": "^1.84.0"
},
@@ -85,6 +81,11 @@
"title": "Open in Editor",
"icon": "$(link-external)"
},
{
"command": "cline.accountButtonClicked",
"title": "Account",
"icon": "$(account)"
},
{
"command": "cline.settingsButtonClicked",
"title": "Settings",
@@ -94,6 +95,32 @@
"command": "cline.openInNewTab",
"title": "Open In New Tab",
"category": "Cline"
},
{
"command": "cline.dev.createTestTasks",
"title": "Create Test Tasks",
"category": "Cline",
"when": "cline.isDevMode"
},
{
"command": "cline.openDocumentation",
"title": "Documentation",
"icon": "$(book)"
},
{
"command": "cline.addToChat",
"title": "Add to Cline",
"category": "Cline"
},
{
"command": "cline.addTerminalOutputToChat",
"title": "Add to Cline",
"category": "Cline"
},
{
"command": "cline.fixWithCline",
"title": "Fix with Cline",
"category": "Cline"
}
],
"menus": {
@@ -119,9 +146,32 @@
"when": "view == claude-dev.SidebarProvider"
},
{
"command": "cline.settingsButtonClicked",
"command": "cline.openDocumentation",
"group": "navigation@5",
"when": "view == claude-dev.SidebarProvider"
},
{
"command": "cline.accountButtonClicked",
"group": "navigation@6",
"when": "view == claude-dev.SidebarProvider"
},
{
"command": "cline.settingsButtonClicked",
"group": "navigation@7",
"when": "view == claude-dev.SidebarProvider"
}
],
"editor/context": [
{
"command": "cline.addToChat",
"group": "navigation",
"when": "editorHasSelection"
}
],
"terminal/context": [
{
"command": "cline.addTerminalOutputToChat",
"group": "navigation"
}
]
},
@@ -160,7 +210,57 @@
"cline.enableCheckpoints": {
"type": "boolean",
"default": true,
"description": "Enables extension to save checkpoints of workspace throughout the task."
"description": "Enables extension to save checkpoints of workspace throughout the task. Uses git under the hood which may not work well with large workspaces."
},
"cline.disableBrowserTool": {
"type": "boolean",
"default": false,
"description": "Disables extension from spawning browser session."
},
"cline.modelSettings.o3Mini.reasoningEffort": {
"type": "string",
"enum": [
"low",
"medium",
"high"
],
"default": "medium",
"description": "Controls the reasoning effort when using the o3-mini model. Higher values may result in more thorough but slower responses."
},
"cline.chromeExecutablePath": {
"type": "string",
"default": null,
"description": "Path to Chrome executable for browser use functionality. If not set, the extension will attempt to find or download it automatically."
},
"cline.preferredLanguage": {
"type": "string",
"enum": [
"English",
"Arabic - العربية",
"Portuguese - Português (Brasil)",
"Czech - Čeština",
"French - Français",
"German - Deutsch",
"Hindi - हिन्दी",
"Hungarian - Magyar",
"Italian - Italiano",
"Japanese - 日本語",
"Korean - 한국어",
"Polish - Polski",
"Portuguese - Português (Portugal)",
"Russian - Русский",
"Simplified Chinese - 简体中文",
"Spanish - Español",
"Traditional Chinese - 繁體中文",
"Turkish - Türkçe"
],
"default": "English",
"description": "The language that Cline should use for communication."
},
"cline.mcpMarketplace.enabled": {
"type": "boolean",
"default": true,
"description": "Controls whether the MCP Marketplace is enabled."
}
}
}
@@ -176,18 +276,19 @@
"watch-tests": "tsc -p . -w --outDir out",
"pretest": "npm run compile-tests && npm run compile && npm run lint",
"check-types": "tsc --noEmit",
"lint": "eslint src --ext ts",
"lint": "eslint src --ext ts && eslint webview-ui/src --ext ts",
"format": "prettier . --check",
"format:fix": "prettier . --write",
"test": "vscode-test",
"install:all": "npm install && cd webview-ui && npm install",
"start:webview": "cd webview-ui && npm run start",
"dev:webview": "cd webview-ui && npm run dev",
"build:webview": "cd webview-ui && npm run build",
"test:webview": "cd webview-ui && npm run test",
"publish:marketplace": "vsce publish && ovsx publish",
"publish:marketplace:prerelease": "vsce publish --pre-release && ovsx publish --pre-release",
"prepare": "husky",
"changeset": "changeset"
"changeset": "changeset",
"version-packages": "changeset version"
},
"devDependencies": {
"@changesets/cli": "^2.27.12",
@@ -196,51 +297,65 @@
"@types/mocha": "^10.0.7",
"@types/node": "20.x",
"@types/should": "^11.2.0",
"@types/sinon": "^17.0.4",
"@types/vscode": "^1.84.0",
"@typescript-eslint/eslint-plugin": "^7.14.1",
"@typescript-eslint/parser": "^7.11.0",
"@vscode/test-cli": "^0.0.9",
"@vscode/test-electron": "^2.4.0",
"chai": "^4.3.10",
"esbuild": "^0.21.5",
"esbuild": "^0.25.0",
"eslint": "^8.57.0",
"husky": "^9.1.7",
"npm-run-all": "^4.1.5",
"prettier": "^3.3.3",
"should": "^13.2.3",
"sinon": "^19.0.2",
"typescript": "^5.4.5"
},
"dependencies": {
"@anthropic-ai/bedrock-sdk": "^0.10.2",
"@anthropic-ai/sdk": "^0.26.0",
"@anthropic-ai/vertex-sdk": "^0.4.1",
"@anthropic-ai/bedrock-sdk": "^0.12.4",
"@anthropic-ai/sdk": "^0.37.0",
"@anthropic-ai/vertex-sdk": "^0.6.4",
"@aws-sdk/client-bedrock-runtime": "^3.758.0",
"@google-cloud/vertexai": "^1.9.3",
"@google/generative-ai": "^0.18.0",
"@mistralai/mistralai": "^1.3.6",
"@modelcontextprotocol/sdk": "^1.0.1",
"@mistralai/mistralai": "^1.5.0",
"@modelcontextprotocol/sdk": "^1.7.0",
"@opentelemetry/api": "^1.4.1",
"@opentelemetry/exporter-trace-otlp-http": "^0.39.1",
"@opentelemetry/resources": "^1.30.1",
"@opentelemetry/sdk-node": "^0.39.1",
"@opentelemetry/sdk-trace-node": "^1.30.1",
"@opentelemetry/semantic-conventions": "^1.30.0",
"@types/clone-deep": "^4.0.4",
"@types/get-folder-size": "^3.0.4",
"@types/pdf-parse": "^1.1.4",
"@types/turndown": "^5.0.5",
"@vscode/codicons": "^0.0.36",
"axios": "^1.7.4",
"axios": "^1.8.2",
"cheerio": "^1.0.0",
"chokidar": "^4.0.1",
"clone-deep": "^4.0.1",
"default-shell": "^2.2.0",
"delay": "^6.0.0",
"diff": "^5.2.0",
"execa": "^9.5.2",
"fast-deep-equal": "^3.1.3",
"firebase": "^11.2.0",
"get-folder-size": "^5.0.0",
"globby": "^14.0.2",
"ignore": "^7.0.3",
"isbinaryfile": "^5.0.2",
"mammoth": "^1.8.0",
"monaco-vscode-textmate-theme-converter": "^0.1.7",
"openai": "^4.61.0",
"ollama": "^0.5.13",
"open-graph-scraper": "^6.9.0",
"openai": "^4.83.0",
"os-name": "^6.0.0",
"p-timeout": "^6.1.4",
"p-wait-for": "^5.0.2",
"pdf-parse": "^1.1.1",
"posthog-node": "^4.8.1",
"puppeteer-chromium-resolver": "^23.0.0",
"puppeteer-core": "^23.4.0",
"serialize-error": "^11.0.3",
@@ -249,6 +364,6 @@
"tree-sitter-wasms": "^0.1.11",
"turndown": "^7.2.0",
"web-tree-sitter": "^0.22.6",
"zod": "^3.23.8"
"zod": "^3.24.2"
}
}
+26 -1
View File
@@ -9,14 +9,23 @@ import { OllamaHandler } from "./providers/ollama"
import { LmStudioHandler } from "./providers/lmstudio"
import { GeminiHandler } from "./providers/gemini"
import { OpenAiNativeHandler } from "./providers/openai-native"
import { ApiStream } from "./transform/stream"
import { ApiStream, ApiStreamUsageChunk } from "./transform/stream"
import { DeepSeekHandler } from "./providers/deepseek"
import { RequestyHandler } from "./providers/requesty"
import { TogetherHandler } from "./providers/together"
import { QwenHandler } from "./providers/qwen"
import { MistralHandler } from "./providers/mistral"
import { VsCodeLmHandler } from "./providers/vscode-lm"
import { ClineHandler } from "./providers/cline"
import { LiteLlmHandler } from "./providers/litellm"
import { AskSageHandler } from "./providers/asksage"
import { XAIHandler } from "./providers/xai"
import { SambanovaHandler } from "./providers/sambanova"
export interface ApiHandler {
createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream
getModel(): { id: string; info: ModelInfo }
getApiStreamUsage?(): Promise<ApiStreamUsageChunk | undefined>
}
export interface SingleCompletionHandler {
@@ -46,10 +55,26 @@ export function buildApiHandler(configuration: ApiConfiguration): ApiHandler {
return new OpenAiNativeHandler(options)
case "deepseek":
return new DeepSeekHandler(options)
case "requesty":
return new RequestyHandler(options)
case "together":
return new TogetherHandler(options)
case "qwen":
return new QwenHandler(options)
case "mistral":
return new MistralHandler(options)
case "vscode-lm":
return new VsCodeLmHandler(options)
case "cline":
return new ClineHandler(options)
case "litellm":
return new LiteLlmHandler(options)
case "asksage":
return new AskSageHandler(options)
case "xai":
return new XAIHandler(options)
case "sambanova":
return new SambanovaHandler(options)
default:
return new AnthropicHandler(options)
}
+38 -3
View File
@@ -1,5 +1,6 @@
import { Anthropic } from "@anthropic-ai/sdk"
import { Stream as AnthropicStream } from "@anthropic-ai/sdk/streaming"
import { withRetry } from "../retry"
import { anthropicDefaultModelId, AnthropicModelId, anthropicModels, ApiHandlerOptions, ModelInfo } from "../../shared/api"
import { ApiHandler } from "../index"
import { ApiStream } from "../transform/stream"
@@ -16,12 +17,18 @@ export class AnthropicHandler implements ApiHandler {
})
}
@withRetry()
async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream {
const model = this.getModel()
let stream: AnthropicStream<Anthropic.Beta.PromptCaching.Messages.RawPromptCachingBetaMessageStreamEvent>
let stream: AnthropicStream<Anthropic.RawMessageStreamEvent>
const modelId = model.id
let budget_tokens = this.options.thinkingBudgetTokens || 0
const reasoningOn = modelId.includes("3-7") && budget_tokens !== 0 ? true : false
switch (modelId) {
// 'latest' alias does not support cache_control
case "claude-3-7-sonnet-20250219":
case "claude-3-5-sonnet-20241022":
case "claude-3-5-haiku-20241022":
case "claude-3-opus-20240229":
@@ -35,11 +42,14 @@ export class AnthropicHandler implements ApiHandler {
)
const lastUserMsgIndex = userMsgIndices[userMsgIndices.length - 1] ?? -1
const secondLastMsgUserIndex = userMsgIndices[userMsgIndices.length - 2] ?? -1
stream = await this.client.beta.promptCaching.messages.create(
stream = await this.client.messages.create(
{
model: modelId,
thinking: reasoningOn ? { type: "enabled", budget_tokens: budget_tokens } : undefined,
max_tokens: model.info.maxTokens || 8192,
temperature: 0,
// "Thinking isnt compatible with temperature, top_p, or top_k modifications as well as forced tool use."
// (https://docs.anthropic.com/en/docs/build-with-claude/extended-thinking#important-considerations-when-using-extended-thinking)
temperature: reasoningOn ? undefined : 0,
system: [
{
text: systemPrompt,
@@ -86,6 +96,7 @@ 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-3-7-sonnet-20250219":
case "claude-3-5-sonnet-20241022":
case "claude-3-5-haiku-20241022":
case "claude-3-opus-20240229":
@@ -144,6 +155,20 @@ export class AnthropicHandler implements ApiHandler {
break
case "content_block_start":
switch (chunk.content_block.type) {
case "thinking":
yield {
type: "reasoning",
reasoning: chunk.content_block.thinking || "",
}
break
case "redacted_thinking":
// Handle redacted thinking blocks - we still mark it as reasoning
// but note that the content is encrypted
yield {
type: "reasoning",
reasoning: "[Redacted thinking block]",
}
break
case "text":
// we may receive multiple text blocks, in which case just insert a line break between them
if (chunk.index > 0) {
@@ -161,12 +186,22 @@ export class AnthropicHandler implements ApiHandler {
break
case "content_block_delta":
switch (chunk.delta.type) {
case "thinking_delta":
yield {
type: "reasoning",
reasoning: chunk.delta.thinking,
}
break
case "text_delta":
yield {
type: "text",
text: chunk.delta.text,
}
break
case "signature_delta":
// We don't need to do anything with the signature in the client
// It's used when sending the thinking block back to the API
break
}
break
case "content_block_stop":
+115
View File
@@ -0,0 +1,115 @@
import { Anthropic } from "@anthropic-ai/sdk"
import { ApiHandler } from ".."
import {
ApiHandlerOptions,
ModelInfo,
AskSageModelId,
askSageModels,
askSageDefaultModelId,
askSageDefaultURL,
} from "../../shared/api"
import { ApiStream } from "../transform/stream"
type AskSageRequest = {
system_prompt: string
message: {
user: "gpt" | "me"
message: string
}[]
model: string
dataset: "none"
}
type AskSageResponse = {
uuid: string
status: number
// Response status
response: string
// Generated response message
message: string
}
export class AskSageHandler implements ApiHandler {
private options: ApiHandlerOptions
private apiUrl: string
private apiKey: string
constructor(options: ApiHandlerOptions) {
console.log("init api url", options.asksageApiUrl, askSageDefaultURL)
this.options = options
this.apiKey = options.asksageApiKey || ""
this.apiUrl = options.asksageApiUrl || askSageDefaultURL
if (!this.apiKey) {
throw new Error("AskSage API key is required")
}
}
async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream {
try {
const model = this.getModel()
// Transform messages into AskSageRequest format
const formattedMessages = messages.map((msg) => {
const content = Array.isArray(msg.content)
? msg.content.map((block) => ("text" in block ? block.text : "")).join("")
: msg.content
return {
user: msg.role === "assistant" ? ("gpt" as const) : ("me" as const),
message: content,
}
})
const request: AskSageRequest = {
system_prompt: systemPrompt,
message: formattedMessages,
model: model.id,
dataset: "none",
}
// Make request to AskSage API
const response = await fetch(`${this.apiUrl}/query`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"x-access-tokens": this.apiKey,
},
body: JSON.stringify(request),
})
if (!response.ok) {
const error = await response.text()
throw new Error(`AskSage API error: ${error}`)
}
const result = (await response.json()) as AskSageResponse
if (!result.message) {
throw new Error("No content in AskSage response")
}
// Return entire response as a single chunk since streaming is not supported
yield {
type: "text",
text: result.message,
}
} catch (error) {
if (error instanceof Error) {
throw new Error(`AskSage request failed: ${error.message}`)
}
}
}
getModel(): { id: string; info: ModelInfo } {
const modelId = this.options.apiModelId
if (modelId && modelId in askSageModels) {
const id = modelId as AskSageModelId
return { id, info: askSageModels[id] }
}
return {
id: askSageDefaultModelId,
info: askSageModels[askSageDefaultModelId],
}
}
}
+584 -35
View File
@@ -1,58 +1,103 @@
import AnthropicBedrock from "@anthropic-ai/bedrock-sdk"
import { Anthropic } from "@anthropic-ai/sdk"
import { withRetry } from "../retry"
import { ApiHandler } from "../"
import { convertToR1Format } from "../transform/r1-format"
import { ApiHandlerOptions, bedrockDefaultModelId, BedrockModelId, bedrockModels, ModelInfo } from "../../shared/api"
import { calculateApiCostOpenAI } from "../../utils/cost"
import { ApiStream } from "../transform/stream"
import { fromNodeProviderChain } from "@aws-sdk/credential-providers"
import {
BedrockRuntimeClient,
ConversationRole,
ConverseStreamCommand,
InvokeModelWithResponseStreamCommand,
} from "@aws-sdk/client-bedrock-runtime"
// https://docs.anthropic.com/en/api/claude-on-amazon-bedrock
export class AwsBedrockHandler implements ApiHandler {
private options: ApiHandlerOptions
private client: AnthropicBedrock
constructor(options: ApiHandlerOptions) {
this.options = options
this.client = new AnthropicBedrock({
// Authenticate by either providing the keys below or use the default AWS credential providers, such as
// using ~/.aws/credentials or the "AWS_SECRET_ACCESS_KEY" and "AWS_ACCESS_KEY_ID" environment variables.
...(this.options.awsAccessKey ? { awsAccessKey: this.options.awsAccessKey } : {}),
...(this.options.awsSecretKey ? { awsSecretKey: this.options.awsSecretKey } : {}),
...(this.options.awsSessionToken ? { awsSessionToken: this.options.awsSessionToken } : {}),
// awsRegion changes the aws region to which the request is made. By default, we read AWS_REGION,
// and if that's not present, we default to us-east-1. Note that we do not read ~/.aws/config for the region.
awsRegion: this.options.awsRegion,
})
}
@withRetry()
async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream {
// cross region inference requires prefixing the model id with the region
let modelId: string
if (this.options.awsUseCrossRegionInference) {
let regionPrefix = (this.options.awsRegion || "").slice(0, 3)
switch (regionPrefix) {
case "us-":
modelId = `us.${this.getModel().id}`
break
case "eu-":
modelId = `eu.${this.getModel().id}`
break
default:
// cross region inference is not supported in this region, falling back to default model
modelId = this.getModel().id
break
}
} else {
modelId = this.getModel().id
let modelId = await this.getModelId()
const model = this.getModel()
// Check if this is an Amazon Nova model
if (modelId.includes("amazon.nova")) {
yield* this.createNovaMessage(systemPrompt, messages, modelId, model)
return
}
const stream = await this.client.messages.create({
// Check if this is a Deepseek model
if (modelId.includes("deepseek")) {
yield* this.createDeepseekMessage(systemPrompt, messages, modelId, model)
return
}
let budget_tokens = this.options.thinkingBudgetTokens || 0
const reasoningOn = modelId.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[])
const lastUserMsgIndex = userMsgIndices[userMsgIndices.length - 1] ?? -1
const secondLastMsgUserIndex = userMsgIndices[userMsgIndices.length - 2] ?? -1
// Create anthropic client, using sessions created or renewed after this handler's
// initialization, and allowing for session renewal if necessary as well
const client = await this.getAnthropicClient()
const stream = await client.messages.create({
model: modelId,
max_tokens: this.getModel().info.maxTokens || 8192,
temperature: 0,
system: systemPrompt,
messages,
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" },
}),
}
: content,
),
}
}
return message
}),
stream: true,
})
for await (const chunk of stream) {
switch (chunk.type) {
case "message_start":
@@ -61,6 +106,8 @@ export class AwsBedrockHandler implements ApiHandler {
type: "usage",
inputTokens: usage.input_tokens || 0,
outputTokens: usage.output_tokens || 0,
cacheWriteTokens: usage.cache_creation_input_tokens || undefined,
cacheReadTokens: usage.cache_read_input_tokens || undefined,
}
break
case "message_delta":
@@ -70,9 +117,22 @@ export class AwsBedrockHandler implements ApiHandler {
outputTokens: chunk.usage.output_tokens || 0,
}
break
case "content_block_start":
switch (chunk.content_block.type) {
case "thinking":
yield {
type: "reasoning",
reasoning: chunk.content_block.thinking || "",
}
break
case "redacted_thinking":
// Handle redacted thinking blocks - we still mark it as reasoning
// but note that the content is encrypted
yield {
type: "reasoning",
reasoning: "[Redacted thinking block]",
}
break
case "text":
if (chunk.index > 0) {
yield {
@@ -89,6 +149,12 @@ export class AwsBedrockHandler implements ApiHandler {
break
case "content_block_delta":
switch (chunk.delta.type) {
case "thinking_delta":
yield {
type: "reasoning",
reasoning: chunk.delta.thinking,
}
break
case "text_delta":
yield {
type: "text",
@@ -112,4 +178,487 @@ export class AwsBedrockHandler implements ApiHandler {
info: bedrockModels[bedrockDefaultModelId],
}
}
// Default AWS region
private static readonly DEFAULT_REGION = "us-east-1"
/**
* Gets AWS credentials using the provider chain
* Centralizes credential retrieval logic for all AWS services
*/
private async getAwsCredentials(): Promise<{
accessKeyId: string
secretAccessKey: string
sessionToken?: string
}> {
// Create AWS credentials by executing an AWS provider chain
const providerChain = fromNodeProviderChain()
return await AwsBedrockHandler.withTempEnv(
() => {
AwsBedrockHandler.setEnv("AWS_REGION", this.options.awsRegion)
AwsBedrockHandler.setEnv("AWS_ACCESS_KEY_ID", this.options.awsAccessKey)
AwsBedrockHandler.setEnv("AWS_SECRET_ACCESS_KEY", this.options.awsSecretKey)
AwsBedrockHandler.setEnv("AWS_SESSION_TOKEN", this.options.awsSessionToken)
AwsBedrockHandler.setEnv("AWS_PROFILE", this.options.awsProfile)
},
() => providerChain(),
)
}
/**
* Gets the AWS region to use, with fallback to default
*/
private getRegion(): string {
return this.options.awsRegion || AwsBedrockHandler.DEFAULT_REGION
}
/**
* Creates a BedrockRuntimeClient with the appropriate credentials
*/
private async getBedrockClient(): Promise<BedrockRuntimeClient> {
const credentials = await this.getAwsCredentials()
return new BedrockRuntimeClient({
region: this.getRegion(),
credentials: {
accessKeyId: credentials.accessKeyId,
secretAccessKey: credentials.secretAccessKey,
sessionToken: credentials.sessionToken,
},
...(this.options.awsBedrockEndpoint && { endpoint: this.options.awsBedrockEndpoint }),
})
}
/**
* Creates an AnthropicBedrock client with the appropriate credentials
*/
private async getAnthropicClient(): Promise<AnthropicBedrock> {
const credentials = await this.getAwsCredentials()
// Return an AnthropicBedrock client with the resolved/assumed credentials.
return new AnthropicBedrock({
awsAccessKey: credentials.accessKeyId,
awsSecretKey: credentials.secretAccessKey,
awsSessionToken: credentials.sessionToken,
awsRegion: this.getRegion(),
...(this.options.awsBedrockEndpoint && { baseURL: this.options.awsBedrockEndpoint }),
})
}
/**
* Gets the appropriate model ID, accounting for cross-region inference if enabled
*/
async getModelId(): Promise<string> {
if (this.options.awsUseCrossRegionInference) {
let regionPrefix = this.getRegion().slice(0, 3)
switch (regionPrefix) {
case "us-":
return `us.${this.getModel().id}`
case "eu-":
return `eu.${this.getModel().id}`
case "ap-":
return `apac.${this.getModel().id}`
default:
// cross region inference is not supported in this region, falling back to default model
return this.getModel().id
}
}
return this.getModel().id
}
private static async withTempEnv<R>(updateEnv: () => void, fn: () => Promise<R>): Promise<R> {
const previousEnv = { ...process.env }
try {
updateEnv()
return await fn()
} finally {
process.env = previousEnv
}
}
private static setEnv(key: string, value: string | undefined) {
if (key !== "" && value !== undefined) {
process.env[key] = value
}
}
/**
* Creates a message using the Deepseek R1 model through AWS Bedrock
*/
private async *createDeepseekMessage(
systemPrompt: string,
messages: Anthropic.Messages.MessageParam[],
modelId: string,
model: { id: BedrockModelId; info: ModelInfo },
): ApiStream {
// Get Bedrock client with proper credentials
const client = await this.getBedrockClient()
// Format prompt for DeepSeek R1 according to documentation
const formattedPrompt = this.formatDeepseekR1Prompt(systemPrompt, messages)
// Prepare the request based on DeepSeek R1's expected format
const command = new InvokeModelWithResponseStreamCommand({
modelId: modelId,
contentType: "application/json",
accept: "application/json",
body: JSON.stringify({
prompt: formattedPrompt,
max_tokens: model.info.maxTokens || 8000,
temperature: 0,
}),
})
// Track token usage
const inputTokenEstimate = this.estimateInputTokens(systemPrompt, messages)
let outputTokens = 0
let isFirstChunk = true
let accumulatedTokens = 0
const TOKEN_REPORT_THRESHOLD = 100 // Report usage after accumulating this many tokens
// Execute the streaming request
const response = await client.send(command)
if (response.body) {
for await (const chunk of response.body) {
if (chunk.chunk?.bytes) {
try {
// Parse the response chunk
const decodedChunk = new TextDecoder().decode(chunk.chunk.bytes)
const parsedChunk = JSON.parse(decodedChunk)
// Report usage on first chunk
if (isFirstChunk) {
isFirstChunk = false
const totalCost = calculateApiCostOpenAI(model.info, inputTokenEstimate, 0, 0, 0)
yield {
type: "usage",
inputTokens: inputTokenEstimate,
outputTokens: 0,
totalCost: totalCost,
}
}
// Handle DeepSeek R1 response format
if (parsedChunk.choices && parsedChunk.choices.length > 0) {
// For non-streaming response (full response)
const text = parsedChunk.choices[0].text
if (text) {
const chunkTokens = this.estimateTokenCount(text)
outputTokens += chunkTokens
accumulatedTokens += chunkTokens
yield {
type: "text",
text: text,
}
if (accumulatedTokens >= TOKEN_REPORT_THRESHOLD) {
const totalCost = calculateApiCostOpenAI(model.info, 0, accumulatedTokens, 0, 0)
yield {
type: "usage",
inputTokens: 0,
outputTokens: accumulatedTokens,
totalCost: totalCost,
}
accumulatedTokens = 0
}
}
} else if (parsedChunk.delta?.text) {
// For streaming response (delta updates)
const text = parsedChunk.delta.text
const chunkTokens = this.estimateTokenCount(text)
outputTokens += chunkTokens
accumulatedTokens += chunkTokens
yield {
type: "text",
text: text,
}
// Report aggregated token usage only when threshold is reached
if (accumulatedTokens >= TOKEN_REPORT_THRESHOLD) {
const totalCost = calculateApiCostOpenAI(model.info, 0, accumulatedTokens, 0, 0)
yield {
type: "usage",
inputTokens: 0,
outputTokens: accumulatedTokens,
totalCost: totalCost,
}
accumulatedTokens = 0
}
}
} catch (error) {
console.error("Error parsing Deepseek response chunk:", error)
// Propagate the error by yielding a text response with error information
yield {
type: "text",
text: `[ERROR] Failed to parse Deepseek response: ${error instanceof Error ? error.message : String(error)}`,
}
}
}
}
// Report any remaining accumulated tokens at the end of the stream
if (accumulatedTokens > 0) {
const totalCost = calculateApiCostOpenAI(model.info, 0, accumulatedTokens, 0, 0)
yield {
type: "usage",
inputTokens: 0,
outputTokens: accumulatedTokens,
totalCost: totalCost,
}
}
// Add final total cost calculation that includes both input and output tokens
const finalTotalCost = calculateApiCostOpenAI(model.info, inputTokenEstimate, outputTokens, 0, 0)
yield {
type: "usage",
inputTokens: inputTokenEstimate,
outputTokens: outputTokens,
totalCost: finalTotalCost,
}
}
}
/**
* Formats prompt for DeepSeek R1 model according to documentation
* First uses convertToR1Format to merge consecutive messages with the same role,
* then converts to the string format that DeepSeek R1 expects
*/
private formatDeepseekR1Prompt(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): string {
// First use convertToR1Format to merge consecutive messages with the same role
const r1Messages = convertToR1Format([{ role: "user", content: systemPrompt }, ...messages])
// Then convert to the special string format expected by DeepSeek R1
let combinedContent = ""
for (const message of r1Messages) {
let content = ""
if (message.content) {
if (typeof message.content === "string") {
content = message.content
} else {
// Extract text content from message parts
content = message.content
.filter((part) => part.type === "text")
.map((part) => part.text)
.join("\n")
}
}
combinedContent += message.role === "user" ? "User: " + content + "\n" : "Assistant: " + content + "\n"
}
// Format according to DeepSeek R1's expected prompt format
return `<begin▁of▁sentence><User>${combinedContent}<Assistant><think>\n`
}
/**
* Estimates token count based on text length (approximate)
* Note: This is a rough estimation, as the actual token count depends on the tokenizer
*/
private estimateInputTokens(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): number {
// For Deepseek R1, we estimate the token count of the formatted prompt
// The formatted prompt includes special tokens and consistent formatting
const formattedPrompt = this.formatDeepseekR1Prompt(systemPrompt, messages)
return Math.ceil(formattedPrompt.length / 4)
}
/**
* Estimates token count for a text string
*/
private estimateTokenCount(text: string): number {
// Approximate 4 characters per token
return Math.ceil(text.length / 4)
}
/**
* Creates a message using Amazon Nova models through AWS Bedrock
* Implements support for Nova Micro, Nova Lite, and Nova Pro models
*/
private async *createNovaMessage(
systemPrompt: string,
messages: Anthropic.Messages.MessageParam[],
modelId: string,
model: { id: BedrockModelId; info: ModelInfo },
): ApiStream {
// Get Bedrock client with proper credentials
const client = await this.getBedrockClient()
// Format messages for Nova model
const formattedMessages = this.formatNovaMessages(messages)
// Prepare request for Nova model
const command = new ConverseStreamCommand({
modelId: modelId,
messages: formattedMessages,
system: systemPrompt ? [{ text: systemPrompt }] : undefined,
inferenceConfig: {
maxTokens: model.info.maxTokens || 5000,
temperature: 0,
// topP: 0.9, // Alternative: use topP instead of temperature
},
})
// Execute the streaming request and handle response
try {
const response = await client.send(command)
if (response.stream) {
let hasReportedInputTokens = false
for await (const chunk of response.stream) {
// Handle metadata events with token usage information
if (chunk.metadata?.usage) {
// Report complete token usage from the model itself
const inputTokens = chunk.metadata.usage.inputTokens || 0
const outputTokens = chunk.metadata.usage.outputTokens || 0
yield {
type: "usage",
inputTokens,
outputTokens,
totalCost: calculateApiCostOpenAI(model.info, inputTokens, outputTokens, 0, 0),
}
hasReportedInputTokens = true
}
// Handle content delta (text generation)
if (chunk.contentBlockDelta?.delta?.text) {
yield {
type: "text",
text: chunk.contentBlockDelta.delta.text,
}
}
// Handle reasoning content if present
if (chunk.contentBlockDelta?.delta?.reasoningContent?.text) {
yield {
type: "reasoning",
reasoning: chunk.contentBlockDelta.delta.reasoningContent.text,
}
}
// Handle errors
if (chunk.internalServerException) {
yield {
type: "text",
text: `[ERROR] Internal server error: ${chunk.internalServerException.message}`,
}
} else if (chunk.modelStreamErrorException) {
yield {
type: "text",
text: `[ERROR] Model stream error: ${chunk.modelStreamErrorException.message}`,
}
} else if (chunk.validationException) {
yield {
type: "text",
text: `[ERROR] Validation error: ${chunk.validationException.message}`,
}
} else if (chunk.throttlingException) {
yield {
type: "text",
text: `[ERROR] Throttling error: ${chunk.throttlingException.message}`,
}
} else if (chunk.serviceUnavailableException) {
yield {
type: "text",
text: `[ERROR] Service unavailable: ${chunk.serviceUnavailableException.message}`,
}
}
}
}
} catch (error) {
console.error("Error processing Nova model response:", error)
yield {
type: "text",
text: `[ERROR] Failed to process Nova response: ${error instanceof Error ? error.message : String(error)}`,
}
}
}
/**
* Formats messages for Amazon Nova models according to the SDK specification
*/
private formatNovaMessages(messages: Anthropic.Messages.MessageParam[]): { role: ConversationRole; content: any[] }[] {
return messages.map((message) => {
// Determine role (user or assistant)
const role = message.role === "user" ? ConversationRole.USER : ConversationRole.ASSISTANT
// Process content based on type
let content: any[] = []
if (typeof message.content === "string") {
// Simple text content
content = [{ text: message.content }]
} else if (Array.isArray(message.content)) {
// Convert Anthropic content format to Nova content format
content = message.content
.map((item) => {
// Text content
if (item.type === "text") {
return { text: item.text }
}
// Image content
if (item.type === "image") {
// Handle different image source formats
let imageData: Uint8Array
let format = "jpeg" // default format
// Extract format from media_type if available
if (item.source.media_type) {
// Extract format from media_type (e.g., "image/jpeg" -> "jpeg")
const formatMatch = item.source.media_type.match(/image\/(\w+)/)
if (formatMatch && formatMatch[1]) {
format = formatMatch[1]
// Ensure format is one of the allowed values
if (!["png", "jpeg", "gif", "webp"].includes(format)) {
format = "jpeg" // Default to jpeg if not supported
}
}
}
// Get image data
try {
if (typeof item.source.data === "string") {
// Handle base64 encoded data
const base64Data = item.source.data.replace(/^data:image\/\w+;base64,/, "")
imageData = new Uint8Array(Buffer.from(base64Data, "base64"))
} else if (item.source.data && typeof item.source.data === "object") {
// Try to convert to Uint8Array
imageData = new Uint8Array(Buffer.from(item.source.data as any))
} else {
console.error("Unsupported image data format")
return null // Skip this item if format is not supported
}
} catch (error) {
console.error("Could not convert image data to Uint8Array:", error)
return null // Skip this item if conversion fails
}
return {
image: {
format,
source: {
bytes: imageData,
},
},
}
}
// Return null for unsupported content types
return null
})
.filter(Boolean) // Remove any null items
}
// Return formatted message
return {
role,
content,
}
})
}
}
+123
View File
@@ -0,0 +1,123 @@
import { Anthropic } from "@anthropic-ai/sdk"
import OpenAI from "openai"
import { ApiHandler } from "../"
import { ApiHandlerOptions, ModelInfo, openRouterDefaultModelId, openRouterDefaultModelInfo } from "../../shared/api"
import { createOpenRouterStream } from "../transform/openrouter-stream"
import { ApiStream, ApiStreamUsageChunk } from "../transform/stream"
import axios from "axios"
import { OpenRouterErrorResponse } from "./types"
export class ClineHandler implements ApiHandler {
private options: ApiHandlerOptions
private client: OpenAI
lastGenerationId?: string
constructor(options: ApiHandlerOptions) {
this.options = options
this.client = new OpenAI({
baseURL: "https://api.cline.bot/v1",
apiKey: this.options.clineApiKey || "",
})
}
async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream {
this.lastGenerationId = undefined
const stream = await createOpenRouterStream(
this.client,
systemPrompt,
messages,
this.getModel(),
this.options.o3MiniReasoningEffort,
this.options.thinkingBudgetTokens,
this.options.openRouterProviderSorting,
)
let didOutputUsage: boolean = false
for await (const chunk of stream) {
// openrouter returns an error object instead of the openai sdk throwing an error
if ("error" in chunk) {
const error = chunk.error as OpenRouterErrorResponse["error"]
console.error(`Cline API Error: ${error?.code} - ${error?.message}`)
// Include metadata in the error message if available
const metadataStr = error.metadata ? `\nMetadata: ${JSON.stringify(error.metadata, null, 2)}` : ""
throw new Error(`Cline API Error ${error.code}: ${error.message}${metadataStr}`)
}
if (!this.lastGenerationId && chunk.id) {
this.lastGenerationId = chunk.id
}
const delta = chunk.choices[0]?.delta
if (delta?.content) {
yield {
type: "text",
text: delta.content,
}
}
// Reasoning tokens are returned separately from the content
if ("reasoning" in delta && delta.reasoning) {
yield {
type: "reasoning",
// @ts-ignore-next-line
reasoning: delta.reasoning,
}
}
if (!didOutputUsage && chunk.usage) {
yield {
type: "usage",
inputTokens: chunk.usage.prompt_tokens || 0,
outputTokens: chunk.usage.completion_tokens || 0,
// @ts-ignore-next-line
totalCost: chunk.usage.cost || 0,
}
didOutputUsage = true
}
}
// Fallback to generation endpoint if usage chunk not returned
if (!didOutputUsage) {
const apiStreamUsage = await this.getApiStreamUsage()
if (apiStreamUsage) {
yield apiStreamUsage
}
}
}
async getApiStreamUsage(): Promise<ApiStreamUsageChunk | undefined> {
if (this.lastGenerationId) {
try {
const response = await axios.get(`https://api.cline.bot/v1/generation?id=${this.lastGenerationId}`, {
headers: {
Authorization: `Bearer ${this.options.clineApiKey}`,
},
timeout: 15_000, // this request hangs sometimes
})
const generation = response.data
return {
type: "usage",
inputTokens: generation?.native_tokens_prompt || 0,
outputTokens: generation?.native_tokens_completion || 0,
totalCost: generation?.total_cost || 0,
}
} catch (error) {
// ignore if fails
console.error("Error fetching cline generation details:", error)
}
}
return undefined
}
getModel(): { id: string; info: ModelInfo } {
const modelId = this.options.openRouterModelId
const modelInfo = this.options.openRouterModelInfo
if (modelId && modelInfo) {
return { id: modelId, info: modelInfo }
}
return { id: openRouterDefaultModelId, info: openRouterDefaultModelInfo }
}
}
+55 -9
View File
@@ -1,9 +1,12 @@
import { Anthropic } from "@anthropic-ai/sdk"
import OpenAI from "openai"
import { withRetry } from "../retry"
import { ApiHandler } from "../"
import { ApiHandlerOptions, DeepSeekModelId, ModelInfo, deepSeekDefaultModelId, deepSeekModels } from "../../shared/api"
import { calculateApiCostOpenAI } from "../../utils/cost"
import { convertToOpenAiMessages } from "../transform/openai-format"
import { ApiStream } from "../transform/stream"
import { convertToR1Format } from "../transform/r1-format"
export class DeepSeekHandler implements ApiHandler {
private options: ApiHandlerOptions
@@ -17,12 +20,56 @@ export class DeepSeekHandler implements ApiHandler {
})
}
private async *yieldUsage(info: ModelInfo, usage: OpenAI.Completions.CompletionUsage | undefined): ApiStream {
// Deepseek reports total input AND cache reads/writes,
// see context caching: https://api-docs.deepseek.com/guides/kv_cache)
// where the input tokens is the sum of the cache hits/misses, just like OpenAI.
// This affects:
// 1) context management truncation algorithm, and
// 2) cost calculation
// Deepseek usage includes extra fields.
// Safely cast the prompt token details section to the appropriate structure.
interface DeepSeekUsage extends OpenAI.CompletionUsage {
prompt_cache_hit_tokens?: number
prompt_cache_miss_tokens?: number
}
const deepUsage = usage as DeepSeekUsage
const inputTokens = deepUsage?.prompt_tokens || 0
const outputTokens = deepUsage?.completion_tokens || 0
const cacheReadTokens = deepUsage?.prompt_cache_hit_tokens || 0
const cacheWriteTokens = deepUsage?.prompt_cache_miss_tokens || 0
const totalCost = calculateApiCostOpenAI(info, inputTokens, outputTokens, cacheWriteTokens, cacheReadTokens)
yield {
type: "usage",
inputTokens: inputTokens,
outputTokens: outputTokens,
cacheWriteTokens: cacheWriteTokens,
cacheReadTokens: cacheReadTokens,
totalCost: totalCost,
}
}
@withRetry()
async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream {
const model = this.getModel()
const isDeepseekReasoner = model.id.includes("deepseek-reasoner")
let openAiMessages: OpenAI.Chat.ChatCompletionMessageParam[] = [
{ role: "system", content: systemPrompt },
...convertToOpenAiMessages(messages),
]
if (isDeepseekReasoner) {
openAiMessages = convertToR1Format([{ role: "user", content: systemPrompt }, ...messages])
}
const stream = await this.client.chat.completions.create({
model: model.id,
max_completion_tokens: model.info.maxTokens,
messages: [{ role: "system", content: systemPrompt }, ...convertToOpenAiMessages(messages)],
messages: openAiMessages,
stream: true,
stream_options: { include_usage: true },
// Only set temperature for non-reasoner models
@@ -38,17 +85,16 @@ export class DeepSeekHandler implements ApiHandler {
}
}
if (chunk.usage) {
if (delta && "reasoning_content" in delta && delta.reasoning_content) {
yield {
type: "usage",
inputTokens: chunk.usage.prompt_tokens || 0, // (deepseek reports total input AND cache reads/writes, see context caching: https://api-docs.deepseek.com/guides/kv_cache) where the input tokens is the sum of the cache hits/misses, while anthropic reports them as separate tokens. This is important to know for 1) context management truncation algorithm, and 2) cost calculation (NOTE: we report both input and cache stats but for now set input price to 0 since all the cost calculation will be done using cache hits/misses)
outputTokens: chunk.usage.completion_tokens || 0,
// @ts-ignore-next-line
cacheReadTokens: chunk.usage.prompt_cache_hit_tokens || 0,
// @ts-ignore-next-line
cacheWriteTokens: chunk.usage.prompt_cache_miss_tokens || 0,
type: "reasoning",
reasoning: (delta.reasoning_content as string | undefined) || "",
}
}
if (chunk.usage) {
yield* this.yieldUsage(model.info, chunk.usage)
}
}
}
+2
View File
@@ -1,5 +1,6 @@
import { Anthropic } from "@anthropic-ai/sdk"
import { GoogleGenerativeAI } from "@google/generative-ai"
import { withRetry } from "../retry"
import { ApiHandler } from "../"
import { ApiHandlerOptions, geminiDefaultModelId, GeminiModelId, geminiModels, ModelInfo } from "../../shared/api"
import { convertAnthropicMessageToGemini } from "../transform/gemini-format"
@@ -17,6 +18,7 @@ export class GeminiHandler implements ApiHandler {
this.client = new GoogleGenerativeAI(options.geminiApiKey)
}
@withRetry()
async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream {
const model = this.client.getGenerativeModel({
model: this.getModel().id,
+67
View File
@@ -0,0 +1,67 @@
import { Anthropic } from "@anthropic-ai/sdk"
import OpenAI from "openai"
import { ApiHandlerOptions, liteLlmDefaultModelId, liteLlmModelInfoSaneDefaults } from "../../shared/api"
import { ApiHandler } from ".."
import { ApiStream } from "../transform/stream"
import { convertToOpenAiMessages } from "../transform/openai-format"
export class LiteLlmHandler implements ApiHandler {
private options: ApiHandlerOptions
private client: OpenAI
constructor(options: ApiHandlerOptions) {
this.options = options
this.client = new OpenAI({
baseURL: this.options.liteLlmBaseUrl || "http://localhost:4000",
apiKey: this.options.liteLlmApiKey || "noop",
})
}
async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream {
const formattedMessages = convertToOpenAiMessages(messages)
const systemMessage: OpenAI.Chat.ChatCompletionSystemMessageParam = {
role: "system",
content: systemPrompt,
}
const modelId = this.options.liteLlmModelId || liteLlmDefaultModelId
const isOminiModel = modelId.includes("o1-mini") || modelId.includes("o3-mini")
let temperature: number | undefined = 0
if (isOminiModel) {
temperature = undefined // does not support temperature
}
const stream = await this.client.chat.completions.create({
model: this.options.liteLlmModelId || liteLlmDefaultModelId,
messages: [systemMessage, ...formattedMessages],
temperature,
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 (chunk.usage) {
yield {
type: "usage",
inputTokens: chunk.usage.prompt_tokens || 0,
outputTokens: chunk.usage.completion_tokens || 0,
}
}
}
}
getModel() {
return {
id: this.options.liteLlmModelId || liteLlmDefaultModelId,
info: liteLlmModelInfoSaneDefaults,
}
}
}
+2 -1
View File
@@ -1,5 +1,6 @@
import { Anthropic } from "@anthropic-ai/sdk"
import { Mistral } from "@mistralai/mistralai"
import { withRetry } from "../retry"
import { ApiHandler } from "../"
import {
ApiHandlerOptions,
@@ -21,11 +22,11 @@ export class MistralHandler implements ApiHandler {
constructor(options: ApiHandlerOptions) {
this.options = options
this.client = new Mistral({
serverURL: "https://codestral.mistral.ai",
apiKey: this.options.mistralApiKey,
})
}
@withRetry()
async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream {
const stream = await this.client.chat.stream({
model: this.getModel().id,
+12 -17
View File
@@ -1,40 +1,35 @@
import { Anthropic } from "@anthropic-ai/sdk"
import OpenAI from "openai"
import { Message, Ollama } from "ollama"
import { ApiHandler } from "../"
import { ApiHandlerOptions, ModelInfo, openAiModelInfoSaneDefaults } from "../../shared/api"
import { convertToOpenAiMessages } from "../transform/openai-format"
import { convertToOllamaMessages } from "../transform/ollama-format"
import { ApiStream } from "../transform/stream"
export class OllamaHandler implements ApiHandler {
private options: ApiHandlerOptions
private client: OpenAI
private client: Ollama
constructor(options: ApiHandlerOptions) {
this.options = options
this.client = new OpenAI({
baseURL: (this.options.ollamaBaseUrl || "http://localhost:11434") + "/v1",
apiKey: "ollama",
})
this.client = new Ollama({ host: this.options.ollamaBaseUrl || "http://localhost:11434" })
}
async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream {
const openAiMessages: OpenAI.Chat.ChatCompletionMessageParam[] = [
{ role: "system", content: systemPrompt },
...convertToOpenAiMessages(messages),
]
const ollamaMessages: Message[] = [{ role: "system", content: systemPrompt }, ...convertToOllamaMessages(messages)]
const stream = await this.client.chat.completions.create({
const stream = await this.client.chat({
model: this.getModel().id,
messages: openAiMessages,
temperature: 0,
messages: ollamaMessages,
stream: true,
options: {
num_ctx: Number(this.options.ollamaApiOptionsCtxNum) || 32768,
},
})
for await (const chunk of stream) {
const delta = chunk.choices[0]?.delta
if (delta?.content) {
if (typeof chunk.message.content === "string") {
yield {
type: "text",
text: delta.content,
text: chunk.message.content,
}
}
}
+52 -14
View File
@@ -1,5 +1,6 @@
import { Anthropic } from "@anthropic-ai/sdk"
import OpenAI from "openai"
import { withRetry } from "../retry"
import { ApiHandler } from "../"
import {
ApiHandlerOptions,
@@ -9,7 +10,9 @@ import {
openAiNativeModels,
} from "../../shared/api"
import { convertToOpenAiMessages } from "../transform/openai-format"
import { calculateApiCostOpenAI } from "../../utils/cost"
import { ApiStream } from "../transform/stream"
import { ChatCompletionReasoningEffort } from "openai/resources/chat/completions.mjs"
export class OpenAiNativeHandler implements ApiHandler {
private options: ApiHandlerOptions
@@ -22,30 +25,70 @@ export class OpenAiNativeHandler implements ApiHandler {
})
}
private async *yieldUsage(info: ModelInfo, usage: OpenAI.Completions.CompletionUsage | undefined): ApiStream {
const inputTokens = usage?.prompt_tokens || 0
const outputTokens = usage?.completion_tokens || 0
const cacheReadTokens = usage?.prompt_tokens_details?.cached_tokens || 0
const cacheWriteTokens = 0
const totalCost = calculateApiCostOpenAI(info, inputTokens, outputTokens, cacheWriteTokens, cacheReadTokens)
yield {
type: "usage",
inputTokens: inputTokens,
outputTokens: outputTokens,
cacheWriteTokens: cacheWriteTokens,
cacheReadTokens: cacheReadTokens,
totalCost: totalCost,
}
}
@withRetry()
async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream {
switch (this.getModel().id) {
const model = this.getModel()
switch (model.id) {
case "o1":
case "o1-preview":
case "o1-mini": {
// o1 doesnt support streaming, non-1 temp, or system prompt
const response = await this.client.chat.completions.create({
model: this.getModel().id,
model: model.id,
messages: [{ role: "user", content: systemPrompt }, ...convertToOpenAiMessages(messages)],
})
yield {
type: "text",
text: response.choices[0]?.message.content || "",
}
yield {
type: "usage",
inputTokens: response.usage?.prompt_tokens || 0,
outputTokens: response.usage?.completion_tokens || 0,
yield* this.yieldUsage(model.info, response.usage)
break
}
case "o3-mini": {
const stream = await this.client.chat.completions.create({
model: model.id,
messages: [{ role: "developer", content: systemPrompt }, ...convertToOpenAiMessages(messages)],
stream: true,
stream_options: { include_usage: true },
reasoning_effort: (this.options.o3MiniReasoningEffort as ChatCompletionReasoningEffort) || "medium",
})
for await (const chunk of stream) {
const delta = chunk.choices[0]?.delta
if (delta?.content) {
yield {
type: "text",
text: delta.content,
}
}
if (chunk.usage) {
// Only last chunk contains usage
yield* this.yieldUsage(model.info, chunk.usage)
}
}
break
}
default: {
const stream = await this.client.chat.completions.create({
model: this.getModel().id,
model: model.id,
// max_completion_tokens: this.getModel().info.maxTokens,
temperature: 0,
messages: [{ role: "system", content: systemPrompt }, ...convertToOpenAiMessages(messages)],
@@ -61,14 +104,9 @@ export class OpenAiNativeHandler implements ApiHandler {
text: delta.content,
}
}
// contains a null value except for the last chunk which contains the token usage statistics for the entire request
if (chunk.usage) {
yield {
type: "usage",
inputTokens: chunk.usage.prompt_tokens || 0,
outputTokens: chunk.usage.completion_tokens || 0,
}
// Only last chunk contains usage
yield* this.yieldUsage(model.info, chunk.usage)
}
}
}
+36 -5
View File
@@ -1,9 +1,12 @@
import { Anthropic } from "@anthropic-ai/sdk"
import OpenAI, { AzureOpenAI } from "openai"
import { withRetry } from "../retry"
import { ApiHandlerOptions, azureOpenAiDefaultApiVersion, ModelInfo, openAiModelInfoSaneDefaults } from "../../shared/api"
import { ApiHandler } from "../index"
import { convertToOpenAiMessages } from "../transform/openai-format"
import { ApiStream } from "../transform/stream"
import { convertToR1Format } from "../transform/r1-format"
import { ChatCompletionReasoningEffort } from "openai/resources/chat/completions.mjs"
export class OpenAiHandler implements ApiHandler {
private options: ApiHandlerOptions
@@ -12,7 +15,8 @@ export class OpenAiHandler implements ApiHandler {
constructor(options: ApiHandlerOptions) {
this.options = options
// Azure API shape slightly differs from the core API shape: https://github.com/openai/openai-node?tab=readme-ov-file#microsoft-azure-openai
if (this.options.openAiBaseUrl?.toLowerCase().includes("azure.com")) {
// Use azureApiVersion to determine if this is an Azure endpoint, since the URL may not always contain 'azure.com'
if (this.options.azureApiVersion || this.options.openAiBaseUrl?.toLowerCase().includes("azure.com")) {
this.client = new AzureOpenAI({
baseURL: this.options.openAiBaseUrl,
apiKey: this.options.openAiApiKey,
@@ -26,15 +30,34 @@ export class OpenAiHandler implements ApiHandler {
}
}
@withRetry()
async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream {
const openAiMessages: OpenAI.Chat.ChatCompletionMessageParam[] = [
const modelId = this.options.openAiModelId ?? ""
const isDeepseekReasoner = modelId.includes("deepseek-reasoner")
const isO3Mini = modelId.includes("o3-mini")
let openAiMessages: OpenAI.Chat.ChatCompletionMessageParam[] = [
{ role: "system", content: systemPrompt },
...convertToOpenAiMessages(messages),
]
let temperature: number | undefined = this.options.openAiModelInfo?.temperature ?? openAiModelInfoSaneDefaults.temperature
let reasoningEffort: ChatCompletionReasoningEffort | undefined = undefined
if (isDeepseekReasoner) {
openAiMessages = convertToR1Format([{ role: "user", content: systemPrompt }, ...messages])
}
if (isO3Mini) {
openAiMessages = [{ role: "developer", content: systemPrompt }, ...convertToOpenAiMessages(messages)]
temperature = undefined // does not support temperature
reasoningEffort = (this.options.o3MiniReasoningEffort as ChatCompletionReasoningEffort) || "medium"
}
const stream = await this.client.chat.completions.create({
model: this.options.openAiModelId ?? "",
model: modelId,
messages: openAiMessages,
temperature: 0,
temperature,
reasoning_effort: reasoningEffort,
stream: true,
stream_options: { include_usage: true },
})
@@ -46,6 +69,14 @@ export class OpenAiHandler implements ApiHandler {
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",
@@ -59,7 +90,7 @@ export class OpenAiHandler implements ApiHandler {
getModel(): { id: string; info: ModelInfo } {
return {
id: this.options.openAiModelId ?? "",
info: openAiModelInfoSaneDefaults,
info: this.options.openAiModelInfo ?? openAiModelInfoSaneDefaults,
}
}
}
+83 -122
View File
@@ -1,15 +1,18 @@
import { Anthropic } from "@anthropic-ai/sdk"
import axios from "axios"
import { setTimeout as setTimeoutPromise } from "node:timers/promises"
import OpenAI from "openai"
import { ApiHandler } from "../"
import { ApiHandlerOptions, ModelInfo, openRouterDefaultModelId, openRouterDefaultModelInfo } from "../../shared/api"
import { convertToOpenAiMessages } from "../transform/openai-format"
import { ApiStream } from "../transform/stream"
import delay from "delay"
import { withRetry } from "../retry"
import { createOpenRouterStream } from "../transform/openrouter-stream"
import { ApiStream, ApiStreamUsageChunk } from "../transform/stream"
import { OpenRouterErrorResponse } from "./types"
export class OpenRouterHandler implements ApiHandler {
private options: ApiHandlerOptions
private client: OpenAI
lastGenerationId?: string
constructor(options: ApiHandlerOptions) {
this.options = options
@@ -23,110 +26,34 @@ export class OpenRouterHandler implements ApiHandler {
})
}
@withRetry()
async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream {
const model = this.getModel()
this.lastGenerationId = undefined
// Convert Anthropic messages to OpenAI format
const openAiMessages: OpenAI.Chat.ChatCompletionMessageParam[] = [
{ role: "system", content: systemPrompt },
...convertToOpenAiMessages(messages),
]
const stream = await createOpenRouterStream(
this.client,
systemPrompt,
messages,
this.getModel(),
this.options.o3MiniReasoningEffort,
this.options.thinkingBudgetTokens,
this.options.openRouterProviderSorting,
)
// prompt caching: https://openrouter.ai/docs/prompt-caching
// this is specifically for claude models (some models may 'support prompt caching' automatically without this)
switch (model.id) {
case "anthropic/claude-3.5-sonnet":
case "anthropic/claude-3.5-sonnet:beta":
case "anthropic/claude-3.5-sonnet-20240620":
case "anthropic/claude-3.5-sonnet-20240620:beta":
case "anthropic/claude-3-5-haiku":
case "anthropic/claude-3-5-haiku:beta":
case "anthropic/claude-3-5-haiku-20241022":
case "anthropic/claude-3-5-haiku-20241022:beta":
case "anthropic/claude-3-haiku":
case "anthropic/claude-3-haiku:beta":
case "anthropic/claude-3-opus":
case "anthropic/claude-3-opus:beta":
openAiMessages[0] = {
role: "system",
content: [
{
type: "text",
text: systemPrompt,
// @ts-ignore-next-line
cache_control: { type: "ephemeral" },
},
],
}
// Add cache_control to the last two user messages
// (note: this works because we only ever add one user message at a time, but if we added multiple we'd need to mark the user message before the last assistant message)
const lastTwoUserMessages = openAiMessages.filter((msg) => msg.role === "user").slice(-2)
lastTwoUserMessages.forEach((msg) => {
if (typeof msg.content === "string") {
msg.content = [{ type: "text", text: msg.content }]
}
if (Array.isArray(msg.content)) {
// NOTE: this is fine since env details will always be added at the end. but if it weren't there, and the user added a image_url type message, it would pop a text part before it and then move it after to the end.
let lastTextPart = msg.content.filter((part) => part.type === "text").pop()
if (!lastTextPart) {
lastTextPart = { type: "text", text: "..." }
msg.content.push(lastTextPart)
}
// @ts-ignore-next-line
lastTextPart["cache_control"] = { type: "ephemeral" }
}
})
break
default:
break
}
// Not sure how openrouter defaults max tokens when no value is provided, but the anthropic api requires this value and since they offer both 4096 and 8192 variants, we should ensure 8192.
// (models usually default to max tokens allowed)
let maxTokens: number | undefined
switch (model.id) {
case "anthropic/claude-3.5-sonnet":
case "anthropic/claude-3.5-sonnet:beta":
case "anthropic/claude-3.5-sonnet-20240620":
case "anthropic/claude-3.5-sonnet-20240620:beta":
case "anthropic/claude-3-5-haiku":
case "anthropic/claude-3-5-haiku:beta":
case "anthropic/claude-3-5-haiku-20241022":
case "anthropic/claude-3-5-haiku-20241022:beta":
maxTokens = 8_192
break
}
// Removes messages in the middle when close to context window limit. Should not be applied to models that support prompt caching since it would continuously break the cache.
let shouldApplyMiddleOutTransform = !model.info.supportsPromptCache
// except for deepseek (which we set supportsPromptCache to true for), where because the context window is so small our truncation algo might miss and we should use openrouter's middle-out transform as a fallback to ensure we don't exceed the context window (FIXME: once we have a more robust token estimator we should not rely on this)
if (model.id === "deepseek/deepseek-chat") {
shouldApplyMiddleOutTransform = true
}
// @ts-ignore-next-line
const stream = await this.client.chat.completions.create({
model: model.id,
max_tokens: maxTokens,
temperature: 0,
messages: openAiMessages,
stream: true,
transforms: shouldApplyMiddleOutTransform ? ["middle-out"] : undefined,
})
let genId: string | undefined
let didOutputUsage: boolean = false
for await (const chunk of stream) {
// openrouter returns an error object instead of the openai sdk throwing an error
if ("error" in chunk) {
const error = chunk.error as { message?: string; code?: number }
const error = chunk.error as OpenRouterErrorResponse["error"]
console.error(`OpenRouter API Error: ${error?.code} - ${error?.message}`)
throw new Error(`OpenRouter API Error ${error?.code}: ${error?.message}`)
// Include metadata in the error message if available
const metadataStr = error.metadata ? `\nMetadata: ${JSON.stringify(error.metadata, null, 2)}` : ""
throw new Error(`OpenRouter API Error ${error.code}: ${error.message}${metadataStr}`)
}
if (!genId && chunk.id) {
genId = chunk.id
if (!this.lastGenerationId && chunk.id) {
this.lastGenerationId = chunk.id
}
const delta = chunk.choices[0]?.delta
@@ -136,39 +63,76 @@ export class OpenRouterHandler implements ApiHandler {
text: delta.content,
}
}
// if (chunk.usage) {
// yield {
// type: "usage",
// inputTokens: chunk.usage.prompt_tokens || 0,
// outputTokens: chunk.usage.completion_tokens || 0,
// }
// }
// Reasoning tokens are returned separately from the content
if ("reasoning" in delta && delta.reasoning) {
yield {
type: "reasoning",
// @ts-ignore-next-line
reasoning: delta.reasoning,
}
}
if (!didOutputUsage && chunk.usage) {
yield {
type: "usage",
inputTokens: chunk.usage.prompt_tokens || 0,
outputTokens: chunk.usage.completion_tokens || 0,
// @ts-ignore-next-line
totalCost: chunk.usage.cost || 0,
}
didOutputUsage = true
}
}
await delay(500) // FIXME: necessary delay to ensure generation endpoint is ready
// Fallback to generation endpoint if usage chunk not returned
if (!didOutputUsage) {
const apiStreamUsage = await this.getApiStreamUsage()
if (apiStreamUsage) {
yield apiStreamUsage
}
}
}
async getApiStreamUsage(): Promise<ApiStreamUsageChunk | undefined> {
if (this.lastGenerationId) {
await setTimeoutPromise(500) // FIXME: necessary delay to ensure generation endpoint is ready
try {
const generationIterator = this.fetchGenerationDetails(this.lastGenerationId)
const generation = (await generationIterator.next()).value
// console.log("OpenRouter generation details:", generation)
return {
type: "usage",
// cacheWriteTokens: 0,
// cacheReadTokens: 0,
// openrouter generation endpoint fails often
inputTokens: generation?.native_tokens_prompt || 0,
outputTokens: generation?.native_tokens_completion || 0,
totalCost: generation?.total_cost || 0,
}
} catch (error) {
// ignore if fails
console.error("Error fetching OpenRouter generation details:", error)
}
}
return undefined
}
@withRetry({ maxRetries: 4, baseDelay: 250, maxDelay: 1000, retryAllErrors: true })
async *fetchGenerationDetails(genId: string) {
// console.log("Fetching generation details for:", genId)
try {
const response = await axios.get(`https://openrouter.ai/api/v1/generation?id=${genId}`, {
headers: {
Authorization: `Bearer ${this.options.openRouterApiKey}`,
},
timeout: 5_000, // this request hangs sometimes
timeout: 15_000, // this request hangs sometimes
})
const generation = response.data?.data
console.log("OpenRouter generation details:", response.data)
yield {
type: "usage",
// cacheWriteTokens: 0,
// cacheReadTokens: 0,
// openrouter generation endpoint fails often
inputTokens: generation?.native_tokens_prompt || 0,
outputTokens: generation?.native_tokens_completion || 0,
totalCost: generation?.total_cost || 0,
}
yield response.data?.data
} catch (error) {
// ignore if fails
console.error("Error fetching OpenRouter generation details:", error)
throw error
}
}
@@ -178,9 +142,6 @@ export class OpenRouterHandler implements ApiHandler {
if (modelId && modelInfo) {
return { id: modelId, info: modelInfo }
}
return {
id: openRouterDefaultModelId,
info: openRouterDefaultModelInfo,
}
return { id: openRouterDefaultModelId, info: openRouterDefaultModelInfo }
}
}
+99
View File
@@ -0,0 +1,99 @@
import { Anthropic } from "@anthropic-ai/sdk"
import OpenAI from "openai"
import { ApiHandler } from "../"
import {
ApiHandlerOptions,
ModelInfo,
mainlandQwenModels,
internationalQwenModels,
mainlandQwenDefaultModelId,
internationalQwenDefaultModelId,
MainlandQwenModelId,
InternationalQwenModelId,
} from "../../shared/api"
import { convertToOpenAiMessages } from "../transform/openai-format"
import { ApiStream } from "../transform/stream"
import { convertToR1Format } from "../transform/r1-format"
export class QwenHandler implements ApiHandler {
private options: ApiHandlerOptions
private client: OpenAI
constructor(options: ApiHandlerOptions) {
this.options = options
this.client = new OpenAI({
baseURL:
this.options.qwenApiLine === "china"
? "https://dashscope.aliyuncs.com/compatible-mode/v1"
: "https://dashscope-intl.aliyuncs.com/compatible-mode/v1",
apiKey: this.options.qwenApiKey,
})
}
getModel(): { id: MainlandQwenModelId | InternationalQwenModelId; info: ModelInfo } {
const modelId = this.options.apiModelId
// Branch based on API line to let poor typescript know what to do
if (this.options.qwenApiLine === "china") {
return {
id: (modelId as MainlandQwenModelId) ?? mainlandQwenDefaultModelId,
info: mainlandQwenModels[modelId as MainlandQwenModelId] ?? mainlandQwenModels[mainlandQwenDefaultModelId],
}
} else {
return {
id: (modelId as InternationalQwenModelId) ?? internationalQwenDefaultModelId,
info:
internationalQwenModels[modelId as InternationalQwenModelId] ??
internationalQwenModels[internationalQwenDefaultModelId],
}
}
}
async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream {
const model = this.getModel()
const isDeepseekReasoner = model.id.includes("deepseek-r1")
let openAiMessages: OpenAI.Chat.ChatCompletionMessageParam[] = [
{ role: "system", content: systemPrompt },
...convertToOpenAiMessages(messages),
]
if (isDeepseekReasoner) {
openAiMessages = convertToR1Format([{ role: "user", content: systemPrompt }, ...messages])
}
const stream = await this.client.chat.completions.create({
model: model.id,
max_completion_tokens: model.info.maxTokens,
messages: openAiMessages,
stream: true,
stream_options: { include_usage: true },
...(model.id === "deepseek-r1" ? {} : { temperature: 0 }),
})
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,
// @ts-ignore-next-line
cacheReadTokens: chunk.usage.prompt_cache_hit_tokens || 0,
// @ts-ignore-next-line
cacheWriteTokens: chunk.usage.prompt_cache_miss_tokens || 0,
}
}
}
}
}
+96
View File
@@ -0,0 +1,96 @@
import { Anthropic } from "@anthropic-ai/sdk"
import OpenAI from "openai"
import { ApiHandlerOptions, ModelInfo, openAiModelInfoSaneDefaults } from "../../shared/api"
import { ApiHandler } from "../index"
import { withRetry } from "../retry"
import { convertToOpenAiMessages } from "../transform/openai-format"
import { ApiStream } from "../transform/stream"
export class RequestyHandler implements ApiHandler {
private options: ApiHandlerOptions
private client: OpenAI
constructor(options: ApiHandlerOptions) {
this.options = options
this.client = new OpenAI({
baseURL: "https://router.requesty.ai/v1",
apiKey: this.options.requestyApiKey,
defaultHeaders: {
"HTTP-Referer": "https://cline.bot",
"X-Title": "Cline",
},
})
}
@withRetry()
async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream {
const modelId = this.options.requestyModelId ?? ""
let openAiMessages: OpenAI.Chat.ChatCompletionMessageParam[] = [
{ role: "system", content: systemPrompt },
...convertToOpenAiMessages(messages),
]
// @ts-ignore-next-line
const stream = await this.client.chat.completions.create({
model: modelId,
messages: openAiMessages,
temperature: 0,
stream: true,
stream_options: { include_usage: true },
...(modelId === "openai/o3-mini" ? { reasoning_effort: this.options.o3MiniReasoningEffort || "medium" } : {}),
})
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) || "",
}
}
// 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 (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 = 0 // TODO: Replace with calculateApiCostOpenAI(model.info, inputTokens, outputTokens, cacheWriteTokens, cacheReadTokens)
yield {
type: "usage",
inputTokens: inputTokens,
outputTokens: outputTokens,
cacheWriteTokens: cacheWriteTokens,
cacheReadTokens: cacheReadTokens,
totalCost: totalCost,
}
}
}
}
getModel(): { id: string; info: ModelInfo } {
return {
id: this.options.requestyModelId ?? "",
info: openAiModelInfoSaneDefaults,
}
}
}
+75
View File
@@ -0,0 +1,75 @@
import { Anthropic } from "@anthropic-ai/sdk"
import OpenAI from "openai"
import { withRetry } from "../retry"
import { ApiHandlerOptions, ModelInfo, SambanovaModelId, sambanovaDefaultModelId, sambanovaModels } from "../../shared/api"
import { ApiHandler } from "../index"
import { convertToOpenAiMessages } from "../transform/openai-format"
import { ApiStream } from "../transform/stream"
import { convertToR1Format } from "../transform/r1-format"
export class SambanovaHandler implements ApiHandler {
private options: ApiHandlerOptions
private client: OpenAI
constructor(options: ApiHandlerOptions) {
this.options = options
this.client = new OpenAI({
baseURL: "https://api.sambanova.ai/v1",
apiKey: this.options.sambanovaApiKey,
})
}
@withRetry()
async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream {
const model = this.getModel()
let openAiMessages: OpenAI.Chat.ChatCompletionMessageParam[] = [
{ role: "system", content: systemPrompt },
...convertToOpenAiMessages(messages),
]
const modelId = model.id.toLowerCase()
if (modelId.includes("deepseek") || modelId.includes("qwen") || modelId.includes("qwq")) {
openAiMessages = convertToR1Format([{ role: "user", content: systemPrompt }, ...messages])
}
const stream = await this.client.chat.completions.create({
model: this.getModel().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 (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 && modelId in sambanovaModels) {
const id = modelId as SambanovaModelId
return { id, info: sambanovaModels[id] }
}
return {
id: sambanovaDefaultModelId,
info: sambanovaModels[sambanovaDefaultModelId],
}
}
}
+75
View File
@@ -0,0 +1,75 @@
import { Anthropic } from "@anthropic-ai/sdk"
import OpenAI from "openai"
import { withRetry } from "../retry"
import { ApiHandlerOptions, ModelInfo, openAiModelInfoSaneDefaults } from "../../shared/api"
import { ApiHandler } from "../index"
import { convertToOpenAiMessages } from "../transform/openai-format"
import { ApiStream } from "../transform/stream"
import { convertToR1Format } from "../transform/r1-format"
export class TogetherHandler implements ApiHandler {
private options: ApiHandlerOptions
private client: OpenAI
constructor(options: ApiHandlerOptions) {
this.options = options
this.client = new OpenAI({
baseURL: "https://api.together.xyz/v1",
apiKey: this.options.togetherApiKey,
})
}
@withRetry()
async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream {
const modelId = this.options.togetherModelId ?? ""
const isDeepseekReasoner = modelId.includes("deepseek-reasoner")
let openAiMessages: OpenAI.Chat.ChatCompletionMessageParam[] = [
{ role: "system", content: systemPrompt },
...convertToOpenAiMessages(messages),
]
if (isDeepseekReasoner) {
openAiMessages = convertToR1Format([{ role: "user", content: systemPrompt }, ...messages])
}
const stream = await this.client.chat.completions.create({
model: modelId,
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 } {
return {
id: this.options.togetherModelId ?? "",
info: openAiModelInfoSaneDefaults,
}
}
}
+22
View File
@@ -0,0 +1,22 @@
// For the following openrouter error type sources, see the docs here:
// https://openrouter.ai/docs/api-reference/errors
export type OpenRouterErrorResponse = {
error: {
message: string
code: number
metadata?: OpenRouterProviderErrorMetadata | OpenRouterModerationErrorMetadata | Record<string, unknown>
}
}
export type OpenRouterProviderErrorMetadata = {
provider_name: string // The name of the provider that encountered the error
raw: unknown // The raw error from the provider
}
export type OpenRouterModerationErrorMetadata = {
reasons: string[] // Why your input was flagged
flagged_input: string // The text segment that was flagged, limited to 100 characters. If the flagged input is longer than 100 characters, it will be truncated in the middle and replaced with ...
provider_name: string // The name of the provider that requested moderation
model_slug: string
}
+239 -45
View File
@@ -1,76 +1,270 @@
import { Anthropic } from "@anthropic-ai/sdk"
import { AnthropicVertex } from "@anthropic-ai/vertex-sdk"
import { withRetry } from "../retry"
import { ApiHandler } from "../"
import { ApiHandlerOptions, ModelInfo, vertexDefaultModelId, VertexModelId, vertexModels } from "../../shared/api"
import { ApiStream } from "../transform/stream"
import { VertexAI } from "@google-cloud/vertexai"
// https://docs.anthropic.com/en/api/claude-on-vertex-ai
export class VertexHandler implements ApiHandler {
private options: ApiHandlerOptions
private client: AnthropicVertex
private clientAnthropic: AnthropicVertex
private clientVertex: VertexAI
constructor(options: ApiHandlerOptions) {
this.options = options
this.client = new AnthropicVertex({
this.clientAnthropic = new AnthropicVertex({
projectId: this.options.vertexProjectId,
// https://cloud.google.com/vertex-ai/generative-ai/docs/partner-models/use-claude#regions
region: this.options.vertexRegion,
})
this.clientVertex = new VertexAI({
project: this.options.vertexProjectId,
location: this.options.vertexRegion,
})
}
@withRetry()
async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream {
const stream = await this.client.messages.create({
model: this.getModel().id,
max_tokens: this.getModel().info.maxTokens || 8192,
temperature: 0,
system: systemPrompt,
messages,
stream: true,
})
for await (const chunk of stream) {
switch (chunk.type) {
case "message_start":
const usage = chunk.message.usage
yield {
type: "usage",
inputTokens: usage.input_tokens || 0,
outputTokens: usage.output_tokens || 0,
}
break
case "message_delta":
yield {
type: "usage",
inputTokens: 0,
outputTokens: chunk.usage.output_tokens || 0,
}
break
const model = this.getModel()
const modelId = model.id
case "content_block_start":
switch (chunk.content_block.type) {
case "text":
if (chunk.index > 0) {
if (modelId.includes("claude")) {
let budget_tokens = this.options.thinkingBudgetTokens || 0
const reasoningOn = modelId.includes("3-7") && budget_tokens !== 0 ? true : false
let stream
switch (modelId) {
case "claude-3-7-sonnet@20250219":
case "claude-3-5-sonnet-v2@20241022":
case "claude-3-5-sonnet@20240620":
case "claude-3-5-haiku@20241022":
case "claude-3-opus@20240229":
case "claude-3-haiku@20240307": {
// Find indices of user messages for cache control
const userMsgIndices = messages.reduce(
(acc, msg, index) => (msg.role === "user" ? [...acc, index] : acc),
[] as number[],
)
const lastUserMsgIndex = userMsgIndices[userMsgIndices.length - 1] ?? -1
const secondLastMsgUserIndex = userMsgIndices[userMsgIndices.length - 2] ?? -1
stream = await this.clientAnthropic.beta.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",
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,
cache_control: {
type: "ephemeral",
},
},
]
: message.content.map((content, contentIndex) =>
contentIndex === message.content.length - 1
? {
...content,
cache_control: {
type: "ephemeral",
},
}
: content,
),
}
}
return {
...message,
content:
typeof message.content === "string"
? [
{
type: "text",
text: message.content,
},
]
: message.content,
}
}),
stream: true,
},
{
headers: {},
},
)
break
}
default: {
stream = await this.clientAnthropic.beta.messages.create({
model: modelId,
max_tokens: model.info.maxTokens || 8192,
temperature: 0,
system: [
{
text: systemPrompt,
type: "text",
},
],
messages: messages.map((message) => ({
...message,
content:
typeof message.content === "string"
? [
{
type: "text",
text: message.content,
},
]
: message.content,
})),
stream: true,
})
break
}
}
for await (const chunk of stream) {
switch (chunk.type) {
case "message_start":
const usage = chunk.message.usage
yield {
type: "usage",
inputTokens: usage.input_tokens || 0,
outputTokens: usage.output_tokens || 0,
cacheWriteTokens: usage.cache_creation_input_tokens || undefined,
cacheReadTokens: usage.cache_read_input_tokens || undefined,
}
break
case "message_delta":
yield {
type: "usage",
inputTokens: 0,
outputTokens: chunk.usage.output_tokens || 0,
}
break
case "message_stop":
break
case "content_block_start":
switch (chunk.content_block.type) {
case "thinking":
yield {
type: "reasoning",
reasoning: chunk.content_block.thinking || "",
}
break
case "redacted_thinking":
// Handle redacted thinking blocks - we still mark it as reasoning
// but note that the content is encrypted
yield {
type: "reasoning",
reasoning: "[Redacted thinking block]",
}
break
case "text":
if (chunk.index > 0) {
yield {
type: "text",
text: "\n",
}
}
yield {
type: "text",
text: "\n",
text: chunk.content_block.text,
}
break
}
break
case "content_block_delta":
switch (chunk.delta.type) {
case "thinking_delta":
yield {
type: "reasoning",
reasoning: chunk.delta.thinking,
}
break
case "text_delta":
yield {
type: "text",
text: chunk.delta.text,
}
break
}
break
case "content_block_stop":
break
}
}
} else {
// gemini
const generativeModel = this.clientVertex.getGenerativeModel({
model: this.getModel().id,
systemInstruction: {
role: "system",
parts: [{ text: systemPrompt }],
},
})
const request = {
contents: [
{
role: "user",
parts: messages.map((m) => {
if (typeof m.content === "string") {
return { text: m.content }
} else if (Array.isArray(m.content)) {
return {
text: m.content
.map((block) => {
if (typeof block === "string") {
return block
} else if (block.type === "text") {
return block.text
} else {
console.log("Unsupported block type", block)
return ""
}
})
.join(" "),
}
} else {
return { text: "" }
}
}),
},
],
}
const streamingResult = await generativeModel.generateContentStream(request)
for await (const chunk of streamingResult.stream) {
// If usage data is available, yield it similarly:
// yield { type: "usage", inputTokens: 0, outputTokens: 0 }
// Otherwise, just yield text:
const candidates = chunk.candidates || []
for (const candidate of candidates) {
for (const part of candidate.content?.parts || []) {
if (part.text) {
yield {
type: "text",
text: chunk.content_block.text,
text: part.text,
}
break
}
}
break
case "content_block_delta":
switch (chunk.delta.type) {
case "text_delta":
yield {
type: "text",
text: chunk.delta.text,
}
break
}
break
}
}
}
}
+2 -2
View File
@@ -1,7 +1,7 @@
import { Anthropic } from "@anthropic-ai/sdk"
import * as vscode from "vscode"
import { ApiHandler, SingleCompletionHandler } from "../"
import { calculateApiCost } from "../../utils/cost"
import { calculateApiCostAnthropic } from "../../utils/cost"
import { ApiStream } from "../transform/stream"
import { convertToVsCodeLmMessages } from "../transform/vscode-lm-format"
import { SELECTOR_SEPARATOR, stringifyVsCodeLmModelSelector } from "../../shared/vsCodeSelectorUtils"
@@ -525,7 +525,7 @@ export class VsCodeLmHandler implements ApiHandler, SingleCompletionHandler {
type: "usage",
inputTokens: totalInputTokens,
outputTokens: totalOutputTokens,
totalCost: calculateApiCost(this.getModel().info, totalInputTokens, totalOutputTokens),
totalCost: calculateApiCostAnthropic(this.getModel().info, totalInputTokens, totalOutputTokens),
}
} catch (error: unknown) {
this.ensureCleanState()
+64
View File
@@ -0,0 +1,64 @@
import { Anthropic } from "@anthropic-ai/sdk"
import OpenAI from "openai"
import { ApiHandler } from "../"
import { ApiHandlerOptions, XAIModelId, ModelInfo, xaiDefaultModelId, xaiModels } from "../../shared/api"
import { convertToOpenAiMessages } from "../transform/openai-format"
import { ApiStream } from "../transform/stream"
export class XAIHandler implements ApiHandler {
private options: ApiHandlerOptions
private client: OpenAI
constructor(options: ApiHandlerOptions) {
this.options = options
this.client = new OpenAI({
baseURL: "https://api.x.ai/v1",
apiKey: this.options.xaiApiKey,
})
}
async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream {
const stream = await this.client.chat.completions.create({
model: this.getModel().id,
max_completion_tokens: this.getModel().info.maxTokens,
temperature: 0,
messages: [{ role: "system", content: systemPrompt }, ...convertToOpenAiMessages(messages)],
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 (chunk.usage) {
yield {
type: "usage",
inputTokens: 0,
outputTokens: chunk.usage.completion_tokens || 0,
// @ts-ignore-next-line
cacheReadTokens: chunk.usage.prompt_cache_hit_tokens || 0,
// @ts-ignore-next-line
cacheWriteTokens: chunk.usage.prompt_cache_miss_tokens || 0,
}
}
}
}
getModel(): { id: XAIModelId; info: ModelInfo } {
const modelId = this.options.apiModelId
if (modelId && modelId in xaiModels) {
const id = modelId as XAIModelId
return { id, info: xaiModels[id] }
}
return {
id: xaiDefaultModelId,
info: xaiModels[xaiDefaultModelId],
}
}
}
+216
View File
@@ -0,0 +1,216 @@
import { describe, it } from "mocha"
import "should"
import { withRetry } from "./retry"
describe("Retry Decorator", () => {
describe("withRetry", () => {
it("should not retry on success", async () => {
let callCount = 0
class TestClass {
@withRetry()
async *successMethod() {
callCount++
yield "success"
}
}
const test = new TestClass()
const result = []
for await (const value of test.successMethod()) {
result.push(value)
}
callCount.should.equal(1)
result.should.deepEqual(["success"])
})
it("should retry on rate limit (429) error", async () => {
let callCount = 0
class TestClass {
@withRetry({ maxRetries: 2, baseDelay: 10, maxDelay: 100 })
async *failMethod() {
callCount++
if (callCount === 1) {
const error: any = new Error("Rate limit exceeded")
error.status = 429
throw error
}
yield "success after retry"
}
}
const test = new TestClass()
const result = []
for await (const value of test.failMethod()) {
result.push(value)
}
callCount.should.equal(2)
result.should.deepEqual(["success after retry"])
})
it("should not retry on non-rate-limit errors", async () => {
let callCount = 0
class TestClass {
@withRetry()
async *failMethod() {
callCount++
throw new Error("Regular error")
}
}
const test = new TestClass()
try {
for await (const _ of test.failMethod()) {
// Should not reach here
}
throw new Error("Should have thrown")
} catch (error: any) {
error.message.should.equal("Regular error")
callCount.should.equal(1)
}
})
it("should respect retry-after header with delta seconds", async () => {
let callCount = 0
const startTime = Date.now()
class TestClass {
@withRetry({ maxRetries: 2, baseDelay: 1000 }) // Use large baseDelay to ensure header takes precedence
async *failMethod() {
callCount++
if (callCount === 1) {
const error: any = new Error("Rate limit exceeded")
error.status = 429
error.headers = { "retry-after": "0.01" } // 10ms delay
throw error
}
yield "success after retry"
}
}
const test = new TestClass()
const result = []
for await (const value of test.failMethod()) {
result.push(value)
}
const duration = Date.now() - startTime
duration.should.be.approximately(10, 10) // Allow 10ms variance
callCount.should.equal(2)
result.should.deepEqual(["success after retry"])
})
it("should respect retry-after header with Unix timestamp", async () => {
let callCount = 0
const startTime = Date.now()
const retryTimestamp = Math.floor(Date.now() / 1000) + 0.01 // 10ms in the future
class TestClass {
@withRetry({ maxRetries: 2, baseDelay: 1000 }) // Use large baseDelay to ensure header takes precedence
async *failMethod() {
callCount++
if (callCount === 1) {
const error: any = new Error("Rate limit exceeded")
error.status = 429
error.headers = { "retry-after": retryTimestamp.toString() }
throw error
}
yield "success after retry"
}
}
const test = new TestClass()
const result = []
for await (const value of test.failMethod()) {
result.push(value)
}
const duration = Date.now() - startTime
duration.should.be.approximately(10, 10) // Allow 10ms variance
callCount.should.equal(2)
result.should.deepEqual(["success after retry"])
})
it("should use exponential backoff when no retry-after header", async () => {
let callCount = 0
const startTime = Date.now()
class TestClass {
@withRetry({ maxRetries: 2, baseDelay: 10, maxDelay: 100 })
async *failMethod() {
callCount++
if (callCount === 1) {
const error: any = new Error("Rate limit exceeded")
error.status = 429
throw error
}
yield "success after retry"
}
}
const test = new TestClass()
const result = []
for await (const value of test.failMethod()) {
result.push(value)
}
const duration = Date.now() - startTime
// First retry should be after baseDelay (10ms)
duration.should.be.approximately(10, 10)
callCount.should.equal(2)
result.should.deepEqual(["success after retry"])
})
it("should respect maxDelay", async () => {
let callCount = 0
const startTime = Date.now()
class TestClass {
@withRetry({ maxRetries: 3, baseDelay: 50, maxDelay: 10 })
async *failMethod() {
callCount++
if (callCount < 3) {
const error: any = new Error("Rate limit exceeded")
error.status = 429
throw error
}
yield "success after retries"
}
}
const test = new TestClass()
const result = []
for await (const value of test.failMethod()) {
result.push(value)
}
const duration = Date.now() - startTime
// Both retries should be capped at maxDelay (10ms each)
duration.should.be.approximately(20, 20)
callCount.should.equal(3)
result.should.deepEqual(["success after retries"])
})
it("should throw after maxRetries attempts", async () => {
let callCount = 0
class TestClass {
@withRetry({ maxRetries: 2, baseDelay: 10 })
async *failMethod() {
callCount++
const error: any = new Error("Rate limit exceeded")
error.status = 429
throw error
}
}
const test = new TestClass()
try {
for await (const _ of test.failMethod()) {
// Should not reach here
}
throw new Error("Should have thrown")
} catch (error: any) {
error.message.should.equal("Rate limit exceeded")
callCount.should.equal(2) // Initial attempt + 1 retry
}
})
})
})
+64
View File
@@ -0,0 +1,64 @@
interface RetryOptions {
maxRetries?: number
baseDelay?: number
maxDelay?: number
retryAllErrors?: boolean
}
const DEFAULT_OPTIONS: Required<RetryOptions> = {
maxRetries: 3,
baseDelay: 1_000,
maxDelay: 10_000,
retryAllErrors: false,
}
export function withRetry(options: RetryOptions = {}) {
const { maxRetries, baseDelay, maxDelay, retryAllErrors } = { ...DEFAULT_OPTIONS, ...options }
return function (_target: any, _propertyKey: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value
descriptor.value = async function* (...args: any[]) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
yield* originalMethod.apply(this, args)
return
} catch (error: any) {
const isRateLimit = error?.status === 429
const isLastAttempt = attempt === maxRetries - 1
if ((!isRateLimit && !retryAllErrors) || isLastAttempt) {
throw error
}
// Get retry delay from header or calculate exponential backoff
// Check various rate limit headers
const retryAfter =
error.headers?.["retry-after"] ||
error.headers?.["x-ratelimit-reset"] ||
error.headers?.["ratelimit-reset"]
let delay: number
if (retryAfter) {
// Handle both delta-seconds and Unix timestamp formats
const retryValue = parseInt(retryAfter, 10)
if (retryValue > Date.now() / 1000) {
// Unix timestamp
delay = retryValue * 1000 - Date.now()
} else {
// Delta seconds
delay = retryValue * 1000
}
} else {
// Use exponential backoff if no header
delay = Math.min(maxDelay, baseDelay * Math.pow(2, attempt))
}
await new Promise((resolve) => setTimeout(resolve, delay))
}
}
}
return descriptor
}
}
+5 -107
View File
@@ -1,26 +1,7 @@
import { Anthropic } from "@anthropic-ai/sdk"
import {
Content,
EnhancedGenerateContentResponse,
FunctionCallPart,
FunctionDeclaration,
FunctionResponsePart,
InlineDataPart,
Part,
SchemaType,
TextPart,
} from "@google/generative-ai"
import { Content, EnhancedGenerateContentResponse, InlineDataPart, Part, TextPart } from "@google/generative-ai"
export function convertAnthropicContentToGemini(
content:
| string
| Array<
| Anthropic.Messages.TextBlockParam
| Anthropic.Messages.ImageBlockParam
| Anthropic.Messages.ToolUseBlockParam
| Anthropic.Messages.ToolResultBlockParam
>,
): Part[] {
export function convertAnthropicContentToGemini(content: string | Anthropic.ContentBlockParam[]): Part[] {
if (typeof content === "string") {
return [{ text: content } as TextPart]
}
@@ -38,55 +19,6 @@ export function convertAnthropicContentToGemini(
mimeType: block.source.media_type,
},
} as InlineDataPart
case "tool_use":
return {
functionCall: {
name: block.name,
args: block.input,
},
} as FunctionCallPart
case "tool_result":
const name = block.tool_use_id.split("-")[0]
if (!block.content) {
return []
}
if (typeof block.content === "string") {
return {
functionResponse: {
name,
response: {
name,
content: block.content,
},
},
} as FunctionResponsePart
} else {
// The only case when tool_result could be array is when the tool failed and we're providing ie user feedback potentially with images
const textParts = block.content.filter((part) => part.type === "text")
const imageParts = block.content.filter((part) => part.type === "image")
const text = textParts.length > 0 ? textParts.map((part) => part.text).join("\n\n") : ""
const imageText = imageParts.length > 0 ? "\n\n(See next part for image)" : ""
return [
{
functionResponse: {
name,
response: {
name,
content: text + imageText,
},
},
} as FunctionResponsePart,
...imageParts.map(
(part) =>
({
inlineData: {
data: part.source.data,
mimeType: part.source.media_type,
},
}) as InlineDataPart,
),
]
}
default:
throw new Error(`Unsupported content block type: ${(block as any).type}`)
}
@@ -100,26 +32,6 @@ export function convertAnthropicMessageToGemini(message: Anthropic.Messages.Mess
}
}
export function convertAnthropicToolToGemini(tool: Anthropic.Messages.Tool): FunctionDeclaration {
return {
name: tool.name,
description: tool.description || "",
parameters: {
type: SchemaType.OBJECT,
properties: Object.fromEntries(
Object.entries(tool.input_schema.properties || {}).map(([key, value]) => [
key,
{
type: (value as any).type.toUpperCase(),
description: (value as any).description || "",
},
]),
),
required: (tool.input_schema.required as string[]) || [],
},
}
}
/*
It looks like gemini likes to double escape certain characters when writing file contents: https://discuss.ai.google.dev/t/function-call-string-property-is-double-escaped/37867
*/
@@ -133,23 +45,7 @@ export function convertGeminiResponseToAnthropic(response: EnhancedGenerateConte
// Add the main text response
const text = response.text()
if (text) {
content.push({ type: "text", text })
}
// Add function calls as tool_use blocks
const functionCalls = response.functionCalls()
if (functionCalls) {
functionCalls.forEach((call, index) => {
if ("content" in call.args && typeof call.args.content === "string") {
call.args.content = unescapeGeminiContent(call.args.content)
}
content.push({
type: "tool_use",
id: `${call.name}-${index}-${Date.now()}`,
name: call.name,
input: call.args,
})
})
content.push({ type: "text", text, citations: null })
}
// Determine stop reason
@@ -183,6 +79,8 @@ export function convertGeminiResponseToAnthropic(response: EnhancedGenerateConte
usage: {
input_tokens: response.usageMetadata?.promptTokenCount ?? 0,
output_tokens: response.usageMetadata?.candidatesTokenCount ?? 0,
cache_creation_input_tokens: null,
cache_read_input_tokens: null,
},
}
}
+14 -45
View File
@@ -1,5 +1,4 @@
import { Anthropic } from "@anthropic-ai/sdk"
import { Mistral } from "@mistralai/mistralai"
import { AssistantMessage } from "@mistralai/mistralai/models/components/assistantmessage"
import { SystemMessage } from "@mistralai/mistralai/models/components/systemmessage"
import { ToolMessage } from "@mistralai/mistralai/models/components/toolmessage"
@@ -21,25 +20,15 @@ export function convertToMistralMessages(anthropicMessages: Anthropic.Messages.M
})
} else {
if (anthropicMessage.role === "user") {
const { nonToolMessages, toolMessages } = anthropicMessage.content.reduce<{
nonToolMessages: (Anthropic.TextBlockParam | Anthropic.ImageBlockParam)[]
toolMessages: Anthropic.ToolResultBlockParam[]
}>(
(acc, part) => {
if (part.type === "tool_result") {
acc.toolMessages.push(part)
} else if (part.type === "text" || part.type === "image") {
acc.nonToolMessages.push(part)
} // user cannot send tool_use messages
return acc
},
{ nonToolMessages: [], toolMessages: [] },
// Filter to only include text and image blocks
const textAndImageBlocks = anthropicMessage.content.filter(
(part) => part.type === "text" || part.type === "image",
)
if (nonToolMessages.length > 0) {
if (textAndImageBlocks.length > 0) {
mistralMessages.push({
role: "user",
content: nonToolMessages.map((part) => {
content: textAndImageBlocks.map((part) => {
if (part.type === "image") {
return {
type: "image_url",
@@ -53,37 +42,17 @@ export function convertToMistralMessages(anthropicMessages: Anthropic.Messages.M
})
}
} else if (anthropicMessage.role === "assistant") {
const { nonToolMessages, toolMessages } = anthropicMessage.content.reduce<{
nonToolMessages: (Anthropic.TextBlockParam | Anthropic.ImageBlockParam)[]
toolMessages: Anthropic.ToolUseBlockParam[]
}>(
(acc, part) => {
if (part.type === "tool_use") {
acc.toolMessages.push(part)
} else if (part.type === "text" || part.type === "image") {
acc.nonToolMessages.push(part)
} // assistant cannot send tool_result messages
return acc
},
{ nonToolMessages: [], toolMessages: [] },
)
// Only process text blocks - assistant cannot send images or other content types in Mistral's API format
const textBlocks = anthropicMessage.content.filter((part) => part.type === "text")
let content: string | undefined
if (nonToolMessages.length > 0) {
content = nonToolMessages
.map((part) => {
if (part.type === "image") {
return "" // impossible as the assistant cannot send images
}
return part.text
})
.join("\n")
if (textBlocks.length > 0) {
const content = textBlocks.map((part) => part.text).join("\n")
mistralMessages.push({
role: "assistant",
content,
})
}
mistralMessages.push({
role: "assistant",
content,
})
}
}
}
+3
View File
@@ -376,6 +376,7 @@ export function convertO1ResponseToAnthropicMessage(
{
type: "text",
text: normalText,
citations: null,
},
],
model: completion.model,
@@ -396,6 +397,8 @@ export function convertO1ResponseToAnthropicMessage(
usage: {
input_tokens: completion.usage?.prompt_tokens || 0,
output_tokens: completion.usage?.completion_tokens || 0,
cache_creation_input_tokens: null,
cache_read_input_tokens: null,
},
}
+109
View File
@@ -0,0 +1,109 @@
import { Anthropic } from "@anthropic-ai/sdk"
import { Message } from "ollama"
export function convertToOllamaMessages(anthropicMessages: Anthropic.Messages.MessageParam[]): Message[] {
const ollamaMessages: Message[] = []
for (const anthropicMessage of anthropicMessages) {
if (typeof anthropicMessage.content === "string") {
ollamaMessages.push({
role: anthropicMessage.role,
content: anthropicMessage.content,
})
} else {
if (anthropicMessage.role === "user") {
const { nonToolMessages, toolMessages } = anthropicMessage.content.reduce<{
nonToolMessages: (Anthropic.TextBlockParam | Anthropic.ImageBlockParam)[]
toolMessages: Anthropic.ToolResultBlockParam[]
}>(
(acc, part) => {
if (part.type === "tool_result") {
acc.toolMessages.push(part)
} else if (part.type === "text" || part.type === "image") {
acc.nonToolMessages.push(part)
}
return acc
},
{ nonToolMessages: [], toolMessages: [] },
)
// Process tool result messages FIRST since they must follow the tool use messages
let toolResultImages: string[] = []
toolMessages.forEach((toolMessage) => {
// The Anthropic SDK allows tool results to be a string or an array of text and image blocks, enabling rich and structured content. In contrast, the Ollama SDK only supports tool results as a single string, so we map the Anthropic tool result parts into one concatenated string to maintain compatibility.
let content: string
if (typeof toolMessage.content === "string") {
content = toolMessage.content
} else {
content =
toolMessage.content
?.map((part) => {
if (part.type === "image") {
toolResultImages.push(`data:${part.source.media_type};base64,${part.source.data}`)
return "(see following user message for image)"
}
return part.text
})
.join("\n") ?? ""
}
ollamaMessages.push({
role: "user",
images: toolResultImages.length > 0 ? toolResultImages : undefined,
content: content,
})
})
// Process non-tool messages
if (nonToolMessages.length > 0) {
ollamaMessages.push({
role: "user",
content: nonToolMessages
.map((part) => {
if (part.type === "image") {
return `data:${part.source.media_type};base64,${part.source.data}`
}
return part.text
})
.join("\n"),
})
}
} else if (anthropicMessage.role === "assistant") {
const { nonToolMessages, toolMessages } = anthropicMessage.content.reduce<{
nonToolMessages: (Anthropic.TextBlockParam | Anthropic.ImageBlockParam)[]
toolMessages: Anthropic.ToolUseBlockParam[]
}>(
(acc, part) => {
if (part.type === "tool_use") {
acc.toolMessages.push(part)
} else if (part.type === "text" || part.type === "image") {
acc.nonToolMessages.push(part)
} // assistant cannot send tool_result messages
return acc
},
{ nonToolMessages: [], toolMessages: [] },
)
// Process non-tool messages
let content: string = ""
if (nonToolMessages.length > 0) {
content = nonToolMessages
.map((part) => {
if (part.type === "image") {
return "" // impossible as the assistant cannot send images
}
return part.text
})
.join("\n")
}
ollamaMessages.push({
role: "assistant",
content,
})
}
}
}
return ollamaMessages
}
+3
View File
@@ -161,6 +161,7 @@ export function convertToAnthropicMessage(completion: OpenAI.Chat.Completions.Ch
{
type: "text",
text: openAiMessage.content || "",
citations: null,
},
],
model: completion.model,
@@ -181,6 +182,8 @@ export function convertToAnthropicMessage(completion: OpenAI.Chat.Completions.Ch
usage: {
input_tokens: completion.usage?.prompt_tokens || 0,
output_tokens: completion.usage?.completion_tokens || 0,
cache_creation_input_tokens: null,
cache_read_input_tokens: null,
},
}
+154
View File
@@ -0,0 +1,154 @@
import { ModelInfo } from "../../shared/api"
import { convertToOpenAiMessages } from "./openai-format"
import { convertToR1Format } from "./r1-format"
import { ApiStream, ApiStreamChunk } from "./stream"
import { Anthropic } from "@anthropic-ai/sdk"
import OpenAI from "openai"
import { OpenRouterErrorResponse } from "../providers/types"
export async function createOpenRouterStream(
client: OpenAI,
systemPrompt: string,
messages: Anthropic.Messages.MessageParam[],
model: { id: string; info: ModelInfo },
o3MiniReasoningEffort?: string,
thinkingBudgetTokens?: number,
openRouterProviderSorting?: string,
) {
// Convert Anthropic messages to OpenAI format
let openAiMessages: OpenAI.Chat.ChatCompletionMessageParam[] = [
{ role: "system", content: systemPrompt },
...convertToOpenAiMessages(messages),
]
// prompt caching: https://openrouter.ai/docs/prompt-caching
// this is specifically for claude models (some models may 'support prompt caching' automatically without this)
switch (model.id) {
case "anthropic/claude-3.7-sonnet":
case "anthropic/claude-3.7-sonnet:beta":
case "anthropic/claude-3.7-sonnet:thinking":
case "anthropic/claude-3-7-sonnet":
case "anthropic/claude-3-7-sonnet:beta":
case "anthropic/claude-3.5-sonnet":
case "anthropic/claude-3.5-sonnet:beta":
case "anthropic/claude-3.5-sonnet-20240620":
case "anthropic/claude-3.5-sonnet-20240620:beta":
case "anthropic/claude-3-5-haiku":
case "anthropic/claude-3-5-haiku:beta":
case "anthropic/claude-3-5-haiku-20241022":
case "anthropic/claude-3-5-haiku-20241022:beta":
case "anthropic/claude-3-haiku":
case "anthropic/claude-3-haiku:beta":
case "anthropic/claude-3-opus":
case "anthropic/claude-3-opus:beta":
openAiMessages[0] = {
role: "system",
content: [
{
type: "text",
text: systemPrompt,
// @ts-ignore-next-line
cache_control: { type: "ephemeral" },
},
],
}
// Add cache_control to the last two user messages
// (note: this works because we only ever add one user message at a time, but if we added multiple we'd need to mark the user message before the last assistant message)
const lastTwoUserMessages = openAiMessages.filter((msg) => msg.role === "user").slice(-2)
lastTwoUserMessages.forEach((msg) => {
if (typeof msg.content === "string") {
msg.content = [{ type: "text", text: msg.content }]
}
if (Array.isArray(msg.content)) {
// NOTE: this is fine since env details will always be added at the end. but if it weren't there, and the user added a image_url type message, it would pop a text part before it and then move it after to the end.
let lastTextPart = msg.content.filter((part) => part.type === "text").pop()
if (!lastTextPart) {
lastTextPart = { type: "text", text: "..." }
msg.content.push(lastTextPart)
}
// @ts-ignore-next-line
lastTextPart["cache_control"] = { type: "ephemeral" }
}
})
break
default:
break
}
// Not sure how openrouter defaults max tokens when no value is provided, but the anthropic api requires this value and since they offer both 4096 and 8192 variants, we should ensure 8192.
// (models usually default to max tokens allowed)
let maxTokens: number | undefined
switch (model.id) {
case "anthropic/claude-3.7-sonnet":
case "anthropic/claude-3.7-sonnet:beta":
case "anthropic/claude-3.7-sonnet:thinking":
case "anthropic/claude-3-7-sonnet":
case "anthropic/claude-3-7-sonnet:beta":
case "anthropic/claude-3.5-sonnet":
case "anthropic/claude-3.5-sonnet:beta":
case "anthropic/claude-3.5-sonnet-20240620":
case "anthropic/claude-3.5-sonnet-20240620:beta":
case "anthropic/claude-3-5-haiku":
case "anthropic/claude-3-5-haiku:beta":
case "anthropic/claude-3-5-haiku-20241022":
case "anthropic/claude-3-5-haiku-20241022:beta":
maxTokens = 8_192
break
}
let temperature: number | undefined = 0
let topP: number | undefined = undefined
if (
model.id.startsWith("deepseek/deepseek-r1") ||
model.id === "perplexity/sonar-reasoning" ||
model.id === "qwen/qwq-32b:free" ||
model.id === "qwen/qwq-32b"
) {
// Recommended values from DeepSeek
temperature = 0.7
topP = 0.95
openAiMessages = convertToR1Format([{ role: "user", content: systemPrompt }, ...messages])
}
let reasoning: { max_tokens: number } | undefined = undefined
switch (model.id) {
case "anthropic/claude-3.7-sonnet":
case "anthropic/claude-3.7-sonnet:beta":
case "anthropic/claude-3.7-sonnet:thinking":
case "anthropic/claude-3-7-sonnet":
case "anthropic/claude-3-7-sonnet:beta":
let budget_tokens = thinkingBudgetTokens || 0
const reasoningOn = budget_tokens !== 0 ? true : false
if (reasoningOn) {
temperature = undefined // extended thinking does not support non-1 temperature
reasoning = { max_tokens: budget_tokens }
}
break
}
// Removes messages in the middle when close to context window limit. Should not be applied to models that support prompt caching since it would continuously break the cache.
let shouldApplyMiddleOutTransform = !model.info.supportsPromptCache
// except for deepseek (which we set supportsPromptCache to true for), where because the context window is so small our truncation algo might miss and we should use openrouter's middle-out transform as a fallback to ensure we don't exceed the context window (FIXME: once we have a more robust token estimator we should not rely on this)
if (model.id === "deepseek/deepseek-chat") {
shouldApplyMiddleOutTransform = true
}
// @ts-ignore-next-line
const stream = await client.chat.completions.create({
model: model.id,
max_tokens: maxTokens,
temperature: temperature,
top_p: topP,
messages: openAiMessages,
stream: true,
stream_options: { include_usage: true },
transforms: shouldApplyMiddleOutTransform ? ["middle-out"] : undefined,
include_reasoning: true,
...(model.id === "openai/o3-mini" ? { reasoning_effort: o3MiniReasoningEffort || "medium" } : {}),
...(reasoning ? { reasoning } : {}),
...(openRouterProviderSorting ? { provider: { sort: openRouterProviderSorting } } : {}),
})
return stream
}
+92
View File
@@ -0,0 +1,92 @@
import { Anthropic } from "@anthropic-ai/sdk"
import OpenAI from "openai"
/**
* Converts Anthropic messages to OpenAI format and merges consecutive messages with the same role.
* This is required for DeepSeek Reasoner which does not support successive messages with the same role.
* DeepSeek highly recommends using 'user' role instead of 'system' role for optimal performance.
*
* @param messages Array of Anthropic messages
* @returns Array of OpenAI messages where consecutive messages with the same role are merged together
*/
export function convertToR1Format(messages: Anthropic.Messages.MessageParam[]): OpenAI.Chat.ChatCompletionMessageParam[] {
return messages.reduce<OpenAI.Chat.ChatCompletionMessageParam[]>((merged, message) => {
const lastMessage = merged[merged.length - 1]
let messageContent: string | (OpenAI.Chat.ChatCompletionContentPartText | OpenAI.Chat.ChatCompletionContentPartImage)[] =
""
let hasImages = false
if (Array.isArray(message.content)) {
const textParts: string[] = []
const imageParts: OpenAI.Chat.ChatCompletionContentPartImage[] = []
message.content.forEach((part) => {
if (part.type === "text") {
textParts.push(part.text)
}
if (part.type === "image") {
hasImages = true
imageParts.push({
type: "image_url",
image_url: { url: `data:${part.source.media_type};base64,${part.source.data}` },
})
}
})
if (hasImages) {
const parts: (OpenAI.Chat.ChatCompletionContentPartText | OpenAI.Chat.ChatCompletionContentPartImage)[] = []
if (textParts.length > 0) {
parts.push({ type: "text", text: textParts.join("\n") })
}
parts.push(...imageParts)
messageContent = parts
} else {
messageContent = textParts.join("\n")
}
} else {
messageContent = message.content
}
// If the last message has the same role, merge the content
if (lastMessage?.role === message.role) {
if (typeof lastMessage.content === "string" && typeof messageContent === "string") {
lastMessage.content += `\n${messageContent}`
} else {
const lastContent = Array.isArray(lastMessage.content)
? lastMessage.content
: [{ type: "text" as const, text: lastMessage.content || "" }]
const newContent = Array.isArray(messageContent)
? messageContent
: [{ type: "text" as const, text: messageContent }]
if (message.role === "assistant") {
const mergedContent = [
...lastContent,
...newContent,
] as OpenAI.Chat.ChatCompletionAssistantMessageParam["content"]
lastMessage.content = mergedContent
} else {
const mergedContent = [...lastContent, ...newContent] as OpenAI.Chat.ChatCompletionUserMessageParam["content"]
lastMessage.content = mergedContent
}
}
} else {
// Adds new message with the correct type based on role
if (message.role === "assistant") {
const newMessage: OpenAI.Chat.ChatCompletionAssistantMessageParam = {
role: "assistant",
content: messageContent as OpenAI.Chat.ChatCompletionAssistantMessageParam["content"],
}
merged.push(newMessage)
} else {
const newMessage: OpenAI.Chat.ChatCompletionUserMessageParam = {
role: "user",
content: messageContent as OpenAI.Chat.ChatCompletionUserMessageParam["content"],
}
merged.push(newMessage)
}
}
return merged
}, [])
}
+6 -1
View File
@@ -1,11 +1,16 @@
export type ApiStream = AsyncGenerator<ApiStreamChunk>
export type ApiStreamChunk = ApiStreamTextChunk | ApiStreamUsageChunk
export type ApiStreamChunk = ApiStreamTextChunk | ApiStreamReasoningChunk | ApiStreamUsageChunk
export interface ApiStreamTextChunk {
type: "text"
text: string
}
export interface ApiStreamReasoningChunk {
type: "reasoning"
reasoning: string
}
export interface ApiStreamUsageChunk {
type: "usage"
inputTokens: number
+3
View File
@@ -175,6 +175,7 @@ export async function convertToAnthropicMessage(
return {
type: "text",
text: part.value,
citations: null,
}
}
@@ -195,6 +196,8 @@ export async function convertToAnthropicMessage(
usage: {
input_tokens: 0,
output_tokens: 0,
cache_creation_input_tokens: null,
cache_read_input_tokens: null,
},
}
}
+742 -305
View File
File diff suppressed because it is too large Load Diff
+1 -61
View File
@@ -45,6 +45,7 @@ export const toolParamNames = [
"arguments",
"uri",
"question",
"options",
"response",
"result",
] as const
@@ -58,64 +59,3 @@ export interface ToolUse {
params: Partial<Record<ToolParamName, string>>
partial: boolean
}
export interface ExecuteCommandToolUse extends ToolUse {
name: "execute_command"
// Pick<Record<ToolParamName, string>, "command"> makes "command" required, but Partial<> makes it optional
params: Partial<Pick<Record<ToolParamName, string>, "command" | "requires_approval">>
}
export interface ReadFileToolUse extends ToolUse {
name: "read_file"
params: Partial<Pick<Record<ToolParamName, string>, "path">>
}
export interface WriteToFileToolUse extends ToolUse {
name: "write_to_file"
params: Partial<Pick<Record<ToolParamName, string>, "path" | "content">>
}
export interface ReplaceInFileToolUse extends ToolUse {
name: "replace_in_file"
params: Partial<Pick<Record<ToolParamName, string>, "path" | "diff">>
}
export interface SearchFilesToolUse extends ToolUse {
name: "search_files"
params: Partial<Pick<Record<ToolParamName, string>, "path" | "regex" | "file_pattern">>
}
export interface ListFilesToolUse extends ToolUse {
name: "list_files"
params: Partial<Pick<Record<ToolParamName, string>, "path" | "recursive">>
}
export interface ListCodeDefinitionNamesToolUse extends ToolUse {
name: "list_code_definition_names"
params: Partial<Pick<Record<ToolParamName, string>, "path">>
}
export interface BrowserActionToolUse extends ToolUse {
name: "browser_action"
params: Partial<Pick<Record<ToolParamName, string>, "action" | "url" | "coordinate" | "text">>
}
export interface UseMcpToolToolUse extends ToolUse {
name: "use_mcp_tool"
params: Partial<Pick<Record<ToolParamName, string>, "server_name" | "tool_name" | "arguments">>
}
export interface AccessMcpResourceToolUse extends ToolUse {
name: "access_mcp_resource"
params: Partial<Pick<Record<ToolParamName, string>, "server_name" | "uri">>
}
export interface AskFollowupQuestionToolUse extends ToolUse {
name: "ask_followup_question"
params: Partial<Pick<Record<ToolParamName, string>, "question">>
}
export interface AttemptCompletionToolUse extends ToolUse {
name: "attempt_completion"
params: Partial<Pick<Record<ToolParamName, string>, "result" | "command">>
}
@@ -0,0 +1,155 @@
import { ContextManager } from "./ContextManager"
import { Anthropic } from "@anthropic-ai/sdk"
import { expect } from "chai"
describe("ContextManager", () => {
function createMessages(count: number): Anthropic.Messages.MessageParam[] {
const messages: Anthropic.Messages.MessageParam[] = []
messages.push({
role: "user",
content: "Initial task message",
})
let role: "user" | "assistant" = "assistant"
for (let i = 1; i < count; i++) {
messages.push({
role,
content: `Message ${i}`,
})
role = role === "user" ? "assistant" : "user"
}
return messages
}
describe("getNextTruncationRange", () => {
let contextManager: ContextManager
beforeEach(() => {
contextManager = new ContextManager()
})
it("first truncation with half keep", () => {
const messages = createMessages(11)
const result = contextManager.getNextTruncationRange(messages)
expect(result).to.deep.equal([1, 4])
})
it("first truncation with quarter keep", () => {
const messages = createMessages(11)
const result = contextManager.getNextTruncationRange(messages, undefined, "quarter")
expect(result).to.deep.equal([1, 6])
})
it("sequential truncation with half keep", () => {
const messages = createMessages(21)
const firstRange = contextManager.getNextTruncationRange(messages)
expect(firstRange).to.deep.equal([1, 10])
// Pass the previous range for sequential truncation
const secondRange = contextManager.getNextTruncationRange(messages, firstRange)
expect(secondRange).to.deep.equal([1, 14])
})
it("sequential truncation with quarter keep", () => {
const messages = createMessages(41)
const firstRange = contextManager.getNextTruncationRange(messages, undefined, "quarter")
const secondRange = contextManager.getNextTruncationRange(messages, firstRange, "quarter")
expect(secondRange[0]).to.equal(1)
expect(secondRange[1]).to.be.greaterThan(firstRange[1])
})
it("ensures the last message in range is a user message", () => {
const messages = createMessages(14)
const result = contextManager.getNextTruncationRange(messages)
// Check if the message at the end of range is a user message
const lastRemovedMessage = messages[result[1]]
expect(lastRemovedMessage.role).to.equal("user")
// Check if the next message after the range is an assistant message
const nextMessage = messages[result[1] + 1]
expect(nextMessage.role).to.equal("assistant")
})
it("handles small message arrays", () => {
const messages = createMessages(3)
const result = contextManager.getNextTruncationRange(messages)
expect(result).to.deep.equal([1, 0])
})
it("preserves the message structure when truncating", () => {
const messages = createMessages(20)
const result = contextManager.getNextTruncationRange(messages)
// Get messages after removing the range
const effectiveMessages = [...messages.slice(0, result[0]), ...messages.slice(result[1] + 1)]
// Check first message and alternating pattern
expect(effectiveMessages[0].role).to.equal("user")
for (let i = 1; i < effectiveMessages.length; i++) {
const expectedRole = i % 2 === 1 ? "assistant" : "user"
expect(effectiveMessages[i].role).to.equal(expectedRole)
}
})
})
describe("getTruncatedMessages", () => {
let contextManager: ContextManager
beforeEach(() => {
contextManager = new ContextManager()
})
it("returns original messages when no range is provided", () => {
const messages = createMessages(3)
const result = contextManager.getTruncatedMessages(messages, undefined)
expect(result).to.deep.equal(messages)
})
it("correctly removes messages in the specified range", () => {
const messages = createMessages(5)
const range: [number, number] = [1, 3]
const result = contextManager.getTruncatedMessages(messages, range)
expect(result).to.have.lengthOf(2)
expect(result[0]).to.deep.equal(messages[0])
expect(result[1]).to.deep.equal(messages[4])
})
it("works with a range that starts at the first message after task", () => {
const messages = createMessages(4)
const range: [number, number] = [1, 2]
const result = contextManager.getTruncatedMessages(messages, range)
expect(result).to.have.lengthOf(2)
expect(result[0]).to.deep.equal(messages[0])
expect(result[1]).to.deep.equal(messages[3])
})
it("correctly handles removing a range while preserving alternation pattern", () => {
const messages = createMessages(5)
const range: [number, number] = [1, 2]
const result = contextManager.getTruncatedMessages(messages, range)
expect(result).to.have.lengthOf(3)
expect(result[0]).to.deep.equal(messages[0])
expect(result[1]).to.deep.equal(messages[3])
expect(result[2]).to.deep.equal(messages[4])
expect(result[0].role).to.equal("user")
expect(result[1].role).to.equal("assistant")
expect(result[2].role).to.equal("user")
})
})
})
@@ -0,0 +1,120 @@
import { Anthropic } from "@anthropic-ai/sdk"
import { ClineApiReqInfo, ClineMessage } from "../../shared/ExtensionMessage"
import { ApiHandler } from "../../api"
import { OpenAiHandler } from "../../api/providers/openai"
export class ContextManager {
getNewContextMessagesAndMetadata(
apiConversationHistory: Anthropic.Messages.MessageParam[],
clineMessages: ClineMessage[],
api: ApiHandler,
conversationHistoryDeletedRange: [number, number] | undefined,
previousApiReqIndex: number,
) {
let updatedConversationHistoryDeletedRange = false
// If the previous API request's total token usage is close to the context window, truncate the conversation history to free up space for the new request
if (previousApiReqIndex >= 0) {
const previousRequest = clineMessages[previousApiReqIndex]
if (previousRequest && previousRequest.text) {
const { tokensIn, tokensOut, cacheWrites, cacheReads }: ClineApiReqInfo = JSON.parse(previousRequest.text)
const totalTokens = (tokensIn || 0) + (tokensOut || 0) + (cacheWrites || 0) + (cacheReads || 0)
let contextWindow = api.getModel().info.contextWindow || 128_000
// FIXME: hack to get anyone using openai compatible with deepseek to have the proper context window instead of the default 128k. We need a way for the user to specify the context window for models they input through openai compatible
if (api instanceof OpenAiHandler && api.getModel().id.toLowerCase().includes("deepseek")) {
contextWindow = 64_000
}
let maxAllowedSize: number
switch (contextWindow) {
case 64_000: // deepseek models
maxAllowedSize = contextWindow - 27_000
break
case 128_000: // most models
maxAllowedSize = contextWindow - 30_000
break
case 200_000: // claude models
maxAllowedSize = contextWindow - 40_000
break
default:
maxAllowedSize = Math.max(contextWindow - 40_000, contextWindow * 0.8) // for deepseek, 80% of 64k meant only ~10k buffer which was too small and resulted in users getting context window errors.
}
// This is the most reliable way to know when we're close to hitting the context window.
if (totalTokens >= maxAllowedSize) {
// Since the user may switch between models with different context windows, truncating half may not be enough (ie if switching from claude 200k to deepseek 64k, half truncation will only remove 100k tokens, but we need to remove much more)
// So if totalTokens/2 is greater than maxAllowedSize, we truncate 3/4 instead of 1/2
// FIXME: truncating the conversation in a way that is optimal for prompt caching AND takes into account multi-context window complexity is something we need to improve
const keep = totalTokens / 2 > maxAllowedSize ? "quarter" : "half"
// NOTE: it's okay that we overwriteConversationHistory in resume task since we're only ever removing the last user message and not anything in the middle which would affect this range
conversationHistoryDeletedRange = this.getNextTruncationRange(
apiConversationHistory,
conversationHistoryDeletedRange,
keep,
)
updatedConversationHistoryDeletedRange = true
}
}
}
// conversationHistoryDeletedRange is updated only when we're close to hitting the context window, so we don't continuously break the prompt cache
const truncatedConversationHistory = this.getTruncatedMessages(apiConversationHistory, conversationHistoryDeletedRange)
return {
conversationHistoryDeletedRange: conversationHistoryDeletedRange,
updatedConversationHistoryDeletedRange: updatedConversationHistoryDeletedRange,
truncatedConversationHistory: truncatedConversationHistory,
}
}
public getNextTruncationRange(
apiMessages: Anthropic.Messages.MessageParam[],
currentDeletedRange: [number, number] | undefined,
keep: "half" | "quarter",
): [number, number] {
// Since we always keep the first message, currentDeletedRange[0] will always be 1 (for now until we have a smarter truncation algorithm)
const rangeStartIndex = 1
const startOfRest = currentDeletedRange ? currentDeletedRange[1] + 1 : 1
let messagesToRemove: number
if (keep === "half") {
// Remove half of remaining user-assistant pairs
// We first calculate half of the messages then divide by 2 to get the number of pairs.
// After flooring, we multiply by 2 to get the number of messages.
// Note that this will also always be an even number.
messagesToRemove = Math.floor((apiMessages.length - startOfRest) / 4) * 2 // Keep even number
} else {
// Remove 3/4 of remaining user-assistant pairs
// We calculate 3/4ths of the messages then divide by 2 to get the number of pairs.
// After flooring, we multiply by 2 to get the number of messages.
// Note that this will also always be an even number.
messagesToRemove = Math.floor(((apiMessages.length - startOfRest) * 3) / 4 / 2) * 2
}
let rangeEndIndex = startOfRest + messagesToRemove - 1
// Make sure the last message being removed is a user message, so that the next message after the initial task message is an assistant message. This preservers the user-assistant-user-assistant structure.
// NOTE: anthropic format messages are always user-assistant-user-assistant, while openai format messages can have multiple user messages in a row (we use anthropic format throughout cline)
if (apiMessages[rangeEndIndex].role !== "user") {
rangeEndIndex -= 1
}
// this is an inclusive range that will be removed from the conversation history
return [rangeStartIndex, rangeEndIndex]
}
public getTruncatedMessages(
messages: Anthropic.Messages.MessageParam[],
deletedRange: [number, number] | undefined,
): Anthropic.Messages.MessageParam[] {
if (!deletedRange) {
return messages
}
const [start, end] = deletedRange
// the range is inclusive - both start and end indices and everything in between will be removed from the final result.
// NOTE: if you try to console log these, don't forget that logging a reference to an array may not provide the same result as logging a slice() snapshot of that array at that exact moment. The following DOES in fact include the latest assistant message.
return [...messages.slice(0, start), ...messages.slice(end + 1)]
}
}
@@ -0,0 +1,10 @@
export function checkIsOpenRouterContextWindowError(error: any): boolean {
return error.code === 400 && error.message?.includes("context length")
}
export function checkIsAnthropicContextWindowError(response: any): boolean {
return (
response?.error?.error?.type === "invalid_request_error" &&
response?.error?.error?.message?.includes("prompt is too long")
)
}
@@ -0,0 +1,245 @@
import { ClineIgnoreController } from "./ClineIgnoreController"
import fs from "fs/promises"
import path from "path"
import os from "os"
import { after, beforeEach, describe, it } from "mocha"
import "should"
describe("ClineIgnoreController", () => {
let tempDir: string
let controller: ClineIgnoreController
beforeEach(async () => {
// Create a temp directory for testing
tempDir = path.join(os.tmpdir(), `llm-test-${Date.now()}-${Math.random().toString(36).slice(2)}`)
await fs.mkdir(tempDir)
// Create default .clineignore file
await fs.writeFile(
path.join(tempDir, ".clineignore"),
[".env", "*.secret", "private/", "# This is a comment", "", "temp.*", "file-with-space-at-end.* ", "**/.git/**"].join(
"\n",
),
)
controller = new ClineIgnoreController(tempDir)
await controller.initialize()
})
after(async () => {
// Clean up temp directory
await fs.rm(tempDir, { recursive: true, force: true })
})
describe("Default Patterns", () => {
// it("should block access to common ignored files", async () => {
// const results = [
// controller.validateAccess(".env"),
// controller.validateAccess(".git/config"),
// controller.validateAccess("node_modules/package.json"),
// ]
// results.forEach((result) => result.should.be.false())
// })
it("should allow access to regular files", async () => {
const results = [
controller.validateAccess("src/index.ts"),
controller.validateAccess("README.md"),
controller.validateAccess("package.json"),
]
results.forEach((result) => result.should.be.true())
})
it("should block access to .clineignore file", async () => {
const result = controller.validateAccess(".clineignore")
result.should.be.false()
})
})
describe("Custom Patterns", () => {
it("should block access to custom ignored patterns", async () => {
const results = [
controller.validateAccess("config.secret"),
controller.validateAccess("private/data.txt"),
controller.validateAccess("temp.json"),
controller.validateAccess("nested/deep/file.secret"),
controller.validateAccess("private/nested/deep/file.txt"),
]
results.forEach((result) => result.should.be.false())
})
it("should allow access to non-ignored files", async () => {
const results = [
controller.validateAccess("public/data.txt"),
controller.validateAccess("config.json"),
controller.validateAccess("src/temp/file.ts"),
controller.validateAccess("nested/deep/file.txt"),
controller.validateAccess("not-private/data.txt"),
]
results.forEach((result) => result.should.be.true())
})
it("should handle pattern edge cases", async () => {
await fs.writeFile(
path.join(tempDir, ".clineignore"),
["*.secret", "private/", "*.tmp", "data-*.json", "temp/*"].join("\n"),
)
controller = new ClineIgnoreController(tempDir)
await controller.initialize()
const results = [
controller.validateAccess("data-123.json"), // Should be false (wildcard)
controller.validateAccess("data.json"), // Should be true (doesn't match pattern)
controller.validateAccess("script.tmp"), // Should be false (extension match)
]
results[0].should.be.false() // data-123.json
results[1].should.be.true() // data.json
results[2].should.be.false() // script.tmp
})
// ToDo: handle negation patterns successfully
// it("should handle negation patterns", async () => {
// await fs.writeFile(
// path.join(tempDir, ".clineignore"),
// [
// "temp/*", // Ignore everything in temp
// "!temp/allowed/*", // But allow files in temp/allowed
// "docs/**/*.md", // Ignore all markdown files in docs
// "!docs/README.md", // Except README.md
// "!docs/CONTRIBUTING.md", // And CONTRIBUTING.md
// "assets/", // Ignore all assets
// "!assets/public/", // Except public assets
// "!assets/public/*.png", // Specifically allow PNGs in public assets
// ].join("\n"),
// )
// controller = new ClineIgnoreController(tempDir)
// const results = [
// // Basic negation
// controller.validateAccess("temp/file.txt"), // Should be false (in temp/)
// controller.validateAccess("temp/allowed/file.txt"), // Should be true (negated)
// controller.validateAccess("temp/allowed/nested/file.txt"), // Should be true (negated with nested)
// // Multiple negations in same path
// controller.validateAccess("docs/guide.md"), // Should be false (matches docs/**/*.md)
// controller.validateAccess("docs/README.md"), // Should be true (negated)
// controller.validateAccess("docs/CONTRIBUTING.md"), // Should be true (negated)
// controller.validateAccess("docs/api/guide.md"), // Should be false (nested markdown)
// // Nested negations
// controller.validateAccess("assets/logo.png"), // Should be false (in assets/)
// controller.validateAccess("assets/public/logo.png"), // Should be true (negated and matches *.png)
// controller.validateAccess("assets/public/data.json"), // Should be true (in negated public/)
// ]
// results[0].should.be.false() // temp/file.txt
// results[1].should.be.true() // temp/allowed/file.txt
// results[2].should.be.true() // temp/allowed/nested/file.txt
// results[3].should.be.false() // docs/guide.md
// results[4].should.be.true() // docs/README.md
// results[5].should.be.true() // docs/CONTRIBUTING.md
// results[6].should.be.false() // docs/api/guide.md
// results[7].should.be.false() // assets/logo.png
// results[8].should.be.true() // assets/public/logo.png
// results[9].should.be.true() // assets/public/data.json
// })
it("should handle comments in .clineignore", async () => {
// Create a new .clineignore with comments
await fs.writeFile(
path.join(tempDir, ".clineignore"),
["# Comment line", "*.secret", "private/", "temp.*"].join("\n"),
)
controller = new ClineIgnoreController(tempDir)
await controller.initialize()
const result = controller.validateAccess("test.secret")
result.should.be.false()
})
})
describe("Path Handling", () => {
it("should handle absolute paths and match ignore patterns", async () => {
// Test absolute path that should be allowed
const allowedPath = path.join(tempDir, "src/file.ts")
const allowedResult = controller.validateAccess(allowedPath)
allowedResult.should.be.true()
// Test absolute path that matches an ignore pattern (*.secret)
const ignoredPath = path.join(tempDir, "config.secret")
const ignoredResult = controller.validateAccess(ignoredPath)
ignoredResult.should.be.false()
// Test absolute path in ignored directory (private/)
const ignoredDirPath = path.join(tempDir, "private/data.txt")
const ignoredDirResult = controller.validateAccess(ignoredDirPath)
ignoredDirResult.should.be.false()
})
it("should handle relative paths and match ignore patterns", async () => {
// Test relative path that should be allowed
const allowedResult = controller.validateAccess("./src/file.ts")
allowedResult.should.be.true()
// Test relative path that matches an ignore pattern (*.secret)
const ignoredResult = controller.validateAccess("./config.secret")
ignoredResult.should.be.false()
// Test relative path in ignored directory (private/)
const ignoredDirResult = controller.validateAccess("./private/data.txt")
ignoredDirResult.should.be.false()
})
it("should normalize paths with backslashes", async () => {
const result = controller.validateAccess("src\\file.ts")
result.should.be.true()
})
})
describe("Batch Filtering", () => {
it("should filter an array of paths", async () => {
const paths = ["src/index.ts", ".env", "lib/utils.ts", ".git/config", "dist/bundle.js"]
const filtered = controller.filterPaths(paths)
filtered.should.deepEqual(["src/index.ts", "lib/utils.ts", "dist/bundle.js"])
})
})
describe("Error Handling", () => {
it("should handle invalid paths", async () => {
// Test with an invalid path containing null byte
const result = controller.validateAccess("\0invalid")
result.should.be.true()
})
it("should handle missing .clineignore gracefully", async () => {
// Create a new controller in a directory without .clineignore
const emptyDir = path.join(os.tmpdir(), `llm-test-empty-${Date.now()}`)
await fs.mkdir(emptyDir)
try {
const controller = new ClineIgnoreController(emptyDir)
await controller.initialize()
const result = controller.validateAccess("file.txt")
result.should.be.true()
} finally {
await fs.rm(emptyDir, { recursive: true, force: true })
}
})
it("should handle empty .clineignore", async () => {
await fs.writeFile(path.join(tempDir, ".clineignore"), "")
controller = new ClineIgnoreController(tempDir)
await controller.initialize()
const result = controller.validateAccess("regular-file.txt")
result.should.be.true()
})
})
})
+189
View File
@@ -0,0 +1,189 @@
import path from "path"
import { fileExistsAtPath } from "../../utils/fs"
import fs from "fs/promises"
import ignore, { Ignore } from "ignore"
import * as vscode from "vscode"
export const LOCK_TEXT_SYMBOL = "\u{1F512}"
/**
* Controls LLM access to files by enforcing ignore patterns.
* Designed to be instantiated once in Cline.ts and passed to file manipulation services.
* Uses the 'ignore' library to support standard .gitignore syntax in .clineignore files.
*/
export class ClineIgnoreController {
private cwd: string
private ignoreInstance: Ignore
private disposables: vscode.Disposable[] = []
clineIgnoreContent: string | undefined
constructor(cwd: string) {
this.cwd = cwd
this.ignoreInstance = ignore()
this.clineIgnoreContent = undefined
// Set up file watcher for .clineignore
this.setupFileWatcher()
}
/**
* Initialize the controller by loading custom patterns
* Must be called after construction and before using the controller
*/
async initialize(): Promise<void> {
await this.loadClineIgnore()
}
/**
* Set up the file watcher for .clineignore changes
*/
private setupFileWatcher(): void {
const clineignorePattern = new vscode.RelativePattern(this.cwd, ".clineignore")
const fileWatcher = vscode.workspace.createFileSystemWatcher(clineignorePattern)
// Watch for changes and updates
this.disposables.push(
fileWatcher.onDidChange(() => {
this.loadClineIgnore()
}),
fileWatcher.onDidCreate(() => {
this.loadClineIgnore()
}),
fileWatcher.onDidDelete(() => {
this.loadClineIgnore()
}),
)
// Add fileWatcher itself to disposables
this.disposables.push(fileWatcher)
}
/**
* Load custom patterns from .clineignore if it exists
*/
private async loadClineIgnore(): Promise<void> {
try {
// Reset ignore instance to prevent duplicate patterns
this.ignoreInstance = ignore()
const ignorePath = path.join(this.cwd, ".clineignore")
if (await fileExistsAtPath(ignorePath)) {
const content = await fs.readFile(ignorePath, "utf8")
this.clineIgnoreContent = content
this.ignoreInstance.add(content)
this.ignoreInstance.add(".clineignore")
} else {
this.clineIgnoreContent = undefined
}
} catch (error) {
// Should never happen: reading file failed even though it exists
console.error("Unexpected error loading .clineignore:", error)
}
}
/**
* Check if a file should be accessible to the LLM
* @param filePath - Path to check (relative to cwd)
* @returns true if file is accessible, false if ignored
*/
validateAccess(filePath: string): boolean {
// Always allow access if .clineignore does not exist
if (!this.clineIgnoreContent) {
return true
}
try {
// Normalize path to be relative to cwd and use forward slashes
const absolutePath = path.resolve(this.cwd, filePath)
const relativePath = path.relative(this.cwd, absolutePath).toPosix()
// Ignore expects paths to be path.relative()'d
return !this.ignoreInstance.ignores(relativePath)
} catch (error) {
// console.error(`Error validating access for ${filePath}:`, error)
// Ignore is designed to work with relative file paths, so will throw error for paths outside cwd. We are allowing access to all files outside cwd.
return true
}
}
/**
* Check if a terminal command should be allowed to execute based on file access patterns
* @param command - Terminal command to validate
* @returns path of file that is being accessed if it is being accessed, undefined if command is allowed
*/
validateCommand(command: string): string | undefined {
// Always allow if no .clineignore exists
if (!this.clineIgnoreContent) {
return undefined
}
// Split command into parts and get the base command
const parts = command.trim().split(/\s+/)
const baseCommand = parts[0].toLowerCase()
// Commands that read file contents
const fileReadingCommands = [
// Unix commands
"cat",
"less",
"more",
"head",
"tail",
"grep",
"awk",
"sed",
// PowerShell commands and aliases
"get-content",
"gc",
"type",
"select-string",
"sls",
]
if (fileReadingCommands.includes(baseCommand)) {
// Check each argument that could be a file path
for (let i = 1; i < parts.length; i++) {
const arg = parts[i]
// Skip command flags/options (both Unix and PowerShell style)
if (arg.startsWith("-") || arg.startsWith("/")) {
continue
}
// Ignore PowerShell parameter names
if (arg.includes(":")) {
continue
}
// Validate file access
if (!this.validateAccess(arg)) {
return arg
}
}
}
return undefined
}
/**
* Filter an array of paths, removing those that should be ignored
* @param paths - Array of paths to filter (relative to cwd)
* @returns Array of allowed paths
*/
filterPaths(paths: string[]): string[] {
try {
return paths
.map((p) => ({
path: p,
allowed: this.validateAccess(p),
}))
.filter((x) => x.allowed)
.map((x) => x.path)
} catch (error) {
console.error("Error filtering paths:", error)
return [] // Fail closed for security
}
}
/**
* Clean up resources when the controller is no longer needed
*/
dispose(): void {
this.disposables.forEach((d) => d.dispose())
this.disposables = []
}
}
+41 -6
View File
@@ -7,27 +7,32 @@ import fs from "fs/promises"
import { extractTextFromFile } from "../../integrations/misc/extract-text"
import { isBinaryFile } from "isbinaryfile"
import { diagnosticsToProblemsString } from "../../integrations/diagnostics"
import { getLatestTerminalOutput } from "../../integrations/terminal/get-latest-output"
import { getCommitInfo } from "../../utils/git"
import { getWorkingState } from "../../utils/git"
export function openMention(mention?: string): void {
if (!mention) {
return
}
const cwd = vscode.workspace.workspaceFolders?.map((folder) => folder.uri.fsPath).at(0)
if (!cwd) {
return
}
if (mention.startsWith("/")) {
const relPath = mention.slice(1)
const cwd = vscode.workspace.workspaceFolders?.map((folder) => folder.uri.fsPath).at(0)
if (!cwd) {
return
}
const absPath = path.resolve(cwd, relPath)
if (mention.endsWith("/")) {
vscode.commands.executeCommand("revealInExplorer", vscode.Uri.file(absPath))
// vscode.commands.executeCommand("vscode.openFolder", , { forceNewWindow: false }) opens in new window
} else {
openFile(absPath)
}
} else if (mention === "problems") {
vscode.commands.executeCommand("workbench.actions.view.problems")
} else if (mention === "terminal") {
vscode.commands.executeCommand("workbench.action.terminal.focus")
} else if (mention.startsWith("http")) {
vscode.env.openExternal(vscode.Uri.parse(mention))
}
@@ -46,6 +51,12 @@ export async function parseMentions(text: string, cwd: string, urlContentFetcher
: `'${mentionPath}' (see below for file content)`
} else if (mention === "problems") {
return `Workspace Problems (see below for diagnostics)`
} else if (mention === "terminal") {
return `Terminal Output (see below for output)`
} else if (mention === "git-changes") {
return `Working directory changes (see below for details)`
} else if (/^[a-f0-9]{7,40}$/.test(mention)) {
return `Git commit '${mention}' (see below for commit info)`
}
return match
})
@@ -61,7 +72,10 @@ export async function parseMentions(text: string, cwd: string, urlContentFetcher
}
}
for (const mention of mentions) {
// Filter out duplicate mentions while preserving order
const uniqueMentions = Array.from(new Set(mentions))
for (const mention of uniqueMentions) {
if (mention.startsWith("http")) {
let result: string
if (launchBrowserError) {
@@ -99,6 +113,27 @@ export async function parseMentions(text: string, cwd: string, urlContentFetcher
} catch (error) {
parsedText += `\n\n<workspace_diagnostics>\nError fetching diagnostics: ${error.message}\n</workspace_diagnostics>`
}
} else if (mention === "terminal") {
try {
const terminalOutput = await getLatestTerminalOutput()
parsedText += `\n\n<terminal_output>\n${terminalOutput}\n</terminal_output>`
} catch (error) {
parsedText += `\n\n<terminal_output>\nError fetching terminal output: ${error.message}\n</terminal_output>`
}
} else if (mention === "git-changes") {
try {
const workingState = await getWorkingState(cwd)
parsedText += `\n\n<git_working_state>\n${workingState}\n</git_working_state>`
} catch (error) {
parsedText += `\n\n<git_working_state>\nError fetching working state: ${error.message}\n</git_working_state>`
}
} else if (/^[a-f0-9]{7,40}$/.test(mention)) {
try {
const commitInfo = await getCommitInfo(mention, cwd)
parsedText += `\n\n<git_commit hash="${mention}">\n${commitInfo}\n</git_commit>`
} catch (error) {
parsedText += `\n\n<git_commit hash="${mention}">\nError fetching commit info: ${error.message}\n</git_commit>`
}
}
}
+30 -8
View File
@@ -1,15 +1,16 @@
import { Anthropic } from "@anthropic-ai/sdk"
import * as path from "path"
import * as diff from "diff"
import * as path from "path"
import { ClineIgnoreController, LOCK_TEXT_SYMBOL } from "../ignore/ClineIgnoreController"
export const formatResponse = {
toolDenied: () => `The user denied this operation.`,
toolDeniedWithFeedback: (feedback?: string) =>
`The user denied this operation and provided the following feedback:\n<feedback>\n${feedback}\n</feedback>`,
toolError: (error?: string) => `The tool execution failed with the following error:\n<error>\n${error}\n</error>`,
clineIgnoreError: (path: string) =>
`Access to ${path} is blocked by the .clineignore file settings. You must try to continue in the task without using this file, or ask the user to update the .clineignore file.`,
noToolsUsed: () =>
`[ERROR] You did not use a tool in your previous response! Please retry with a tool use.
@@ -46,7 +47,12 @@ Otherwise, if you have not completed the task and do not need additional informa
return formatImagesIntoBlocks(images)
},
formatFilesList: (absolutePath: string, files: string[], didHitLimit: boolean): string => {
formatFilesList: (
absolutePath: string,
files: string[],
didHitLimit: boolean,
clineIgnoreController?: ClineIgnoreController,
): string => {
const sorted = files
.map((file) => {
// convert absolute path to relative path
@@ -77,14 +83,30 @@ Otherwise, if you have not completed the task and do not need additional informa
// the shorter one comes first
return aParts.length - bParts.length
})
const clineIgnoreParsed = clineIgnoreController
? sorted.map((filePath) => {
// path is relative to absolute path, not cwd
// validateAccess expects either path relative to cwd or absolute path
// otherwise, for validating against ignore patterns like "assets/icons", we would end up with just "icons", which would result in the path not being ignored.
const absoluteFilePath = path.resolve(absolutePath, filePath)
const isIgnored = !clineIgnoreController.validateAccess(absoluteFilePath)
if (isIgnored) {
return LOCK_TEXT_SYMBOL + " " + filePath
}
return filePath
})
: sorted
if (didHitLimit) {
return `${sorted.join(
return `${clineIgnoreParsed.join(
"\n",
)}\n\n(File list truncated. Use list_files on specific subdirectories if you need to explore further.)`
} else if (sorted.length === 0 || (sorted.length === 1 && sorted[0] === "")) {
} else if (clineIgnoreParsed.length === 0 || (clineIgnoreParsed.length === 1 && clineIgnoreParsed[0] === "")) {
return "No files found."
} else {
return sorted.join("\n")
return clineIgnoreParsed.join("\n")
}
},
+49 -12
View File
@@ -1,4 +1,4 @@
import defaultShell from "default-shell"
import { getShell } from "../../utils/shell"
import os from "os"
import osName from "os-name"
import { McpHub } from "../../services/mcp/McpHub"
@@ -38,7 +38,7 @@ Always adhere to this format for the tool use to ensure proper parsing and execu
# Tools
## execute_command
Description: Request to execute a CLI command on the system. Use this when you need to perform system operations or run specific commands to accomplish any step in the user's task. You must tailor your command to the user's system and provide a clear explanation of what the command does. Prefer to execute complex CLI commands over creating executable scripts, as they are more flexible and easier to run. Commands will be executed in the current working directory: ${cwd.toPosix()}
Description: Request to execute a CLI command on the system. Use this when you need to perform system operations or run specific commands to accomplish any step in the user's task. You must tailor your command to the user's system and provide a clear explanation of what the command does. For command chaining, use the appropriate chaining syntax for the user's shell. Prefer to execute complex CLI commands over creating executable scripts, as they are more flexible and easier to run. Commands will be executed in the current working directory: ${cwd.toPosix()}
Parameters:
- command: (required) The CLI command to execute. This should be valid for the current operating system. Ensure the command is properly formatted and does not contain any harmful instructions.
- requires_approval: (required) A boolean indicating whether this command requires explicit user approval before execution in case the user has auto-approve mode enabled. Set to 'true' for potentially impactful operations like installing/uninstalling packages, deleting/overwriting files, system configuration changes, network operations, or any commands that could have unintended side effects. Set to 'false' for safe operations like reading files/directories, running development servers, building projects, and other non-destructive operations.
@@ -216,9 +216,13 @@ Usage:
Description: Ask the user a question to gather additional information needed to complete the task. This tool should be used when you encounter ambiguities, need clarification, or require more details to proceed effectively. It allows for interactive problem-solving by enabling direct communication with the user. Use this tool judiciously to maintain a balance between gathering necessary information and avoiding excessive back-and-forth.
Parameters:
- question: (required) The question to ask the user. This should be a clear, specific question that addresses the information you need.
- options: (optional) An array of 2-5 options for the user to choose from. Each option should be a string describing a possible answer. You may not always need to provide options, but it may be helpful in many cases where it can save the user from having to type out a response manually.
Usage:
<ask_followup_question>
<question>Your question here</question>
<options>
Array of options here (optional), e.g. ["Option 1", "Option 2", "Option 3"]
</options>
</ask_followup_question>
## attempt_completion
@@ -239,9 +243,13 @@ Your final result description here
Description: Respond to the user's inquiry in an effort to plan a solution to the user's task. This tool should be used when you need to provide a response to a question or statement from the user about how you plan to accomplish the task. This tool is only available in PLAN MODE. The environment_details will specify the current mode, if it is not PLAN MODE then you should not use this tool. Depending on the user's message, you may ask questions to get clarification about the user's request, architect a solution to the task, and to brainstorm ideas with the user. For example, if the user's task is to create a website, you may start by asking some clarifying questions, then present a detailed plan for how you will accomplish the task given the context, and perhaps engage in a back and forth to finalize the details before the user switches you to ACT MODE to implement the solution.
Parameters:
- response: (required) The response to provide to the user. Do not try to use tools in this parameter, this is simply a chat response.
- options: (optional) An array of 2-5 options for the user to choose from. Each option should be a string describing a possible choice or path forward in the planning process. This can help guide the discussion and make it easier for the user to provide input on key decisions. You may not always need to provide options, but it may be helpful in many cases where it can save the user from having to type out a response manually. Do NOT present an option to toggle to Act mode, as this will be something you need to direct the user to do manually themselves.
Usage:
<plan_mode_response>
<response>Your response here</response>
<options>
Array of options here (optional), e.g. ["Option 1", "Option 2", "Option 3"]
</options>
</plan_mode_response>
# Tool Use Examples
@@ -331,7 +339,24 @@ ${
<access_mcp_resource>
<server_name>weather-server</server_name>
<uri>weather://san-francisco/current</uri>
</access_mcp_resource>`
</access_mcp_resource>
## Example 6: Another example of using an MCP tool (where the server name is a unique identifier such as a URL)
<use_mcp_tool>
<server_name>github.com/modelcontextprotocol/servers/tree/main/src/github</server_name>
<tool_name>create_issue</tool_name>
<arguments>
{
"owner": "octocat",
"repo": "hello-world",
"title": "Found a bug",
"body": "I'm having a problem with this.",
"labels": ["bug", "help wanted"],
"assignees": ["octocat"]
}
</arguments>
</use_mcp_tool>`
: ""
}
@@ -750,7 +775,7 @@ IMPORTANT: Regardless of what else you see in the MCP settings file, you must de
(Note: the user may also ask you to install the MCP server to the Claude desktop app, in which case you would read then modify \`~/Library/Application\ Support/Claude/claude_desktop_config.json\` on macOS for example. It follows the same format of a top level \`mcpServers\` object.)
6. After you have edited the MCP settings configuration file, the system will automatically run all the servers and expose the available tools and resources in the 'Connected MCP Servers' section.
6. After you have edited the MCP settings configuration file, the system will automatically run all the servers and expose the available tools and resources in the 'Connected MCP Servers' section. (Note: If you encounter a 'not connected' error when testing a newly installed mcp server, a common cause is an incorrect build path in your MCP settings configuration. Since compiled JavaScript files are commonly output to either 'dist/' or 'build/' directories, double-check that the build path in your MCP settings matches where your files are actually being compiled. E.g. If you assumed 'build' as the folder, check tsconfig.json to see if it's using 'dist' instead.)
7. Now that you have access to these new tools and resources, you may suggest ways the user can command you to invoke them - for example, with this new weather tool now available, you can invite the user to ask "what's the weather in San Francisco?"
@@ -796,7 +821,7 @@ You have access to two tools for working with files: **write_to_file** and **rep
## Important Considerations
- Using write_to_file requires providing the files complete final content.
- Using write_to_file requires providing the file's complete final content.
- If you only need to make small changes to an existing file, consider using replace_in_file instead to avoid unnecessarily rewriting the entire file.
- While write_to_file should not be your default choice, don't hesitate to use it when the situation truly calls for it.
@@ -809,12 +834,12 @@ You have access to two tools for working with files: **write_to_file** and **rep
## When to Use
- Small, localized changes like updating a few lines, function implementations, changing variable names, modifying a section of text, etc.
- Targeted improvements where only specific portions of the files content needs to be altered.
- Targeted improvements where only specific portions of the file's content needs to be altered.
- Especially useful for long files where much of the file will remain unchanged.
## Advantages
- More efficient for minor edits, since you dont need to supply the entire file content.
- More efficient for minor edits, since you don't need to supply the entire file content.
- Reduces the chance of errors that can occur when overwriting large files.
# Choosing the Appropriate Tool
@@ -865,9 +890,10 @@ In each user message, the environment_details will specify the current mode. The
## What is PLAN MODE?
- While you are usually in ACT MODE, the user may switch to PLAN MODE in order to have a back and forth with you to plan how to best accomplish the task.
- When starting in PLAN MODE, depending on the user's request, you may need to do some information gathering e.g. using read_file or search_files to get more context about the task. You may also ask the user clarifying questions to get a better understanding of the task.
- Once you've gained more context about the user's request, you should architect a detailed plan for how you will accomplish the task.
- When starting in PLAN MODE, depending on the user's request, you may need to do some information gathering e.g. using read_file or search_files to get more context about the task. You may also ask the user clarifying questions to get a better understanding of the task. You may return mermaid diagrams to visually display your understanding.
- Once you've gained more context about the user's request, you should architect a detailed plan for how you will accomplish the task. Returning mermaid diagrams may be helpful here as well.
- Then you might ask the user if they are pleased with this plan, or if they would like to make any changes. Think of this as a brainstorming session where you can discuss the task and plan the best way to accomplish it.
- If at any point a mermaid diagram would make your plan clearer to help the user quickly see the structure, you are encouraged to include a Mermaid code block in the response. (Note: if you use colors in your mermaid diagrams, be sure to use high contrast colors so the text is readable.)
- Finally once it seems like you've reached a good plan, ask the user to switch you back to ACT MODE to implement the solution.
====
@@ -941,7 +967,7 @@ ${
SYSTEM INFORMATION
Operating System: ${osName()}
Default Shell: ${defaultShell}
Default Shell: ${getShell()}
Home Directory: ${os.homedir().toPosix()}
Current Working Directory: ${cwd.toPosix()}
@@ -957,13 +983,24 @@ You accomplish a given task iteratively, breaking it down into clear steps and w
4. Once you've completed the user's task, you must use the attempt_completion tool to present the result of the task to the user. You may also provide a CLI command to showcase the result of your task; this can be particularly useful for web development tasks, where you can run e.g. \`open index.html\` to show the website you've built.
5. The user may provide feedback, which you can use to make improvements and try again. But DO NOT continue in pointless back and forth conversations, i.e. don't end your responses with questions or offers for further assistance.`
export function addUserInstructions(settingsCustomInstructions?: string, clineRulesFileInstructions?: string) {
export function addUserInstructions(
settingsCustomInstructions?: string,
clineRulesFileInstructions?: string,
clineIgnoreInstructions?: string,
preferredLanguageInstructions?: string,
) {
let customInstructions = ""
if (preferredLanguageInstructions) {
customInstructions += preferredLanguageInstructions + "\n\n"
}
if (settingsCustomInstructions) {
customInstructions += settingsCustomInstructions + "\n\n"
}
if (clineRulesFileInstructions) {
customInstructions += clineRulesFileInstructions
customInstructions += clineRulesFileInstructions + "\n\n"
}
if (clineIgnoreInstructions) {
customInstructions += clineIgnoreInstructions
}
return `
-97
View File
@@ -1,97 +0,0 @@
import { Anthropic } from "@anthropic-ai/sdk"
/*
We can't implement a dynamically updating sliding window as it would break prompt cache
every time. To maintain the benefits of caching, we need to keep conversation history
static. This operation should be performed as infrequently as possible. If a user reaches
a 200k context, we can assume that the first half is likely irrelevant to their current task.
Therefore, this function should only be called when absolutely necessary to fit within
context limits, not as a continuous process.
*/
// export function truncateHalfConversation(
// messages: Anthropic.Messages.MessageParam[],
// ): Anthropic.Messages.MessageParam[] {
// // API expects messages to be in user-assistant order, and tool use messages must be followed by tool results. We need to maintain this structure while truncating.
// // Always keep the first Task message (this includes the project's file structure in environment_details)
// const truncatedMessages = [messages[0]]
// // Remove half of user-assistant pairs
// const messagesToRemove = Math.floor(messages.length / 4) * 2 // has to be even number
// const remainingMessages = messages.slice(messagesToRemove + 1) // has to start with assistant message since tool result cannot follow assistant message with no tool use
// truncatedMessages.push(...remainingMessages)
// return truncatedMessages
// }
/*
getNextTruncationRange: Calculates the next range of messages to be "deleted"
- Takes the full messages array and optional current deleted range
- Always preserves the first message (task message)
- Removes 1/2 of remaining messages (rounded down to even number) after current deleted range
- Returns [startIndex, endIndex] representing inclusive range to delete
getTruncatedMessages: Constructs the truncated array using the deleted range
- Takes full messages array and optional deleted range
- Returns new array with messages in deleted range removed
- Preserves order and structure of remaining messages
The range is represented as [startIndex, endIndex] where both indices are inclusive
The functions maintain the original array integrity while allowing progressive truncation
through the deletedRange parameter
Usage example:
const messages = [user1, assistant1, user2, assistant2, user3, assistant3];
let deletedRange = getNextTruncationRange(messages); // [1,2] (assistant1,user2)
let truncated = getTruncatedMessages(messages, deletedRange);
// [user1, assistant2, user3, assistant3]
deletedRange = getNextTruncationRange(messages, deletedRange); // [2,3] (assistant2,user3)
truncated = getTruncatedMessages(messages, deletedRange);
// [user1, assistant3]
*/
export function getNextTruncationRange(
messages: Anthropic.Messages.MessageParam[],
currentDeletedRange: [number, number] | undefined = undefined,
keep: "half" | "quarter" = "half",
): [number, number] {
// Since we always keep the first message, currentDeletedRange[0] will always be 1 (for now until we have a smarter truncation algorithm)
const rangeStartIndex = 1
const startOfRest = currentDeletedRange ? currentDeletedRange[1] + 1 : 1
let messagesToRemove: number
if (keep === "half") {
// Remove half of user-assistant pairs
messagesToRemove = Math.floor((messages.length - startOfRest) / 4) * 2 // Keep even number
} else {
// Remove 3/4 of user-assistant pairs
messagesToRemove = Math.floor((messages.length - startOfRest) / 8) * 3 * 2
}
let rangeEndIndex = startOfRest + messagesToRemove - 1
// Make sure the last message being removed is a user message, so that the next message after the initial task message is an assistant message. This preservers the user-assistant-user-assistant structure.
// NOTE: anthropic format messages are always user-assitant-user-assistant, while openai format messages can have multiple user messages in a row (we use anthropic format throughout cline)
if (messages[rangeEndIndex].role !== "user") {
rangeEndIndex -= 1
}
// this is an inclusive range that will be removed from the conversation history
return [rangeStartIndex, rangeEndIndex]
}
export function getTruncatedMessages(
messages: Anthropic.Messages.MessageParam[],
deletedRange: [number, number] | undefined,
): Anthropic.Messages.MessageParam[] {
if (!deletedRange) {
return messages
}
const [start, end] = deletedRange
// the range is inclusive - both start and end indices and everything in between will be removed from the final result.
// NOTE: if you try to console log these, don't forget that logging a reference to an array may not provide the same result as logging a slice() snapshot of that array at that exact moment. The following DOES in fact include the latest assistant message.
return [...messages.slice(0, start), ...messages.slice(end + 1)]
}
File diff suppressed because it is too large Load Diff
+288
View File
@@ -0,0 +1,288 @@
import * as vscode from "vscode"
import * as fs from "fs/promises"
import * as path from "path"
import { ClineProvider } from "../../core/webview/ClineProvider"
import { HistoryItem } from "../../shared/HistoryItem"
import { ClineMessage } from "../../shared/ExtensionMessage"
/**
* Registers development-only commands for task manipulation.
* These are only activated in development mode.
*/
export function registerTaskCommands(context: vscode.ExtensionContext, provider: ClineProvider): vscode.Disposable[] {
return [
vscode.commands.registerCommand("cline.dev.createTestTasks", async () => {
const count = await vscode.window.showInputBox({
title: "Test Tasks",
prompt: "How many test tasks to create?",
value: "10",
})
if (!count) {
return
}
const tasksCount = parseInt(count)
const globalStoragePath = context.globalStorageUri.fsPath
const tasksDir = path.join(globalStoragePath, "tasks")
vscode.window.withProgress(
{
location: vscode.ProgressLocation.Notification,
title: `Creating ${tasksCount} test tasks...`,
cancellable: false,
},
async (progress) => {
for (let i = 0; i < tasksCount; i++) {
// Generate a timestamp to ensure unique IDs
const timestamp = Date.now() + i
const taskId = `${timestamp}`
const taskDir = path.join(tasksDir, taskId)
await fs.mkdir(taskDir, { recursive: true })
// Generate a task prompt
const taskName = getRandomTaskName(i)
// Create realistic message sequence
const messages = createRealisticMessageSequence(timestamp, taskName, i)
// Create API conversation history file
await fs.writeFile(
path.join(taskDir, "api_conversation_history.json"),
JSON.stringify(
[
{
role: "user",
content: [{ type: "text", text: `<task>\n${taskName}\n</task>` }],
},
{
role: "assistant",
content: [
{
type: "text",
text: `I'll help you ${taskName.toLowerCase()}. Let me break this down into steps.`,
},
],
},
],
null,
2,
),
)
// Create UI messages file with realistic message sequence
await fs.writeFile(path.join(taskDir, "ui_messages.json"), JSON.stringify(messages, null, 2))
// Create history item to be shown in the HistoryView
const historyItem: HistoryItem = {
id: taskId,
ts: timestamp,
task: taskName,
tokensIn: Math.floor(100 + Math.random() * 900), // Random token count from 100-1000
tokensOut: Math.floor(200 + Math.random() * 1800), // Random token count from 200-2000
cacheWrites: i % 3 === 0 ? Math.floor(50 + Math.random() * 150) : undefined, // Only add cache writes to every 3rd task
cacheReads: i % 3 === 0 ? Math.floor(20 + Math.random() * 80) : undefined, // Only add cache reads to every 3rd task
totalCost: Number((0.0001 + Math.random() * 0.01).toFixed(5)), // Random cost from $0.0001 to $0.0101
size: 1024 * 1024, // 1MB
}
// Update task history in global state
await provider.updateTaskHistory(historyItem)
progress.report({ increment: 100 / tasksCount })
}
// Update the UI to show the new tasks
await provider.postStateToWebview()
vscode.window.showInformationMessage(`Created ${tasksCount} test tasks`)
},
)
}),
]
}
/**
* Creates a realistic sequence of messages that would occur in a typical task
*/
function createRealisticMessageSequence(baseTimestamp: number, taskPrompt: string, taskIndex: number): ClineMessage[] {
// Use an incrementing timestamp to ensure messages appear in sequence
let timestamp = baseTimestamp
const getNextTimestamp = () => {
timestamp += 1000 // Add 1 second between messages
return timestamp
}
// Variables to make different test tasks look unique
const fileName = getRandomFileName(taskIndex)
const commitHash = `commit${taskIndex}${Math.floor(Math.random() * 1000000).toString(16)}`
// Create a realistic message sequence
const messages: ClineMessage[] = [
// Initial task message - uses "say" with "text" which is the format used in Cline.ts
{
ts: baseTimestamp,
type: "say",
say: "text",
text: taskPrompt,
},
// API request started
{
ts: getNextTimestamp(),
type: "say",
say: "api_req_started",
text: JSON.stringify({
request: `<task>\n${taskPrompt}\n</task>`,
tokensIn: Math.floor(100 + Math.random() * 200),
tokensOut: Math.floor(300 + Math.random() * 500),
}),
},
// Reasoning message
{
ts: getNextTimestamp(),
type: "say",
say: "reasoning",
text: `I'll approach this task by breaking it down into manageable steps. First, I'll analyze the requirements, then create a plan, and finally implement the solution systematically.`,
},
// Text response
{
ts: getNextTimestamp(),
type: "say",
say: "text",
text: `I'll help you with this task. Let me start by creating the necessary files and implementing the core functionality.`,
},
]
// Add task-specific messages based on index modulo to create variety
const messageType = taskIndex % 5
if (messageType === 0 || messageType === 2) {
// Tool use - file operations
messages.push({
ts: getNextTimestamp(),
type: "say",
say: "tool",
text: JSON.stringify({
tool: "newFileCreated",
path: fileName,
content: `// Sample code for ${taskPrompt}`,
}),
})
}
if (messageType === 1 || messageType === 3) {
// Command execution
messages.push(
{
ts: getNextTimestamp(),
type: "ask",
ask: "command",
text: `ls -la`,
},
{
ts: getNextTimestamp(),
type: "say",
say: "command_output",
text: `total 24\ndrwxr-xr-x 3 user staff 96 Mar 10 12:34 .\ndrwxr-xr-x 8 user staff 256 Mar 10 12:30 ..\n-rw-r--r-- 1 user staff 158 Mar 10 12:34 ${fileName}`,
},
)
}
if (messageType === 2 || messageType === 4) {
// Browser actions
messages.push(
{
ts: getNextTimestamp(),
type: "ask",
ask: "browser_action_launch",
text: `https://example.com`,
},
{
ts: getNextTimestamp(),
type: "say",
say: "browser_action_result",
text: JSON.stringify({
logs: "Page loaded successfully",
screenshot:
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg==",
}),
},
{
ts: getNextTimestamp(),
type: "say",
say: "browser_action",
text: JSON.stringify({
action: "close",
}),
},
)
}
// Add checkpoint
messages.push({
ts: getNextTimestamp(),
type: "say",
say: "checkpoint_created",
lastCheckpointHash: commitHash,
})
// Add completion result (all tasks end with this)
messages.push({
ts: getNextTimestamp(),
type: "say",
say: "completion_result",
text: `I've completed the task to ${taskPrompt.toLowerCase()}. The implementation includes all the required functionality and meets the specifications. ${"x".repeat(1024 * 1024)}`, // 1MB file
lastCheckpointHash: commitHash,
})
return messages
}
/**
* Returns a random task name for test data
*/
function getRandomTaskName(index: number): string {
const tasks = [
"Create a simple todo application",
"Build a weather forecast widget",
"Implement a markdown parser",
"Design a responsive landing page",
"Develop a currency converter",
"Create a file upload component",
"Build a data visualization dashboard",
"Implement a search functionality",
"Create a user authentication system",
"Design a dark mode toggle",
"Build a countdown timer",
"Create a drag and drop interface",
"Implement form validation",
"Design a multi-step wizard",
"Create a notification system",
]
return tasks[index % tasks.length] + ` (Test ${index + 1})`
}
/**
* Returns a random file name for test data
*/
function getRandomFileName(index: number): string {
const files = [
"index.html",
"styles.css",
"script.js",
"app.jsx",
"main.ts",
"utils.py",
"config.json",
"server.js",
"data.csv",
"README.md",
]
return files[index % files.length]
}
+197 -6
View File
@@ -1,12 +1,14 @@
// The module 'vscode' contains the VS Code extensibility API
// Import the module and reference it with the alias vscode in your code below
import delay from "delay"
import { setTimeout as setTimeoutPromise } from "node:timers/promises"
import * as vscode from "vscode"
import { ClineProvider } from "./core/webview/ClineProvider"
import { Logger } from "./services/logging/Logger"
import { createClineAPI } from "./exports"
import "./utils/path" // necessary to have access to String.prototype.toPosix
import { DIFF_VIEW_URI_SCHEME } from "./integrations/editor/DiffViewProvider"
import assert from "node:assert"
import { telemetryService } from "./services/telemetry/TelemetryService"
/*
Built using https://github.com/microsoft/vscode-webview-ui-toolkit
@@ -30,6 +32,8 @@ export function activate(context: vscode.ExtensionContext) {
const sidebarProvider = new ClineProvider(context, outputChannel)
vscode.commands.executeCommand("setContext", "cline.isDevMode", IS_DEV && IS_DEV === "true")
context.subscriptions.push(
vscode.window.registerWebviewViewProvider(ClineProvider.sideBarId, sidebarProvider, {
webviewOptions: { retainContextWhenHidden: true },
@@ -86,7 +90,7 @@ export function activate(context: vscode.ExtensionContext) {
tabProvider.resolveWebviewView(panel)
// Lock the editor group so clicking on files doesn't open them over the panel
await delay(100)
await setTimeoutPromise(100)
await vscode.commands.executeCommand("workbench.action.lockEditorGroup")
}
@@ -113,14 +117,20 @@ export function activate(context: vscode.ExtensionContext) {
)
context.subscriptions.push(
vscode.commands.registerCommand("cline.accountLoginClicked", () => {
vscode.commands.registerCommand("cline.accountButtonClicked", () => {
sidebarProvider.postMessageToWebview({
type: "action",
action: "accountLoginClicked",
action: "accountButtonClicked",
})
}),
)
context.subscriptions.push(
vscode.commands.registerCommand("cline.openDocumentation", () => {
vscode.env.openExternal(vscode.Uri.parse("https://docs.cline.bot/"))
}),
)
/*
We use the text document content provider API to show the left side for diff view by creating a virtual document for the original content. This makes it readonly so users know to edit the right side if they want to keep their changes.
@@ -160,10 +170,12 @@ export function activate(context: vscode.ExtensionContext) {
case "/auth": {
const token = query.get("token")
const state = query.get("state")
const apiKey = query.get("apiKey")
console.log("Auth callback received:", {
token: token,
state: state,
apiKey: apiKey,
})
// Validate state parameter
@@ -172,8 +184,8 @@ export function activate(context: vscode.ExtensionContext) {
return
}
if (token) {
await visibleProvider.handleAuthCallback(token)
if (token && apiKey) {
await visibleProvider.handleAuthCallback(token, apiKey)
}
break
}
@@ -183,10 +195,189 @@ export function activate(context: vscode.ExtensionContext) {
}
context.subscriptions.push(vscode.window.registerUriHandler({ handleUri }))
// Register size testing commands in development mode
if (IS_DEV && IS_DEV === "true") {
// Use dynamic import to avoid loading the module in production
import("./dev/commands/tasks")
.then((module) => {
const devTaskCommands = module.registerTaskCommands(context, sidebarProvider)
context.subscriptions.push(...devTaskCommands)
Logger.log("Cline dev task commands registered")
})
.catch((error) => {
Logger.log("Failed to register dev task commands: " + error)
})
}
context.subscriptions.push(
vscode.commands.registerCommand("cline.addToChat", async (range?: vscode.Range, diagnostics?: vscode.Diagnostic[]) => {
const editor = vscode.window.activeTextEditor
if (!editor) {
return
}
// Use provided range if available, otherwise use current selection
// (vscode command passes an argument in the first param by default, so we need to ensure it's a Range object)
const textRange = range instanceof vscode.Range ? range : editor.selection
const selectedText = editor.document.getText(textRange)
if (!selectedText) {
return
}
// Get the file path and language ID
const filePath = editor.document.uri.fsPath
const languageId = editor.document.languageId
// Send to sidebar provider
await sidebarProvider.addSelectedCodeToChat(
selectedText,
filePath,
languageId,
Array.isArray(diagnostics) ? diagnostics : undefined,
)
}),
)
context.subscriptions.push(
vscode.commands.registerCommand("cline.addTerminalOutputToChat", async () => {
const terminal = vscode.window.activeTerminal
if (!terminal) {
return
}
// Save current clipboard content
const tempCopyBuffer = await vscode.env.clipboard.readText()
try {
// Copy the *existing* terminal selection (without selecting all)
await vscode.commands.executeCommand("workbench.action.terminal.copySelection")
// Get copied content
let terminalContents = (await vscode.env.clipboard.readText()).trim()
// Restore original clipboard content
await vscode.env.clipboard.writeText(tempCopyBuffer)
if (!terminalContents) {
// No terminal content was copied (either nothing selected or some error)
return
}
// [Optional] Any additional logic to process multi-line content can remain here
// For example:
/*
const lines = terminalContents.split("\n")
const lastLine = lines.pop()?.trim()
if (lastLine) {
let i = lines.length - 1
while (i >= 0 && !lines[i].trim().startsWith(lastLine)) {
i--
}
terminalContents = lines.slice(Math.max(i, 0)).join("\n")
}
*/
// Send to sidebar provider
await sidebarProvider.addSelectedTerminalOutputToChat(terminalContents, terminal.name)
} catch (error) {
// Ensure clipboard is restored even if an error occurs
await vscode.env.clipboard.writeText(tempCopyBuffer)
console.error("Error getting terminal contents:", error)
vscode.window.showErrorMessage("Failed to get terminal contents")
}
}),
)
// Register code action provider
context.subscriptions.push(
vscode.languages.registerCodeActionsProvider(
"*",
new (class implements vscode.CodeActionProvider {
public static readonly providedCodeActionKinds = [vscode.CodeActionKind.QuickFix]
provideCodeActions(
document: vscode.TextDocument,
range: vscode.Range,
context: vscode.CodeActionContext,
): vscode.CodeAction[] {
// Expand range to include surrounding 3 lines
const expandedRange = new vscode.Range(
Math.max(0, range.start.line - 3),
0,
Math.min(document.lineCount - 1, range.end.line + 3),
document.lineAt(Math.min(document.lineCount - 1, range.end.line + 3)).text.length,
)
const addAction = new vscode.CodeAction("Add to Cline", vscode.CodeActionKind.QuickFix)
addAction.command = {
command: "cline.addToChat",
title: "Add to Cline",
arguments: [expandedRange, context.diagnostics],
}
const fixAction = new vscode.CodeAction("Fix with Cline", vscode.CodeActionKind.QuickFix)
fixAction.command = {
command: "cline.fixWithCline",
title: "Fix with Cline",
arguments: [expandedRange, context.diagnostics],
}
// Only show actions when there are errors
if (context.diagnostics.length > 0) {
return [addAction, fixAction]
} else {
return []
}
}
})(),
{
providedCodeActionKinds: [vscode.CodeActionKind.QuickFix],
},
),
)
// Register the command handler
context.subscriptions.push(
vscode.commands.registerCommand("cline.fixWithCline", async (range: vscode.Range, diagnostics: any[]) => {
const editor = vscode.window.activeTextEditor
if (!editor) {
return
}
const selectedText = editor.document.getText(range)
const filePath = editor.document.uri.fsPath
const languageId = editor.document.languageId
// Send to sidebar provider with diagnostics
await sidebarProvider.fixWithCline(selectedText, filePath, languageId, diagnostics)
}),
)
return createClineAPI(outputChannel, sidebarProvider)
}
// This method is called when your extension is deactivated
export function deactivate() {
telemetryService.shutdown()
Logger.log("Cline extension deactivated")
}
// TODO: Find a solution for automatically removing DEV related content from production builds.
// This type of code is fine in production to keep. We just will want to remove it from production builds
// to bring down built asset sizes.
//
// This is a workaround to reload the extension when the source code changes
// since vscode doesn't support hot reload for extensions
const { IS_DEV, DEV_WORKSPACE_FOLDER } = process.env
if (IS_DEV && IS_DEV !== "false") {
assert(DEV_WORKSPACE_FOLDER, "DEV_WORKSPACE_FOLDER must be set in development")
const watcher = vscode.workspace.createFileSystemWatcher(new vscode.RelativePattern(DEV_WORKSPACE_FOLDER, "src/**/*"))
watcher.onDidChange(({ scheme, path }) => {
console.info(`${scheme} ${path} changed. Reloading VSCode...`)
vscode.commands.executeCommand("workbench.action.reloadWindow")
})
}
+8
View File
@@ -0,0 +1,8 @@
// NOTE: These are here temporarily until we find a better home for them
export const GlobalFileNames = {
apiConversationHistory: "api_conversation_history.json",
uiMessages: "ui_messages.json",
openRouterModels: "openrouter_models.json",
mcpSettings: "cline_mcp_settings.json",
clineRules: ".clinerules",
}
@@ -0,0 +1,73 @@
import * as vscode from "vscode"
import fs from "fs/promises"
import path from "path"
import os from "os"
import CheckpointTracker from "./CheckpointTracker"
export async function createTestEnvironment() {
// Create temp directory structure
const tempDir = path.join(os.tmpdir(), `checkpoint-test-${Date.now()}`)
await fs.mkdir(tempDir, { recursive: true })
// Create storage path outside of working directory to avoid submodule issues
const globalStoragePath = path.join(os.tmpdir(), `storage-${Date.now()}`)
await fs.mkdir(globalStoragePath, { recursive: true })
// Create test file in a subdirectory
const testDir = path.join(tempDir, "src")
await fs.mkdir(testDir, { recursive: true })
const testFilePath = path.join(testDir, "test.txt")
// Create .gitignore to prevent git from treating directories as submodules
await fs.writeFile(path.join(tempDir, ".gitignore"), "storage/\n")
// Mock VS Code workspace
const mockWorkspaceFolders = [
{
uri: { fsPath: tempDir },
name: "test",
index: 0,
},
]
const originalDescriptor = Object.getOwnPropertyDescriptor(vscode.workspace, "workspaceFolders")
Object.defineProperty(vscode.workspace, "workspaceFolders", {
get: () => mockWorkspaceFolders,
})
// Mock findFiles to return no nested git repos
const originalFindFiles = vscode.workspace.findFiles
vscode.workspace.findFiles = async () => []
// Mock VS Code configuration
const originalGetConfiguration = vscode.workspace.getConfiguration
vscode.workspace.getConfiguration = () =>
({
get: (key: string) => (key === "enableCheckpoints" ? true : undefined),
}) as any
return {
tempDir,
globalStoragePath,
testFilePath,
originalDescriptor,
originalFindFiles,
originalGetConfiguration,
cleanup: async () => {
// Restore VS Code mocks
if (originalDescriptor) {
Object.defineProperty(vscode.workspace, "workspaceFolders", originalDescriptor)
}
vscode.workspace.getConfiguration = originalGetConfiguration
vscode.workspace.findFiles = originalFindFiles
// Clean up temp directories
await fs.rm(tempDir, { recursive: true, force: true })
await fs.rm(globalStoragePath, { recursive: true, force: true })
}
}
}
export async function createTestTracker(globalStoragePath?: string, taskId = "test-task-1") {
return await CheckpointTracker.create(taskId, globalStoragePath)
}

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