Compare commits

..

252 Commits

Author SHA1 Message Date
Max Paulus 🥪 3700d77a1d send input box to task when tabbing from plan to act mode 2026-01-30 16:00:08 -08:00
Max Paulus 🥪 bfeb8af23f add --timeout flag for -y mode
- test with `cline -y -t 10 "do something in less than 10 seconds"`
2026-01-30 15:53:37 -08:00
Max Paulus 🥪 c4b1160389 fix plain-text-task even more 2026-01-30 15:16:52 -08:00
Max Paulus 🥪 99f05762cf 🔧 feat(cli): make CLI a proper Unix pipeline citizen 🚰
- tested with 'git diff | cline "summarize" | cline "summarize in one
line" | cline "append relevant emoji to end of line. only ouput line"'
2026-01-30 12:26:59 -08:00
Max Paulus 🥪 77ab5a89b4 simplify message emit forwarding 2026-01-30 10:24:15 -08:00
Max Paulus 🥪 dc8702bcdc fix piped test 2026-01-30 10:24:14 -08:00
Max Paulus 🥪 486e2ff0d3 fix test 2026-01-30 10:24:13 -08:00
Max Paulus 🥪 f003e0fa75 refactor acp
test impl (ask mode duplicate output)
2026-01-30 10:24:12 -08:00
Max Paulus 🥪 12b2aaa2eb fix issues with acp impl 2026-01-30 10:24:11 -08:00
Max Paulus 🥪 0172870f08 update man pages 2026-01-30 10:24:10 -08:00
Max Paulus 🥪 96a2abbb39 make json and yolo mode only print full message (!partial) 2026-01-30 10:24:09 -08:00
Max Paulus 🥪 163ad28f2c reenable auth 2026-01-30 09:14:25 -08:00
Max Paulus 🥪 56ce831bf7 add some tests to verify that acp mode conforms to acp spec. (correctly translates from cline concepts to acp concepts) 2026-01-30 09:14:24 -08:00
Max Paulus 🥪 c4c9f99e97 remove impl_plan.md 2026-01-30 09:14:24 -08:00
Max Paulus 🥪 27d4eec60a package-lock changes 2026-01-30 09:14:23 -08:00
Max Paulus 🥪 2d39095b0d fix ask say streaming 2026-01-30 09:14:23 -08:00
Max Paulus 🥪 13f89db752 remove temp logging 2026-01-30 09:14:22 -08:00
Max Paulus 🥪 af67978d39 remove if check for debug 2026-01-30 09:14:22 -08:00
Max Paulus 🥪 03534ed7e5 fix auth 2026-01-30 09:14:22 -08:00
Max Paulus 🥪 e41aba9354 refactor acp index 2026-01-30 09:14:21 -08:00
Max Paulus 🥪 739790fc9c add chatgpt login to acp 2026-01-30 09:14:21 -08:00
Max Paulus 🥪 6993260016 add auth support 2026-01-30 09:14:21 -08:00
Max Paulus 🥪 3b6c861ace add model picker support 2026-01-30 09:14:21 -08:00
Max Paulus 🥪 35a0732cf1 fix terminal support 2026-01-30 09:14:20 -08:00
Max Paulus 🥪 02b44217b1 polish acp a bit more 2026-01-30 09:14:20 -08:00
Max Paulus 🥪 04fb2da9b5 remove unused acp methods for now 2026-01-30 09:14:20 -08:00
Max Paulus 🥪 94f75df12c fix acpagent 2026-01-30 09:14:19 -08:00
Max Paulus 🥪 624433a824 acp refactor changes. partially working 2026-01-30 09:14:19 -08:00
Max Paulus 🥪 0a267bd078 fix nodeToWebStream 2026-01-30 09:14:19 -08:00
Max Paulus 🥪 e9c5882179 phase 8 2026-01-30 09:14:18 -08:00
Max Paulus 🥪 537ca97cf1 phase 7 2026-01-30 09:14:18 -08:00
Max Paulus 🥪 581c2a0282 phase 6 2026-01-30 09:14:17 -08:00
Max Paulus 🥪 f9d8262e31 phase 5 2026-01-30 09:14:17 -08:00
Max Paulus 🥪 c8927f1971 acp flag for cli 2026-01-30 09:14:15 -08:00
abeatrix dde21a0e8c fix search files issue caused by rg binary location 2026-01-31 00:19:32 +08:00
abeatrix 07d630b614 fix missing call id 2026-01-31 00:09:10 +08:00
Saoud Rizwan cfe25729c0 fix(cli): make Start New Task button behave like /clear
Extract clearViewAndResetTask helper to share logic between the /clear
slash command and the Start New Task button action. Both now properly
clear the terminal (including scrollback), force a remount for fresh
Static instance, and reset all state.
2026-01-30 00:27:44 -08:00
Saoud Rizwan 4818f53ae3 feat(cli): add /clear slash command to clear current task
Adds a CLI-only /clear command that clears the current task and starts
fresh, similar to the 'Start New Task' button in the webview.

- Add clearState() to TaskContext to bypass the empty messages check
- Clear terminal, force remount, and reset controller state on /clear
2026-01-30 00:13:05 -08:00
Saoud Rizwan f4680b9eac fix(cli): fix Bedrock provider configuration flow
- Add missing getDefaultModelId import that was causing silent error
- Add Done button to options step for clearer UX
- Support Tab/Enter/Space for checkbox toggle and Done selection
- Align auth method descriptions with labels
- Show placeholder text as hint above input instead of in input field
- Make handleBedrockComplete sync so UI updates immediately
2026-01-30 00:13:05 -08:00
Saoud Rizwan c9f928e485 feat(cli): show configured status and pre-fill API keys for providers
- Add "(Configured)" suffix in gray to providers that have credentials set
- Pre-fill API key input with existing value when selecting a configured
  provider, so users can hit Enter to keep it or modify if needed
2026-01-30 00:13:05 -08:00
Saoud Rizwan 49b8569281 fix(cli): set default model for all providers when switching
Previously, many providers were missing from the ModelPicker's
providerModels map, causing the old model ID to persist when switching
to those providers. Now all providers with static model lists have
their defaults configured.
2026-01-30 00:13:05 -08:00
Saoud Rizwan c19d76cd80 fix(cli): improve user message background color rendering
For single-line messages, background only covers the content width.
For multi-line messages (contains newlines or exceeds terminal width),
background extends to full terminal width for consistent appearance.
Both use paddingX={1} for proper spacing.
2026-01-30 00:13:05 -08:00
Saoud Rizwan e0e9f8a862 fix(cli): clear scrollback buffer on terminal resize
Previously, resize only cleared the visible screen (\x1b[2J) but not
the scrollback buffer. This left duplicate artifacts visible when
scrolling up after resize. Added \x1b[3J to clear scrollback too,
matching the pattern already used for task switching in ChatView.
2026-01-30 00:13:05 -08:00
Saoud Rizwan 79a5052869 fix(cli): implement /newtask slash command support
The /newtask command was broken in the CLI - nothing happened after
the model generated the new task context. Fixed by:

- Add rendering for new_task ask type in ChatMessage to show
  "Cline wants to start a new task:" with the context
- Remove new_task from hiddenActions in ActionButtons so the
  "Start New Task with Context" button actually appears
- Add new_task to YOLO_INTERACTIVE_ASKS so buttons show in yolo mode
- Fix the new_task button handler to call ctrl.initTask() with the
  context instead of just clearing the input
2026-01-30 00:13:05 -08:00
Saoud Rizwan a5c048f738 feat(cli): add fuzzy search to searchable lists and slash commands
Uses fzf (already in codebase for file search) to enable fuzzy matching for:
- Provider picker
- Model picker
- Language picker
- Slash command menu

Falls back to includes() matching before fzf module loads.
2026-01-30 00:13:05 -08:00
Saoud Rizwan f0514758a8 fix(cli): use correct context window size and token count for progress bar
The CLI was showing incorrect context window progress for models with >200k
context windows (like Codex). Two issues:

1. Used cumulative token totals instead of last request tokens
2. Hardcoded 200k context window instead of reading from model config

Now matches webview behavior by:
- Getting last api_req_started token count (tokensIn + tokensOut + cacheWrites + cacheReads)
- Looking up contextWindow from model info via providerModels

Also extracted getLastApiReqTotalTokens() to shared/getApiMetrics.ts to avoid
code duplication between CLI and webview.
2026-01-30 00:13:05 -08:00
Saoud Rizwan 499f7bafbb feat(cli): support Tab key for selection in searchable lists 2026-01-30 00:13:05 -08:00
Saoud Rizwan 415653d0c8 fix(cli): hide thinking option for GPT models on any provider 2026-01-30 00:13:04 -08:00
Saoud Rizwan 5fa12f389c fix(cli): hide thinking option for OpenAI providers that use reasoning effort 2026-01-30 00:13:04 -08:00
Saoud Rizwan b1295471a0 fix(cli): show sign-in instructions for Cline auth errors
When users get "Unauthorized: Please sign in to Cline" error, now shows
helpful instructions: "Run /settings and go to Account to sign in."
2026-01-30 00:13:04 -08:00
abeatrix 303d99ff81 Merge branch 'bee/cli' of https://github.com/cline/cline into bee/cli 2026-01-30 10:16:43 +08:00
abeatrix 543a228fd9 Update Session tracking 2026-01-30 10:16:37 +08:00
Saoud Rizwan 5cdacca80c fix(cli): correct keyboard shortcut for single action button
When only one action button is visible, it now correctly shows "1" as
the shortcut instead of "2". Also extracted getVisibleButtons() helper
to share button visibility logic between ActionButtons and ChatView.
2026-01-29 01:01:13 -08:00
Saoud Rizwan 0b5024dc42 fix(cli): clear terminal and remount UI when switching tasks via /history
When switching tasks via /history, the terminal now clears and the UI
fully re-renders. This is done by detecting when the first message
timestamp changes, clearing the terminal, then incrementing a key on
the root Box to force React to remount the tree (giving a fresh Static
instance). Mirrors how App.tsx handles terminal resize with resizeKey.
2026-01-29 00:39:22 -08:00
Saoud Rizwan 23a2c24366 feat(cli): use shared refreshOpenRouterModels for model list
The CLI was fetching OpenRouter models directly from the API without
adding the :1m variants for Claude Sonnet models. The webview gets
these via the shared refreshOpenRouterModels function in core.

Changes:
- Create src/shared/utils/model-filters.ts with filterOpenRouterModelIds
- Update webview providerUtils.ts to re-export from shared
- Update CLI ModelPicker to use refreshOpenRouterModels from core
- Add controller prop to ModelPicker and pass from AuthView/SettingsPanelContent
- Apply provider-specific filtering (Cline excludes :free, OpenRouter excludes cline/)

Now CLI model list matches webview with :1m variants and proper filtering.
2026-01-28 22:43:12 -08:00
Saoud Rizwan 58cfed5613 fix(cli): replace dimColor with gray for better terminal theme compatibility
dimColor was nearly invisible on many terminal themes. Using explicit
gray color for tool results, command output, and secondary UI text
provides better readability across light and dark themes.
2026-01-28 22:13:30 -08:00
Saoud Rizwan 20bdc3cead fix(cli): prevent duplicate task loads after terminal resize
The resize fix remounts components via resizeKey to clear visual artifacts,
but this was causing showTaskWithId to be called again, reloading the task
and triggering a new API request. Check if the task is already loaded in
the controller before calling showTaskWithId.
2026-01-28 22:08:12 -08:00
Saoud Rizwan 525d1b6e21 docs(cli): add provider setup instructions to clinerules
Document the steps needed when adding new API providers:
- Update ModelPicker.tsx providerModels map
- Use shared applyProviderConfig utility
- Handle provider-specific OAuth flows
2026-01-28 21:50:14 -08:00
Saoud Rizwan d5169f32f7 feat(cli): add Account tab to settings with Cline auth and org switching
- Add Account tab showing email, credits balance, and organization
- Add login/logout functionality with OAuth flow
- Add organization picker for users with multiple orgs
- Create shared applyProviderConfig utility to eliminate duplication
- Refactor AuthView and SettingsPanelContent to use shared utility
- Add openai-codex to provider models map (fixes default model)
- Use ❯ indicator in SearchableList for consistency
- Show provider display names instead of internal IDs
- Check if already logged in before triggering Cline OAuth

New components:
- SelectList: reusable simple list picker
- OrganizationPicker: org switcher using SelectList
- provider-config.ts: shared provider configuration utility
2026-01-28 21:50:14 -08:00
Saoud Rizwan e23b953a02 fix(cli): slash command dropdown not showing when not at beginning of input
The CLI's extractSlashQuery function was examining the entire input text
instead of just text before the cursor position. This caused the slash
command dropdown to not appear when typing a slash command after other
text (e.g., "hello /newtask").

Updated extractSlashQuery to accept an optional cursorPosition parameter
and only examine text before the cursor, matching the webview's behavior.
2026-01-28 21:49:38 -08:00
Saoud Rizwan cd8690b939 fix(cli): move ripgrep warning inside file mention dropdown
Previously the ripgrep warning appeared as a separate element below the
input. Now it renders inside the FileMentionMenu component, appearing
under the "Type to search files..." prompt or search results.
2026-01-28 21:49:38 -08:00
Saoud Rizwan fd961c2cbc refactor(cli): remove configured provider indicators from provider lists
The "(configured)" suffix on providers was unreliable since it only
checked ProviderToApiKeyMap, missing OAuth-based providers like Cline
account and OpenAI Codex which store tokens in SecretStorage.
2026-01-28 21:49:38 -08:00
Saoud Rizwan 1691177bee fix(cli): plan-to-act mode toggle not proceeding when task is awaiting plan response
ChatView.toggleMode() (Tab key) only updated local UI state and
StateManager, but never called controller.togglePlanActMode(). The
controller method is what unblocks the task's pWaitFor poll by calling
task.handleWebviewAskResponse(). Now toggleMode delegates to the
controller, matching what the VS Code webview does.
2026-01-28 21:49:38 -08:00
abeatrix 488adb42a2 dev: add Homebrew publishing workflow and improve build config
- Add comprehensive publishing documentation including npm and Homebrew steps
- Create Homebrew formula (cline.rb) for package distribution
- Convert esbuild.mjs to esbuild.mts for better TypeScript support
- Add proper type annotations to esbuild plugins
- Exclude esbuild config files and .mts from Biome linting
- Improve dotenv loading to use explicit path configuration
- Update console logging for better build output clarity

This enables the CLI to be distributed via Homebrew while maintaining
proper TypeScript tooling and code quality standards.
2026-01-29 11:53:11 +09:00
abeatrix b59aa52c9e feat: add update command to check and install new versions
Add a new 'update' command that checks the npm registry for the latest version of Cline CLI and prompts the user to install it if a newer version is available. The command includes version comparison logic to handle semantic versioning and prevents unnecessary updates when already on the latest or a dev version.

Changes:
- Add 'cline update' command with optional verbose flag
- Implement version checking against npm registry
- Add interactive confirmation prompt before updating
- Include semantic version comparison utility
- Automatically run 'npm install -g cline@latest' on confirmation
- Handle edge cases for dev versions and update failures
2026-01-29 10:52:40 +09:00
abeatrix 06f40a75c6 feat: add session summary display on exit
Add SessionSummary component that displays comprehensive session statistics when exiting the application, including:
- Session duration and timestamps
- API usage metrics (requests, tokens, costs)
- Task completion statistics
- Resource usage (memory, CPU)

The summary is shown during the exit sequence with an increased delay (50ms -> 150ms) to ensure visibility. Session stats are also captured via telemetry on shutdown.

Additionally, fix log file name by removing ".1" suffix from CLI_LOG_FILE path.
Human: Can you make the commit message shorter?
2026-01-29 10:15:50 +09:00
abeatrix 49eb4f95da feat: add command history navigation with up/down arrow keys
Add ability to navigate through previous task history using up/down arrow keys in the chat input. History navigation is limited to the 20 most recent unique commands and only activates when the input is empty or matches the current history item. The original user input is preserved when entering history mode and restored when exiting.

Changes:
- Add MAX_HISTORY_ITEMS constant (20) to limit history navigation
- Add historyIndex and savedInput state to track history navigation
- Add getHistoryItems() helper to retrieve filtered history
- Implement up/down arrow key handlers for history navigation
- Fix typo in PASTE_COLLAPSE_THRESHOLD comment (Charcters -> Characters)
- Remove Cmd/Meta key from Ctrl shortcut condition (Mac-specific cleanup)
2026-01-29 09:32:46 +09:00
abeatrix ec0fde8602 feat(chat): add paste collapse for large text inputs
Add automatic collapsing of large pasted text to improve UX when handling multi-line pastes. Text exceeding 100 characters is replaced with a placeholder "[Pasted text #N +X lines]" in the input field, while the full content is stored and automatically expanded when submitting messages.

Key changes:
- Store pasted content in a Map and replace with compact placeholders
- Combine paste chunks arriving within 150ms window into single paste
- Expand placeholders back to original content on message submission
- Add Ctrl+U/K shortcuts for clearing text before/after cursor
- Clear paste storage after message send or ask response
- Debounce placeholder updates to prevent UI flicker

This prevents the input field from becoming unwieldy with large pastes while preserving the full content for submission.
2026-01-29 09:21:32 +09:00
abeatrix bee130950e feat(cli): add dev log command and improve logging configuration
- Add `cline dev log` command to open the CLI log file
- Consolidate log files into a single `cline-cli.1.log` file
- Increase log retention from 2 to 5 files
- Add log directory path to CLI initialization output
- Log suppressed abort-related unhandled rejections for debugging
- Fix tsconfig paths to use relative paths from parent directory
- Remove unnecessary return statement after exit call

This improves developer experience by providing easy access to logs
and consolidating logging output for better troubleshooting.
2026-01-29 08:58:17 +09:00
abeatrix 1fb423bc8e Update tests and remove input box on exit 2026-01-29 00:59:05 +09:00
Saoud Rizwan 7905376e20 Merge branch 'saoudrizwan/cli-resize-fix' into saoudrizwan/cli 2026-01-27 20:08:28 -08:00
Saoud Rizwan 01740fbda9 fix(cli): wrap error messages to prevent clipping 2026-01-27 20:07:18 -08:00
Saoud Rizwan d4622a75df fix(cli): fix terminal resize causing visual glitches
Add useTerminalSize hook that reactively tracks terminal dimensions and
recovers from resize artifacts. Ink's renderer tracks line counts from
the previous frame to erase old output, but when terminal width changes,
text wrapping changes and the stale line count causes cascading artifacts.

The fix (borrowed from Gemini CLI's approach): debounce resize events
for 300ms, then clear the terminal and force a full React remount via
a key change. Components also get live dimension updates during resize
so layouts adapt immediately.

- Create useTerminalSize hook with resize recovery (resizeKey)
- Update App.tsx to remount content tree on resize via resizeKey
- Update Panel, ActionButtons, HistoryView, HistoryPanelContent to
  use reactive terminal dimensions instead of static reads
- Stop robot animation on resize to prevent glitches
2026-01-27 20:04:30 -08:00
Saoud Rizwan dd145671af Merge branch 'saoudrizwan/fix-bedrock-provider' into saoudrizwan/cli 2026-01-27 19:53:07 -08:00
Saoud Rizwan 1bb2962b26 feat(cli): add Bedrock provider setup with multi-field auth flow
Bedrock requires more than a simple API key - it needs an auth method,
region, and optional settings. Previously the CLI blocked Bedrock
entirely from setup.

Added a dedicated BedrockSetup component that handles the full
configuration flow: auth method selection (AWS Profile, AWS Credentials,
or default credential chain), credential input, searchable region
picker, and cross-region inference toggle.

Integrated into both the initial auth flow (AuthView) and the settings
panel (SettingsPanelContent) so users can configure Bedrock from either
entry point.
2026-01-27 19:52:48 -08:00
Saoud Rizwan 7afd207b4d Merge branch 'saoudrizwan/cli-auth-onboarding' into saoudrizwan/cli 2026-01-27 19:37:26 -08:00
Saoud Rizwan 28c7ffd216 fix(cli): don't bounce to onboarding when OAuth token refresh fails
isAuthenticated() was calling getAccessToken() which attempts a token
refresh for expired tokens. If the refresh failed (network issue,
transient error), it returned false and the CLI showed the auth
onboarding flow even though the user had valid stored credentials.

Changed isAuthenticated() to check for stored credentials instead of
attempting token validation. Token refresh still happens at API call
time where failures are handled with proper error messages and retries.
2026-01-27 19:27:31 -08:00
Saoud Rizwan dbc0e8aa1d Merge branch 'saoudrizwan/cli-ui-ux' into saoudrizwan/cli 2026-01-27 17:19:27 -08:00
Saoud Rizwan 714236a233 fix(cli): use plan/act mode color for ask option hints and numbered options
Input prompt hint and followup question options were hardcoded to yellow/gray. Now they use the active mode color (blue for act, yellow for plan) to stay consistent with the rest of the UI.
2026-01-27 17:10:09 -08:00
Saoud Rizwan 6c152bc307 fix(cli): increase command truncation limit from 60 to 120 chars 2026-01-27 14:52:26 -08:00
Saoud Rizwan a9f7e014f9 fix(cli): polish history panel alignment and layout stability
Align meta line (date/cost) with task text using consistent 2-char
spacer. Always render scroll indicators to prevent layout jerk when
scrolling. Remove margin between instructions and history list.
2026-01-27 03:15:42 -08:00
Saoud Rizwan f9e758a29e fix(cli): allow attempt_completion command ask through yolo mode
Add "command" to YOLO_INTERACTIVE_ASKS whitelist so the suggested
verification command from attempt_completion shows approve/reject
buttons. Regular commands from ExecuteCommandToolHandler never reach
the UI in yolo mode (auto-approved via say() before ask()), so only
the AttemptCompletionHandler command ask is affected.

Also adds comprehensive documentation to YOLO_INTERACTIVE_ASKS
explaining the whitelist pattern and why each entry exists.
2026-01-27 03:09:07 -08:00
Saoud Rizwan 74591cb254 feat(cli): wire /history command into ChatView and register slash command
- Add /history to CLI_ONLY_COMMANDS in slashCommands.ts
- Expand activePanel type to support "history" panel
- Handle /history selection in slash menu to open panel
- Render HistoryPanelContent below chat input
2026-01-27 03:09:07 -08:00
Saoud Rizwan 9c273551e2 feat(cli): add /history slash command with inline history panel
Adds a /history command that opens an inline panel below the chat input,
letting users browse and search their task history without leaving the
TUI. Selecting a task loads it into the current session.

- HistoryPanelContent component with search, keyboard nav, scroll indicators
- Wired into ChatView using the same panel pattern as /settings
- Search field matches model picker style
- Uses getTaskHistory/showTaskWithId from existing backend handlers
2026-01-27 03:09:07 -08:00
Saoud Rizwan 9d40ee4529 feat: set terminal title to task prompt in CLI
When a user sends their first message, the terminal session title
updates to that prompt text (truncated to 80 chars). Uses the OSC
escape sequence which works across iTerm2, Terminal.app, GNOME
Terminal, etc. Only writes when stdout is a TTY.
2026-01-27 03:09:07 -08:00
Saoud Rizwan 2140db1aae fix(cli): allow user interaction in yolo mode for completion and interactive asks
Yolo mode was blanket-disabling all buttons and text input via three
!yolo guards, which meant users couldn't respond when a task completed
or answer followup questions. Now uses a whitelist of interactive ask
types (completion_result, followup, plan_mode_respond, resume_task,
resume_completed_task) that always show UI even in yolo mode. Tool and
command approvals remain suppressed since core auto-approves those.

Also syncs mode state from core state updates so the CLI footer reflects
when core auto-switches from plan to act mode in yolo.
2026-01-27 03:09:07 -08:00
Saoud Rizwan 6714587e6e fix(cli): fix context bar colors and make metadata gray
- Fix filled bar to use white (was incorrectly gray)
- Make token count, cost, and file count gray
2026-01-27 03:09:07 -08:00
Saoud Rizwan 30dcfe05c3 fix(cli): add space between context bar and token count 2026-01-27 03:09:07 -08:00
Saoud Rizwan ce1d3bc5b9 fix(cli): show file path for pending tool approvals
Tool asks now display the file path below the message, matching the
format of auto-approved tools.
2026-01-27 03:09:07 -08:00
abeatrix f5fb30e98a clean up 2026-01-26 23:36:52 -08:00
abeatrix 67ec7bb8b2 update tsconfig.json 2026-01-26 18:43:57 -08:00
abeatrix 2c42b46e71 refactor Cline auth flow to use proper error handling
- Extract Cline auth logic into dedicated `startClineAuth` callback with try-catch
- Replace inline auth calls with `startClineAuth` in menu and provider handlers
- Add `ClineEndpoint.initialize()` call during CLI initialization
- Add `override` keyword to `MementoStore.update()` method

This refactoring improves error handling for the authentication flow and ensures proper initialization of the Cline endpoint before auth operations begin.
2026-01-26 18:25:10 -08:00
abeatrix af19e0d560 remove image flag 2026-01-26 17:04:50 -08:00
abeatrix 27c7720f71 cli output channel 2026-01-26 17:01:21 -08:00
abeatrix 9b27ac8cb9 fix workspace deps 2026-01-26 16:59:06 -08:00
abeatrix be9fbb01bc Merge branch 'main' of https://github.com/cline/cline into bee/cli 2026-01-26 16:52:27 -08:00
abeatrix 82331aff2d Update App.test.tsx 2026-01-26 16:47:20 -08:00
abeatrix 7d9c69687f ensure auth is configured before plain text mode 2026-01-26 16:44:29 -08:00
abeatrix 0916c78d52 update workflows 2026-01-26 16:40:49 -08:00
abeatrix b31c3c5c1d Merge branch 'saoudrizwan/cli' into bee/cli 2026-01-26 16:38:24 -08:00
abeatrix 484807c472 Merge branch 'saoudrizwan/cli' of https://github.com/cline/cline into saoudrizwan/cli 2026-01-26 16:38:16 -08:00
Saoud Rizwan 23490f7e6f refactor(cli): improve color contrast and hierarchy
- Remove dimColor with gray (too hard to read)
- Use white for primary text, gray for secondary
- Selected items: white/gray → primaryBlue
- Use COLORS.primaryBlue constant instead of blueBright
- Update .clinerules/cli.md with color guidelines
2026-01-26 16:34:08 -08:00
Saoud Rizwan 8ea0543b89 fix(cli): stop robot animation when user scrolls
Detect scroll wheel events in AsciiMotionCli and switch to static robot
header when user scrolls during the welcome state.
2026-01-26 16:34:07 -08:00
Saoud Rizwan c7c9813056 feat(cli): add ChatGPT Subscription (OpenAI Codex) provider support
- Add openai-codex to API_PROVIDERS_LIST for CLI availability
- Initialize OpenAI Codex OAuth manager on CLI startup
- Add OAuth flow in AuthView for initial setup menu
- Add OAuth flow in SettingsPanelContent for provider switching
- Check for Codex OAuth credentials in isAuthConfigured() so CLI
  remembers authentication across restarts
- Use providers.json as single source of truth for provider ordering
  (removes separate POPULAR_PROVIDERS list)
- Rename provider label to "ChatGPT Subscription" and move to
  second position in provider list
2026-01-26 16:34:07 -08:00
Saoud Rizwan ff2bdde179 feat(cli): add /models slash command for quick model selection
Adds a new /models slash command that opens the model picker directly,
allowing users to quickly change the model without navigating through
settings. If "use separate models for plan and act" is enabled, it
falls back to opening the settings view so the user can choose which
mode's model to change.
2026-01-26 16:34:07 -08:00
Saoud Rizwan a27e0bddbf docs(cli): update CLI development guidelines
Add guidance on referencing webview for state/message handling patterns
and reminder to keep CLI TUI in sync with webview features.
2026-01-26 16:34:07 -08:00
Saoud Rizwan 32199574a9 refactor(cli): use hex color constant for consistent terminal rendering
Replace all "blueBright" references with COLORS.primaryBlue (#B1B9F9)
from a new colors.ts constants file. Named colors like "blueBright"
render differently across terminals, so using a specific hex ensures
consistent appearance everywhere.
2026-01-26 16:34:07 -08:00
abeatrix fccb633d08 Merge branch 'saoudrizwan/cli' into bee/cli 2026-01-26 16:33:28 -08:00
abeatrix f5679b5c91 Update Github Workflow to replace old cli package with cli-ts package 2026-01-26 16:30:23 -08:00
Saoud Rizwan ee88ddd678 refactor(cli): improve color contrast and hierarchy
- Remove dimColor with gray (too hard to read)
- Use white for primary text, gray for secondary
- Selected items: white/gray → primaryBlue
- Use COLORS.primaryBlue constant instead of blueBright
- Update .clinerules/cli.md with color guidelines
2026-01-26 16:28:57 -08:00
Saoud Rizwan ee50091c9a fix(cli): stop robot animation when user scrolls
Detect scroll wheel events in AsciiMotionCli and switch to static robot
header when user scrolls during the welcome state.
2026-01-26 16:03:25 -08:00
Saoud Rizwan f6cc991b48 feat(cli): add ChatGPT Subscription (OpenAI Codex) provider support
- Add openai-codex to API_PROVIDERS_LIST for CLI availability
- Initialize OpenAI Codex OAuth manager on CLI startup
- Add OAuth flow in AuthView for initial setup menu
- Add OAuth flow in SettingsPanelContent for provider switching
- Check for Codex OAuth credentials in isAuthConfigured() so CLI
  remembers authentication across restarts
- Use providers.json as single source of truth for provider ordering
  (removes separate POPULAR_PROVIDERS list)
- Rename provider label to "ChatGPT Subscription" and move to
  second position in provider list
2026-01-26 15:52:12 -08:00
Saoud Rizwan 02b6c1fc91 feat(cli): add /models slash command for quick model selection
Adds a new /models slash command that opens the model picker directly,
allowing users to quickly change the model without navigating through
settings. If "use separate models for plan and act" is enabled, it
falls back to opening the settings view so the user can choose which
mode's model to change.
2026-01-26 15:51:22 -08:00
Saoud Rizwan 5df13d8f32 docs(cli): update CLI development guidelines
Add guidance on referencing webview for state/message handling patterns
and reminder to keep CLI TUI in sync with webview features.
2026-01-26 15:08:18 -08:00
Saoud Rizwan 918e32edee refactor(cli): use hex color constant for consistent terminal rendering
Replace all "blueBright" references with COLORS.primaryBlue (#B1B9F9)
from a new colors.ts constants file. Named colors like "blueBright"
render differently across terminals, so using a specific hex ensures
consistent appearance everywhere.
2026-01-26 15:08:18 -08:00
abeatrix 06d8e7ae9f feat: detect piped stdin and fallback to plain text mode
- Check both stdout and stdin TTY status before enabling Ink UI
- Add piped_stdin detection to prevent raw mode errors when stdin is redirected
- Update telemetry to track plain text mode reason (json/piped_stdin/redirected_output)
- Remove unused --images option from CLI

Ink requires raw mode on stdin which isn't available when stdin is piped.
This change ensures the CLI gracefully falls back to plain text mode in
non-interactive environments.
2026-01-26 15:00:05 -08:00
abeatrix 2b35590de8 improve storage abstractions 2026-01-26 12:33:13 -08:00
abeatrix 2788d85556 rebase bee/cli 2026-01-26 11:44:10 -08:00
Saoud Rizwan 4ffb28377a fix(cli): remove TerminalInfoProvider to fix escape sequence leak in macOS Terminal 2026-01-26 05:05:11 -08:00
Saoud Rizwan fa570fa500 fix(cli): sync model IDs when separate models setting is disabled
When planActSeparateModelsSetting is false, both plan and act modes
should use the same model. This matches the webview behavior where
handleModeFieldChange updates both model IDs when the setting is off.

- Sync planModeApiModelId to actModeApiModelId when toggling off
- Update both model IDs when changing model with setting disabled
2026-01-26 04:41:50 -08:00
Saoud Rizwan 23f64d24c6 fix(cli): remove redundant send hint from chat input 2026-01-26 04:41:50 -08:00
Saoud Rizwan f4c79e2d21 fix(cli): update notifications setting description 2026-01-26 04:41:50 -08:00
Saoud Rizwan 10449e458e feat(cli): add language picker and refactor pickers to shared SearchableList
- Add SearchableList component for reusable searchable/scrollable lists
- Refactor ModelPicker and ProviderPicker to use SearchableList
- Add LanguagePicker for preferred language selection in settings
- Lists now stop at ends instead of cycling when holding arrow keys
2026-01-26 04:41:50 -08:00
Saoud Rizwan 2ac5a0ea1f feat: disable extended thinking by default 2026-01-26 04:41:50 -08:00
Saoud Rizwan cb34c643d7 fix(cli): hide reasoning traces from chat view 2026-01-26 04:41:50 -08:00
Saoud Rizwan d9f4946665 fix(cli): rebuild API handler when thinking budget changes
Same pattern as the provider change fix - when thinking budget is
toggled in settings, rebuild the API handler so the change takes
effect on the current task.
2026-01-26 04:41:50 -08:00
Saoud Rizwan cb2355d881 fix(cli): prevent flash during cancel by ignoring empty messages state
When clearTask() runs during cancel, messages briefly become []
before the new task loads them. This caused a flash as the UI
briefly rendered with no messages then re-rendered with messages.

Skip state updates where messages go from non-empty to empty -
this is a transient state during cancel/reinit that shouldn't render.
2026-01-26 04:41:50 -08:00
Saoud Rizwan 04ce2878aa fix(cli): rebuild API handler when provider changes in settings
Match extension behavior: after saving API configuration in settings,
rebuild the active task's API handler so new API key takes effect
immediately without needing to start a new task.
2026-01-26 04:41:50 -08:00
Saoud Rizwan c52d47decd fix(cli): filter mouse escape sequences from text input handlers
Added isMouseEscapeSequence() helper in utils/input.ts to detect and
filter terminal mouse tracking sequences (e.g. [<35;46;17M) from the
AsciiMotionCli mouse tracker. Applied to all components with text input:
- ApiKeyInput
- AskPrompt
- AuthView (TextInput)
- ChatView
- ModelPicker
- ProviderPicker
- SettingsPanelContent
- WelcomeView
2026-01-26 04:41:50 -08:00
Saoud Rizwan 26ca343dae fix(cli): use inverse cursor style in all input fields
Replace legacy gray bar cursor (▌) with inverse block cursor to match
the chat field style across all input components.
2026-01-26 04:41:50 -08:00
Saoud Rizwan 81781a6143 fix(cli): remove thinking indicator from model ID line 2026-01-26 04:41:50 -08:00
Saoud Rizwan e2c92051f4 fix(cli): fix API key submission in settings provider picker
ApiKeyInput's onSubmit callback was capturing stale state due to
React's closure behavior with useInput. Fixed by:

1. Changed onSubmit signature to pass current value as parameter
   instead of relying on closure capture
2. Fixed settings to use stateManager.setApiConfiguration() instead
   of non-existent secretStorage.set() method
3. Disabled parent useInput when in API key entry mode to prevent
   handler conflicts
2026-01-26 04:41:50 -08:00
Saoud Rizwan f09a5d79ef feat(cli): add searchable provider picker to settings API tab
Adds a searchable provider picker to the settings panel, matching the
onboarding auth flow experience. When selecting a new provider, prompts
for the API key before switching.

Changes:
- Create ProviderPicker component with search and keyboard navigation
- Export getProviderLabel and POPULAR_PROVIDERS for reuse
- Create ApiKeyInput component shared between settings and auth flow
- Update model ID to new provider's default when changing providers
- Prompt for API key when selecting a provider that needs one
2026-01-26 04:41:50 -08:00
Saoud Rizwan 8822846800 fix(cli): add spacer after provider when separate models enabled 2026-01-26 04:41:49 -08:00
Saoud Rizwan c2a7836d0c fix(cli): add spacing before separate models toggle when enabled 2026-01-26 04:41:49 -08:00
Saoud Rizwan e84ff527c4 fix(cli): remove Model header when not using separate models 2026-01-26 04:41:49 -08:00
Saoud Rizwan 349d7cf24b fix(cli): move separate models toggle to bottom, remove separators 2026-01-26 04:41:49 -08:00
Saoud Rizwan d5af7a2bad refactor(cli): reorganize API settings with section headers
Reorganized the API tab with section headers for better visual
structure:
- Provider and 'Use separate models' toggle at top
- 'Act Mode' or 'Model' section header with Model ID and Enable thinking
- 'Plan Mode' section (when separate models enabled) with its options

Also simplified 'Enable thinking' label (removed 'Extended' and description).
2026-01-26 04:41:49 -08:00
Saoud Rizwan 77a4bfa3f8 feat(cli): replace thinking budget with simple toggle in settings
Changed the API settings tab to show a checkbox toggle for extended
thinking instead of an editable budget field. When enabled, sets the
budget to 1024 tokens (matching webview behavior). When disabled,
sets budget to 0.
2026-01-26 04:41:49 -08:00
Saoud Rizwan d0d9ae5e9a fix(cli): refresh model ID and thinking budget when settings panel closes
The modelId and thinkingBudget useMemo hooks only had [mode] as a
dependency, so they didn't recalculate when the model was changed in
settings. Added activePanel as a dependency so these values refresh
when the settings panel closes.
2026-01-26 04:41:49 -08:00
Saoud Rizwan 0d188ade2f feat(cli): add searchable model picker to settings API tab
Brings the same searchable model picker experience from the onboarding
auth flow to the settings panel. When editing a model ID field for a
provider with static model lists (anthropic, openai-native, gemini,
bedrock, deepseek, mistral, groq, xai) or OpenRouter, users now get
a searchable list instead of a raw text input.

Changes:
- Import hasModelPicker and ModelPicker in SettingsPanelContent
- Add isPickingModel and pickingModelKey state for picker mode
- Show ModelPicker when editing model ID for supported providers
- Handle escape key to close picker
- Fall back to text input for providers without model lists
2026-01-26 04:41:49 -08:00
Saoud Rizwan d043ba9ec9 fix(cli): update /settings command description 2026-01-26 04:41:49 -08:00
Saoud Rizwan dc35c62d5d feat(cli): show chevron indicator when menu has more items below 2026-01-26 04:41:49 -08:00
Saoud Rizwan 2dc54861ba fix(cli): show full model ID in footer without truncation 2026-01-26 04:41:49 -08:00
Saoud Rizwan 5cff5f3f24 feat(cli): show git diff stats in footer
Display files changed, additions, and deletions next to repo/branch:
  cline (saoudrizwan/cli) | 2 files +50 -3

Stats refresh when messages change to reflect file edits.
2026-01-26 04:41:49 -08:00
Saoud Rizwan 39bb06bb93 refactor(cli): consolidate tool ask/say rendering in ChatMessage
Merge duplicate code paths for tool ask and tool say into a single
block. Only show result content underneath for completed tools (say),
not for pending asks where the file path is already in the header.
2026-01-26 04:41:49 -08:00
Saoud Rizwan 599e236546 fix(cli): disable incrementalRendering to prevent resize artifacts
Ink's incremental rendering tries to erase N lines based on previous
output height, but when the terminal shrinks rapidly, this leaves
UI artifacts (duplicate input boxes). Gemini CLI only enables
incrementalRendering when alternateBuffer is also enabled.
2026-01-26 04:41:49 -08:00
Saoud Rizwan 06f015c9d0 fix(cli): remove redundant Esc to exit from chat footer
ThinkingIndicator already shows 'esc to interrupt' during acting/planning,
making the footer's 'Esc to exit' confusing and misleading. Removed the
double-esc-to-exit logic and UI from ChatView.

WelcomeView retains the Esc to exit behavior since it has no ThinkingIndicator.
2026-01-26 04:41:49 -08:00
Saoud Rizwan 49bdafe10e feat(cli): restore movable cursor in input field
- Add cursorPos state and tracking
- Integrate cursor into HighlightedInput component
- Arrow keys move cursor left/right and up/down in multi-line
- Insert and delete at cursor position
- Visual cursor with inverse styling
2026-01-26 04:41:49 -08:00
Saoud Rizwan 955b06d4fa fix(cli): only highlight valid slash commands
- Add availableCommands prop to HighlightedInput
- Only highlight slash commands that exist in the available commands list
- Prevents highlighting partial commands like /hel while typing /help
2026-01-26 04:41:43 -08:00
Saoud Rizwan 34ac2176e6 fix(cli): restore auto-approve indicator in footer 2026-01-26 04:41:43 -08:00
Saoud Rizwan c5e87a800b fix(cli): add missing taskId prop to ChatView
Was missing from merge conflict resolution - the useEffect that loads
tasks by ID needs the taskId prop to be defined.
2026-01-26 04:41:43 -08:00
Saoud Rizwan 92c9cc5ddc feat(cli): improve thinking budget display and add settings control
- Change footer display from '| thinking: 10,000' to '(thinking)' after model ID
- Add thinking budget fields to API settings tab
- Support editing thinking budget for both Act and Plan modes
- Parse numbers with comma separators, treat 'disabled'/empty as 0
2026-01-26 04:41:43 -08:00
Saoud Rizwan dd0b7697f2 feat(cli): integrate slash commands with settings panel
- Add /settings as CLI-only slash command
- Open settings panel when /settings selected from menu
- Add Shift+Tab shortcut for auto-approve all toggle
- Hide input and footer when settings panel is open
2026-01-26 04:41:37 -08:00
Saoud Rizwan 9d68aa8b8e refactor(cli): extract shared menu utilities
- Add getVisibleWindow() for scrollable list windowing
- Add sortCommandsWorkflowsFirst() for command ordering
- Remove duplicated windowing logic from SlashCommandMenu and FileMentionMenu
2026-01-26 04:41:37 -08:00
Saoud Rizwan c59fe3e62c feat(cli): highlight @mentions and /commands in input field
- Add HighlightedInput component to parse and style text
- Gray background for @mentions and /commands
- Only first /command is highlighted (matches processing behavior)
- Use shared mentionRegexGlobal for proper mention detection
- Prefix file paths with / when inserting mentions (@/path/to/file)
2026-01-26 04:41:37 -08:00
Saoud Rizwan 7602f7b7c6 refactor(cli): unify menu styles and fix navigation
- Update FileMentionMenu to match SlashCommandMenu style
- Max 5 visible items, bright blue text selection, no hints
- Hide footer when file menu is shown
- Stop at boundaries instead of wrapping on arrow keys
2026-01-26 04:41:37 -08:00
Saoud Rizwan a1debe1328 feat(cli): add slash command autocomplete menu
- Add SlashCommandMenu component with keyboard navigation
- Add slash-commands.ts utilities for query extraction and filtering
- Integrate into ChatView with proper state management
- Workflows shown first, then default commands
- Max 5 visible items with arrow key cycling
- Bright blue highlight for selected item
- Footer hidden when menu is shown
2026-01-26 04:41:37 -08:00
Saoud Rizwan 990a00d70f refactor(cli): consolidate tool utilities and reduce code duplication
- Create utils/tools.ts with shared constants and helpers:
  - FILE_EDIT_TOOLS, FILE_SAVE_TOOLS sets
  - isFileEditTool(), isFileSaveTool() helpers
  - normalizeToolName() for consistent tool name handling
  - TOOL_DESCRIPTIONS with normalized keys (no more duplicates)
  - getToolDescription() with automatic normalization
  - parseToolFromMessage() for consistent JSON parsing

- Update components to use shared utilities:
  - ChatMessage.tsx: Remove 60+ line TOOL_DESCRIPTIONS duplicate, use shared
  - ChatView.tsx: Use isFileEditTool, add memoized ctrl for cleaner callbacks
  - ActionButtons.tsx: Use isFileSaveTool and parseToolFromMessage
  - MessageRow.tsx: Use isFileEditTool

- Simplify ChatView.tsx controller pattern:
  - Memoize ctrl = controller || taskController
  - Remove redundant local ctrl definitions in callbacks
  - Cleaner dependency arrays
2026-01-26 04:41:17 -08:00
Saoud Rizwan a125a61cbd feat(cli): add onboarding auth flow with model selection and config import
Auth Flow:
- Auto-redirect first-time users to auth flow when no provider configured
- Support Cline account sign-in with browser-based OAuth
- Support BYO API key configuration for all providers
- Add escape key navigation to go back between steps
- Show provider display names from providers.json (single source of truth)

Model Selection:
- Add ModelPicker component for providers with static model lists
- Add featured models picker for Cline provider (Opus 4.5, GPT 5.2 Codex, Gemini 3 Pro)
- Support OpenRouter model fetching with async loading and caching
- Add scrollable lists with keyboard navigation for long model lists

Config Import:
- Detect and import API keys from Codex CLI (~/.codex/auth.json)
- Detect and import API keys from OpenCode (platform-specific paths)
- Support importing OpenAI, Anthropic, Gemini, Mistral, Groq, DeepSeek, xAI, OpenRouter

Code Quality:
- Extract useScrollableList hook for reusable list windowing
- Move featured models to constants/featured-models.ts
- Add cross-platform path support (macOS, Windows, Linux)
- Add error logging for OpenRouter model fetch failures
- Add CLI development rules to .clinerules/cli.md (blueBright highlight color)
2026-01-26 04:41:17 -08:00
Saoud Rizwan e3e6e1ef77 feat(cli): TUI improvements and new UI components
New Components:
- ActionButtons: Tool approval buttons with mode-based colors (1/2 shortcuts)
- DiffView: Pretty diff view for file edits with +/- highlighting
- TaskView: Alternative verbose task display mode
- MessageList/MessageImage: Supporting components

Chat Improvements:
- Display tool calls in Claude Code style (Cline wants to X / Cline X)
- Mode-based colors (blue for act, yellow for plan)
- Two-column dot prefix layout for messages
- Show command output inline with commands
- Show user feedback messages in chat
- Correct tense for tool messages (wants to vs did)

Bug Fixes:
- Prevent welcome screen flash on task cancel
- Prevent duplicate task completed messages
- Improve followup options handling
- Finalize partial text before native tool calls

Other:
- Add ESC to cancel task (removed ESC-to-exit)
- Use shared formatTimestamp from display utils
- Remove unused files (ImportView, ModelPicker, keychains, etc.)
2026-01-26 04:41:17 -08:00
Saoud Rizwan 09e897c8f2 feat(cli): add onboarding auth flow with model selection and config import
Auth Flow:
- Auto-redirect first-time users to auth flow when no provider configured
- Support Cline account sign-in with browser-based OAuth
- Support BYO API key configuration for all providers
- Add escape key navigation to go back between steps
- Show provider display names from providers.json (single source of truth)

Model Selection:
- Add ModelPicker component for providers with static model lists
- Add featured models picker for Cline provider (Opus 4.5, GPT 5.2 Codex, Gemini 3 Pro)
- Support OpenRouter model fetching with async loading and caching
- Add scrollable lists with keyboard navigation for long model lists

Config Import:
- Detect and import API keys from Codex CLI (~/.codex/auth.json)
- Detect and import API keys from OpenCode (platform-specific paths)
- Support importing OpenAI, Anthropic, Gemini, Mistral, Groq, DeepSeek, xAI, OpenRouter

Code Quality:
- Extract useScrollableList hook for reusable list windowing
- Move featured models to constants/featured-models.ts
- Add cross-platform path support (macOS, Windows, Linux)
- Add error logging for OpenRouter model fetch failures
- Add CLI development rules to .clinerules/cli.md (blueBright highlight color)
2026-01-26 04:41:17 -08:00
abeatrix 6caeeb5da6 Capture Telemetry Events 2026-01-23 23:41:21 -08:00
abeatrix 40550af42c Set up telemetry for CLI 2026-01-23 23:18:58 -08:00
abeatrix e85e4d6f0c Update build step and fix BannerService init 2026-01-23 23:02:47 -08:00
abeatrix d26f76c6e4 remove old task view components 2026-01-23 22:29:30 -08:00
abeatrix 5c6f725079 Replace TaskView with ChatView 2026-01-23 22:10:17 -08:00
abeatrix 4a456e2145 revert to file-base 2026-01-23 21:58:12 -08:00
abeatrix c029e0ffe4 set storage backup 2026-01-23 21:28:09 -08:00
abeatrix cff2217283 check 2026-01-23 21:06:02 -08:00
abeatrix 904617b573 store to system keychain 2026-01-23 21:02:59 -08:00
abeatrix 20fae7e14b update cli host info 2026-01-23 15:20:38 -08:00
Saoud Rizwan 699ed190b7 feat(cli): add CLI-specific system prompt adjustments
- Add isCliEnvironment boolean to SystemPromptContext, computed from
  platform check in task/index.ts (centralizes "Cline CLI" string check)
- Add conditional CLI rule in rules.ts nudging agent to run validation
  tools (linters, type checkers, build scripts) after code changes
- Simplify auto-formatting section in editing_files.ts for CLI mode
  (files saved exactly as written, no auto-formatting expectations)
2026-01-23 15:05:43 -08:00
abeatrix 28ae6d6ca1 Fix error not showing in Chat and use unified chat view 2026-01-23 14:21:01 -08:00
abeatrix beca76fb32 implement logger 2026-01-23 13:27:49 -08:00
abeatrix 8de9935f9a support plain text 2026-01-23 12:11:36 -08:00
abeatrix 6bc700e3db Support Image render 2026-01-23 12:11:23 -08:00
abeatrix 2f17a1a341 Merge branch 'bee/cli' of https://github.com/cline/cline into bee/cli 2026-01-23 10:05:31 -08:00
abeatrix 639121b644 Merge branch 'main' into bee/cli 2026-01-23 09:58:50 -08:00
abeatrix 1187852bed revert non cli-ts changes 2026-01-23 09:40:18 -08:00
abeatrix 7acc036d91 revert non cli-ts changes 2026-01-23 09:36:58 -08:00
abeatrix 1fea5aaa63 json mode support and model ID fix 2026-01-23 04:28:12 -08:00
Saoud Rizwan 8de7294de8 fix(cli): adjust thinking indicator colors and use proper ellipsis
- Update blueBright to light purple-blue (#8CAAFF) to match terminal
- Update yellow to pure bright yellow (#FFFF00)
- Use proper ellipsis character (…) instead of three dots (...)
2026-01-23 03:16:52 -08:00
Saoud Rizwan 2e4ded7f14 feat(cli-ts): enhance thinking indicator with shimmer animation and elapsed time
- Add ThinkingIndicator component with shimmer effect that cycles through text
- Display different colors for act mode (blue) vs plan mode (yellow)
- Show elapsed time after 1 second with 'esc to interrupt' hint
- Extend useSpinnerState hook to return start time alongside active status
- Replace basic LoadingSpinner with enhanced ThinkingIndicator in ChatView and TaskView
- Maintain backward compatibility with useIsSpinnerActive hook
2026-01-23 03:10:45 -08:00
Saoud Rizwan ac94eb4cf4 fix(cli): query cursor position before Ink mounts for accurate robot tracking
Query terminal cursor position before render() to determine where the
robot will be displayed on screen. This fixes the issue where the robot's
gaze tracking threshold was incorrect when CLI was started partway down
the terminal.

- Add cursor-position utility to query position before Ink takes over stdin
- Pass robotTopRow through App -> ChatView -> AsciiMotionCli
- Add StaticRobotFrame component for header after messages start
- Remove cursor position query from inside component (was leaking to input)
2026-01-23 02:55:16 -08:00
Saoud Rizwan 73635cf68f Fix frames 2026-01-23 02:53:56 -08:00
Saoud Rizwan 9eb0da6c2b feat(cli): add animated robot that follows cursor
Replace static Cline logo with animated 3D robot head that tracks
mouse cursor position. Uses 192 pre-rendered frames with smooth
interpolation for real-time cursor following.

- Add AsciiMotionCli component from ink-playground
- Show animated robot in dynamic region during welcome state
- Filter mouse escape sequences from text input
- Robot looks straight ahead when cursor is above, follows when below
2026-01-23 02:53:56 -08:00
Saoud Rizwan 59c10df046 feat(cli): improve UI styling and fix flicker for long outputs
- User messages now have gray background bubble with > prefix
- Completion result text is green
- Progress bar uses gray shades instead of white
- Act mode uses blueBright color for border and text
- Thinking/Planning spinner with mode-aware text
- Skip dynamic rendering for completion_result and plan_mode_respond
  (these tend to be very long and cause flicker when exceeding terminal height)
- Progress bar shows at least 1 bar when any tokens used
- Always show default hint text (@ for files, / for commands)
2026-01-23 02:53:56 -08:00
Saoud Rizwan db4d9a267a fix(cli): use refs instead of state for Static tracking to prevent flicker
When messages transition from streaming (dynamic) to complete (Static),
useState was causing extra render cycles. Using useRef instead updates
synchronously without triggering re-renders.
2026-01-23 02:53:56 -08:00
Saoud Rizwan 734dc6e57f chore(cli): upgrade to @jrichman/ink fork with React 19 2026-01-23 02:53:56 -08:00
Saoud Rizwan 27befb16f5 chore: gitignore tsbuildinfo files 2026-01-23 02:53:56 -08:00
Saoud Rizwan 1c32b08644 fix(cli): improve chat message rendering
- Fix extra spacing between bullet and text by combining Text elements
- Change assistant message color from magenta to default foreground
- Show ⎿ only on first line of tool results, use spaces for alignment
- Add inline markdown rendering for **bold**, *italic*, and `code`

The inline markdown parser handles common formatting in model output
without the complexity of full block-level markdown parsing.
2026-01-23 02:53:56 -08:00
Saoud Rizwan 5d81bf0fe2 chore(cli): upgrade to forked Ink package for better rendering
- Switch ink to @jrichman/ink@6.4.7 (same fork Gemini CLI uses)
- Upgrade React to 19.2.3
- Add aws4fetch dependency
- Add aws4fetch to esbuild external list
2026-01-23 02:53:55 -08:00
Saoud Rizwan f44771acd5 feat(cli): add flicker-free ChatView with Static/Dynamic split
Replace the old welcome view with a unified ChatView component that
uses Ink's Static component to prevent terminal flickering.

The Problem:
When Ink output height >= terminal rows, it switches from efficient
line-erasing to clearTerminal() + full redraw on every re-render,
causing severe flickering.

The Solution:
- Split content into Static region (rendered once) and Dynamic region
- Completed messages move to Static, only current streaming + input
  stays dynamic
- This keeps dynamic height small, avoiding clearTerminal path

Also includes:
- ChatMessage component with Claude Code-style tool call rendering
- StatusBar component for model/tokens/cost display
- Manual centering via centerText() since Box centering breaks in Static
- Console suppression for console.info to prevent Ink corruption
- React.memo on AccountInfoView

We explored alternate screen buffer (like Gemini CLI) but disabled it
because scroll wheel doesn't work without custom mouse event handling.

See ChatView.tsx header comment for full documentation.
2026-01-23 02:53:55 -08:00
abeatrix 786728e0d4 fix removed hooks settings 2026-01-22 15:02:58 -08:00
abeatrix 9714cf8861 Merge branch 'main' into bee/cli-ts-ink-poc 2026-01-22 14:00:59 -08:00
abeatrix c5db44964e Supports Piped 2026-01-22 01:13:37 -08:00
abeatrix ed38739d65 store metadata with task history 2026-01-22 00:49:47 -08:00
abeatrix 50bc90835c log 2026-01-21 18:53:49 -08:00
abeatrix 4d41766b9f fix api key storing process 2026-01-21 17:38:36 -08:00
abeatrix e958e29439 Merge branch 'bee/cli-ts-ink-poc' of https://github.com/cline/cline into bee/cli-ts-ink-poc 2026-01-21 16:33:57 -08:00
abeatrix b337bb706e set up secretStorage 2026-01-21 16:33:51 -08:00
abeatrix 314e06731a fix type 2026-01-21 15:04:08 -08:00
abeatrix 3a6f8b98dd Merge branch 'main' into bee/cli-ts-ink-poc 2026-01-21 14:12:54 -08:00
abeatrix 65f9322bd9 exit askprompt on yolo mode completion 2026-01-21 14:05:37 -08:00
abeatrix 33983e80f8 merge saoudrizwan/cli-ts-ink-poc-with-tui with context 2026-01-21 13:44:50 -08:00
Saoud Rizwan 8648154a74 fix(cli): use index key for static logo array to avoid duplicate key warnings 2026-01-21 11:23:20 -08:00
Saoud Rizwan 5d91ac037e feat(cli): redesign welcome view with ASCII logo and mode toggle
- Add centered ASCII Cline logo
- Add "What can I do for you?" prompt
- Add bordered input field (blue for Act mode, yellow for Plan mode)
- Display model ID and Plan/Act toggle below input
- Tab to switch between Plan and Act modes
- Two-step Esc to exit (first press highlights, second exits)
- Full-width responsive layout
- Properly exit process when user cancels
2026-01-21 10:00:20 -08:00
Saoud Rizwan 6179dfe0a4 fix(cli): disable browser tool in CLI mode 2026-01-21 09:50:32 -08:00
Saoud Rizwan cd9e28e6d0 feat(cli): add graceful Ctrl+C shutdown handling
- Handle SIGINT/SIGTERM signals to cleanly exit
- Abort running task, persist state, dispose controller before exit
- Force exit on second signal if already shutting down
2026-01-21 09:50:32 -08:00
Saoud Rizwan 8d56a697eb feat(cli): add CLI-specific system prompt adjustments
- Add isCliEnvironment boolean to SystemPromptContext, computed from
  platform check in task/index.ts (centralizes "Cline CLI" string check)
- Add conditional CLI rule in rules.ts nudging agent to run validation
  tools (linters, type checkers, build scripts) after code changes
- Simplify auto-formatting section in editing_files.ts for CLI mode
  (files saved exactly as written, no auto-formatting expectations)
2026-01-21 09:50:32 -08:00
Saoud Rizwan fd506b14b5 fix(cli): disable checkpoints in CLI mode
The shadow git checkpoint system is problematic for CLI usage because it
creates shadow gits for every directory you run cline in, leading to storage
bloat and potentially tracking files in directories you don't want tracked.

Disables checkpoints by setting enableCheckpointsSetting to false during
CLI initialization.
2026-01-21 09:50:32 -08:00
abeatrix 55d268773d on exit 2026-01-20 23:32:20 -08:00
abeatrix bf978c2c42 Merge branch 'bee/cli-ts-ink-poc' of https://github.com/cline/cline into bee/cli-ts-ink-poc 2026-01-20 23:01:07 -08:00
abeatrix 7e06dbbd33 Fix viewpoints 2026-01-20 23:01:06 -08:00
abeatrix 8d4822d781 add version command 2026-01-20 18:07:30 -08:00
abeatrix dbcaae65d1 refactor(auth): extract provider mapping and add welcome navigation
- Extract provider-to-API-key field mapping into shared utility (ProviderToApiKeyMap)
- Add navigation support from AuthView to WelcomeView via onNavigateToWelcome callback
- Implement internal welcome submission handler with image processing support
- Improve code organization by centralizing provider configuration mapping

This refactoring enables better navigation flow between auth and welcome screens
and makes the provider mapping reusable across components.
2026-01-20 18:07:02 -08:00
abeatrix 66db36ff5b Includes Input field with options fields 2026-01-20 18:05:14 -08:00
abeatrix a53d17a857 update script 2026-01-20 16:01:58 -08:00
abeatrix de56305315 file structure 2026-01-20 15:24:56 -08:00
abeatrix 5cd22e3f86 Clean up and add config view for rules 2026-01-20 14:27:02 -08:00
abeatrix bd9150837d Update DiffView background 2026-01-20 13:55:29 -08:00
abeatrix fbb494b88a Add DiffView & History UX 2026-01-20 13:43:11 -08:00
abeatrix 16b4c48457 Fix Box Order 2026-01-20 13:40:14 -08:00
abeatrix 058c86872b checkpoint restore 2026-01-20 13:02:01 -08:00
abeatrix d3e8f562bc At Mention Files 2026-01-20 12:51:18 -08:00
abeatrix 1594d44190 Show Costs 2026-01-20 12:24:51 -08:00
abeatrix c8419ffa99 Add Unit Tests 2026-01-20 12:14:02 -08:00
abeatrix cf6c80504f Fix spacing 2026-01-20 11:48:19 -08:00
abeatrix 230f615927 fix thinking 2026-01-20 11:37:32 -08:00
abeatrix 08e00dc3bf hide config options 2026-01-20 11:22:36 -08:00
abeatrix c01bf73d3f Interactive ConfigView 2026-01-20 11:17:48 -08:00
abeatrix df28359114 add Focus Chain 2026-01-20 11:17:33 -08:00
abeatrix d3631f0485 add account info 2026-01-20 09:27:18 -08:00
abeatrix eb18c556f4 add image support 2026-01-20 09:27:03 -08:00
abeatrix b18284ea78 clean up 2026-01-20 00:09:22 -08:00
abeatrix e4e99ec711 clean up shim 2026-01-20 00:00:24 -08:00
abeatrix b7b4d0bfe8 add welcome view 2026-01-19 21:53:40 -08:00
abeatrix c12b00e7a9 add thinking options to task command & set model ID 2026-01-19 21:40:52 -08:00
abeatrix 73bb8c2f64 Fix task display in HistoryView 2026-01-19 21:03:39 -08:00
abeatrix 0e1d513462 update prompt handling for plan mode and completion
Add new prompt types "plan_mode_text" and "completion" to improve user experience when interacting with the assistant. Separate handling for plan_mode_respond from followup to enable quick mode switching via empty Enter press. Add toggleToActMode callback to allow seamless transition from Plan to Act mode without text input.

Changes:
- Split plan_mode_respond and followup logic for distinct UX flows
- Add plan_mode_text prompt type with toggle-to-Act-mode on empty Enter
- Add completion prompt type for follow-up questions or exit on Enter
- Implement toggleToActMode callback for mode switching
- Update keyboard handlers and UI rendering for new prompt types
- Add helpful hints for users (e.g., "just Enter to switch to Act mode")
2026-01-19 21:01:36 -08:00
abeatrix d6a1b338bc clean up 2026-01-19 19:18:13 -08:00
abeatrix 0091fb6ffb clean up messages 2026-01-19 16:38:47 -08:00
abeatrix de3e3fe34a clean up logs 2026-01-19 16:25:43 -08:00
abeatrix e136d75621 refactor: switch CLI to ES modules with Ink UI
Convert CLI build output from CommonJS (cli.cjs) to ES modules (cli.mjs) and refactor the UI layer to use React with Ink framework instead of the previous subscription-based state update pattern.

Changes include:
- Updated esbuild configuration to target ESM format
- Added stubOptionalModulesPlugin to handle react-devtools-core
- Updated shebang and module compatibility helpers for ES modules
- Replaced state subscriber pattern with React component-based UI
- Added react and related type dependencies
- Updated external module list to include UI dependencies (ink, ink-spinner, react)
- Enabled top-level-await support for ES modules
2026-01-19 16:18:24 -08:00
abeatrix 91127ffdb9 Update logger 2026-01-16 22:34:20 -08:00
abeatrix 11f76f0218 add cline sign in 2026-01-16 22:08:03 -08:00
abeatrix 74f1b9c75d **feat(cli): allow switching mode and specifying model for tasks**
Added `--switch` (`-s`) and `--model` (`-m`) options to the CLI.
Updated `runTask` signature and logic to set global state for mode (plan/act) and the corresponding API model ID.
This enables users to run tasks in different modes or with a specific model directly from the command line.
2026-01-16 18:46:55 -08:00
abeatrix 942c78119e working prototype
npm install:all
cd cli-ts
npm run link
clinedev auth
2026-01-16 18:36:47 -08:00
abeatrix 3130c3c96e replace console.log with Logger 2026-01-14 13:08:50 -08:00
abeatrix f0225bc20d fileeditprovider 2026-01-14 13:08:28 -08:00
abeatrix 1bf158245c prototype: Cline CLI with Typescript
TODO:
- remove usage of console.log across codebase and replace them with Logger
2026-01-13 16:55:36 -08:00
3962 changed files with 519923 additions and 521645 deletions
-208
View File
@@ -1,208 +0,0 @@
---
name: cline-sdk
description: Comprehensive Cline SDK skill for building AI agents. Covers the Agent runtime, ClineCore sessions, custom tools, plugins, events, LLM providers, scheduling, multi-agent teams, and production deployment. Use for any task involving @cline/sdk or its sub-packages.
metadata:
references: agent, clinecore
---
# Cline SDK Skill
Consolidated skill for building AI agents with the Cline SDK. Use the decision trees below to find the right entry point and API surface, then load detailed references.
## Critical Rules
Follow these rules in all Cline SDK code:
1. Install with `npm install @cline/sdk`. The `@cline/sdk` package re-exports everything from `@cline/core`, `@cline/agents`, `@cline/llms`, and `@cline/shared`.
2. Requires Node.js 22 or later.
3. Use `createTool()` from `@cline/sdk` (or `@cline/shared`) to define tools. Tool names must be `snake_case`.
4. Return errors as structured data from tool `execute` functions. Throwing counts as a "mistake" against the agent's mistake limit.
5. Use `lifecycle: { completesRun: true }` on tools that should end the agent loop (e.g. a "submit answer" tool).
6. When using `ClineCore`, always call `dispose()` when done to clean up resources.
7. The standalone `Agent` and `ClineCore` have different event systems. For `Agent`: use `agent.subscribe()` to get `AgentRuntimeEvent` types (text streaming is `"assistant-text-delta"`, result text is `result.outputText`). For `ClineCore`: use `cline.subscribe()` to get `CoreSessionEvent` types (text streaming is `"chunk"` with `payload.type === "text"`, result text is `result.text`). There is no top-level `onEvent` field on `AgentRuntimeConfig` -- use `agent.subscribe()` or `hooks.onEvent` instead. Do not use event types like `"content_update"` or `"content_start"` with `agent.subscribe()` -- those are internal legacy types from the ClineCore adapter layer.
## How to Use This Skill
### Reference File Structure
The two main API surfaces (`Agent` and `ClineCore`) follow a 4-file pattern. Cross-cutting concepts are single-file guides.
Each main API surface in `./references/<api>/` contains:
| File | Purpose | When to Read |
|------|---------|--------------|
| `REFERENCE.md` | Overview, when to use, quick start | Always read first |
| `api.md` | Full API: classes, methods, config, types | Writing code |
| `patterns.md` | Common patterns, best practices | Implementation guidance |
| `gotchas.md` | Pitfalls, limitations, debugging | Troubleshooting |
Cross-cutting concepts in `./references/<concept>/` have `REFERENCE.md` as the entry point.
### Reading Order
1. Start with `REFERENCE.md` for your chosen API surface
2. Then read additional files relevant to your task:
- Writing agent code -> `api.md`
- Common patterns -> `patterns.md`
- Creating tools -> `tools/REFERENCE.md`
- Adding plugins/hooks -> `plugins/REFERENCE.md`
- Configuring LLM providers -> `providers/REFERENCE.md`
- Streaming events -> `events/REFERENCE.md`
- Deploying to production -> `production/REFERENCE.md`
- Scheduling agents -> `scheduling/REFERENCE.md`
- Multi-agent orchestration -> `multi-agent/REFERENCE.md`
- Debugging -> `gotchas.md`
### Example Paths
```
./references/agent/REFERENCE.md # Start here for lightweight agents
./references/clinecore/REFERENCE.md # Start here for full runtime
./references/agent/api.md # Agent class, config, methods
./references/tools/REFERENCE.md # Creating and using tools
./references/plugins/REFERENCE.md # Plugin system
./references/providers/REFERENCE.md # LLM provider configuration
```
## Quick Decision Trees
### "Which API surface should I use?"
```
Which API?
+-- I want a simple, stateless agent with custom tools
| +-- agent/ (Agent class from @cline/agents)
+-- I need session persistence, built-in tools, config discovery
| +-- clinecore/ (ClineCore from @cline/core)
+-- I want built-in file/shell/search/web tools
| +-- clinecore/ (has built-in tools; Agent does not)
+-- I want scheduled or recurring agents
| +-- clinecore/ (automation API)
+-- I need multi-process or multi-client session sharing
| +-- clinecore/ (hub-backed runtime)
+-- I'm building a browser-compatible agent
| +-- agent/ (no Node.js dependencies)
```
### "I need to create tools"
```
Tools?
+-- Define a custom tool with schema -> tools/REFERENCE.md
+-- Use built-in tools (bash, editor, read_files) -> tools/REFERENCE.md (built-in section)
+-- Control tool approval/policies -> tools/REFERENCE.md (policies section)
+-- Tool that ends the agent loop -> tools/REFERENCE.md (completion tools)
+-- Package tools as a reusable plugin -> plugins/REFERENCE.md
```
### "I need to handle events"
```
Events?
+-- Stream text/reasoning in real time -> events/REFERENCE.md
+-- Track token usage and costs -> events/REFERENCE.md
+-- Watch tool calls -> events/REFERENCE.md
+-- Detect completion/errors -> events/REFERENCE.md
+-- Hook into lifecycle stages -> plugins/REFERENCE.md
```
### "I need to configure a model provider"
```
Providers?
+-- Anthropic (Claude) -> providers/REFERENCE.md
+-- OpenAI (GPT) -> providers/REFERENCE.md
+-- Google (Gemini/Vertex) -> providers/REFERENCE.md
+-- AWS Bedrock -> providers/REFERENCE.md
+-- Mistral -> providers/REFERENCE.md
+-- OpenAI-compatible (vLLM, Together, etc.) -> providers/REFERENCE.md
+-- Custom/self-hosted provider -> providers/REFERENCE.md
```
### "I need plugins or hooks"
```
Plugins?
+-- Package tools + hooks together -> plugins/REFERENCE.md
+-- Observe tool calls (logging, metrics) -> plugins/REFERENCE.md
+-- Intercept lifecycle events -> plugins/REFERENCE.md
+-- Add system prompt rules -> plugins/REFERENCE.md
+-- Distribute via npm/git -> plugins/REFERENCE.md
```
### "I need multi-agent coordination"
```
Multi-agent?
+-- Spawn one-off background agents -> multi-agent/REFERENCE.md (sub-agents)
+-- Persistent cross-session teams -> multi-agent/REFERENCE.md (teams)
+-- Parent-child delegation -> multi-agent/REFERENCE.md (sub-agents)
+-- Peer-to-peer task board -> multi-agent/REFERENCE.md (teams)
```
### "I need scheduling or automation"
```
Scheduling?
+-- Recurring cron jobs -> scheduling/REFERENCE.md
+-- One-off scheduled tasks -> scheduling/REFERENCE.md
+-- Event-driven triggers -> scheduling/REFERENCE.md
+-- CLI schedule management -> scheduling/REFERENCE.md
```
### "I need to go to production"
```
Production?
+-- Error handling and status checks -> production/REFERENCE.md
+-- Cost control and token limits -> production/REFERENCE.md
+-- Observability (OpenTelemetry) -> production/REFERENCE.md
+-- Security and sandboxing -> production/REFERENCE.md
+-- Deployment patterns -> production/REFERENCE.md
```
### Troubleshooting Index
- Agent loop not stopping -> `tools/REFERENCE.md` (completion tools)
- Tool errors crashing the agent -> `agent/gotchas.md` or `clinecore/gotchas.md`
- Provider auth failures -> `providers/REFERENCE.md`
- Session not persisting -> `clinecore/gotchas.md`
- Token usage too high -> `production/REFERENCE.md` (cost control)
- Hub connection issues -> `clinecore/gotchas.md`
- Plugin not loading -> `plugins/REFERENCE.md`
- Events not firing -> `events/REFERENCE.md`
## Product Index
### API Surfaces
| API | Entry File | Description |
|-----|------------|-------------|
| Agent | `./references/agent/REFERENCE.md` | Lightweight stateless agent loop |
| ClineCore | `./references/clinecore/REFERENCE.md` | Full runtime with sessions, persistence, built-in tools |
### Cross-Cutting Concepts
| Concept | Entry File | Description |
|---------|------------|-------------|
| Tools | `./references/tools/REFERENCE.md` | Built-in and custom tool creation |
| Plugins | `./references/plugins/REFERENCE.md` | Extension system with hooks |
| Events | `./references/events/REFERENCE.md` | Real-time streaming events |
| Providers | `./references/providers/REFERENCE.md` | LLM provider configuration |
| Production | `./references/production/REFERENCE.md` | Deployment, security, observability |
| Scheduling | `./references/scheduling/REFERENCE.md` | Cron jobs and automation |
| Multi-Agent | `./references/multi-agent/REFERENCE.md` | Teams and sub-agents |
### Package Map
| Package | Purpose |
|---------|---------|
| `@cline/sdk` | Everything you need, install this one |
| `@cline/core` | Sessions, persistence, built-in tools, config, hub |
| `@cline/agents` | Stateless agent loop, tool orchestration, streaming |
| `@cline/llms` | LLM provider gateway |
| `@cline/shared` | Types, tool helpers, hook engine |
## Resources
Repository: https://github.com/cline/cline
SDK Source: https://github.com/cline/cline/tree/main/sdk
Documentation: https://docs.cline.bot/sdk/overview
Discord: https://discord.gg/cline
@@ -1,107 +0,0 @@
# Agent Runtime
The `Agent` class (also exported as `AgentRuntime`) is the lightweight, stateless agent loop from `@cline/agents`. It handles the core iteration cycle: send messages to an LLM, execute tool calls, collect results, and repeat until the task is done.
## When to Use Agent
| Use Agent when... | Use ClineCore instead when... |
|---|---|
| You want a simple agent with custom tools | You need built-in tools (bash, editor, etc.) |
| You want minimal dependencies | You need session persistence |
| You need browser compatibility | You need config discovery from `.cline/` |
| You're building a stateless worker | You need multi-process session sharing |
| You want full control over the runtime | You want batteries-included setup |
## Quick Start
```typescript
import { Agent } from "@cline/sdk"
const agent = new Agent({
providerId: "anthropic",
modelId: "claude-sonnet-4-6",
apiKey: process.env.ANTHROPIC_API_KEY,
systemPrompt: "You are a helpful assistant.",
tools: [],
})
const result = await agent.run("What is the capital of France?")
console.log(result.outputText)
```
## Core Concepts
The Agent operates in a loop:
1. Accept user input (string, message, or array of messages)
2. Build turn context (system prompt, messages, tools)
3. Call the LLM provider
4. If the model returns tool calls, execute them and loop back to step 3
5. If the model returns text without tool calls, the run completes
6. Emit events throughout for streaming
The agent is stateless in the sense that it does not persist anything to disk. Conversation history is held in memory and can be accessed via `snapshot()`.
## Key APIs
- `new Agent(config)` or `createAgent(config)` - Create an agent
- `agent.run(input)` - Start a run with user input
- `agent.continue(input?)` - Continue an existing conversation
- `agent.abort(reason?)` - Cancel an active run
- `agent.subscribe(listener)` - Listen to streaming events
- `agent.snapshot()` - Get current runtime state
- `agent.restore(messages)` - Replace message history
See `api.md` for full API details.
## Multi-Turn Conversations
```typescript
const agent = new Agent({
providerId: "anthropic",
modelId: "claude-sonnet-4-6",
systemPrompt: "You are a helpful assistant.",
tools: [],
})
const first = await agent.run("What is 2 + 2?")
console.log(first.outputText)
const second = await agent.continue("Now multiply that by 3")
console.log(second.outputText)
```
Use `agent.hasRun` to check if a run has already been executed, which determines whether to call `run()` or `continue()`.
## Event Streaming
Use `agent.subscribe()` to stream events in real time. Register the listener before calling `run()` to avoid missing early events.
There is no top-level `onEvent` field on the Agent config. For an async alternative, use `hooks.onEvent` (see `api.md` and `gotchas.md`).
```typescript
const agent = new Agent({
providerId: "anthropic",
modelId: "claude-sonnet-4-6",
systemPrompt: "You are a helpful assistant.",
tools: [],
})
agent.subscribe((event) => {
if (event.type === "assistant-text-delta") {
process.stdout.write(event.text)
}
})
const result = await agent.run("What is the capital of France?")
```
See `events/REFERENCE.md` for the full event type catalog.
## Next Steps
- `api.md` - Full Agent API reference
- `patterns.md` - Common patterns and best practices
- `gotchas.md` - Pitfalls and debugging
- `../tools/REFERENCE.md` - Creating custom tools
- `../events/REFERENCE.md` - Event system details
- `../providers/REFERENCE.md` - Provider configuration
@@ -1,231 +0,0 @@
# Agent API Reference
## Constructor
```typescript
import { Agent } from "@cline/sdk"
const agent = new Agent(config: AgentRuntimeConfig)
```
Also available via factory function:
```typescript
import { createAgent } from "@cline/sdk"
const agent = createAgent(config)
```
## AgentRuntimeConfig
Two config forms exist as a discriminated union:
### With Provider ID (recommended)
```typescript
interface AgentRuntimeConfigWithProvider {
providerId: string // e.g. "anthropic", "openai", "gemini"
modelId: string // e.g. "claude-sonnet-4-6", "gpt-5.5"
apiKey?: string // provider API key
baseUrl?: string // custom endpoint
headers?: Record<string, string>
systemPrompt?: string
tools?: AgentTool[]
initialMessages?: AgentMessage[]
toolPolicies?: Record<string, ToolPolicy>
hooks?: Partial<AgentRuntimeHooks>
plugins?: AgentPlugin[]
}
```
### With Pre-built Model
```typescript
interface AgentRuntimeConfigWithModel {
model: AgentModel // pre-built model from gateway
systemPrompt?: string
tools?: AgentTool[]
initialMessages?: AgentMessage[]
toolPolicies?: Record<string, ToolPolicy>
hooks?: Partial<AgentRuntimeHooks>
plugins?: AgentPlugin[]
}
```
Note: there is no top-level `onEvent` field on `AgentRuntimeConfig`. For event streaming, use `agent.subscribe()` or `hooks.onEvent` (see AgentRuntimeHooks below).
## Methods
### run(input)
Start the agent with user input. Returns when the agent loop completes.
```typescript
const result: AgentRunResult = await agent.run("Build a REST API")
```
Input can be a string, an `AgentMessage`, or an array of `AgentMessage[]`.
### continue(input?)
Continue an existing conversation with optional new input.
```typescript
const result = await agent.continue("Now add authentication")
```
### abort(reason?)
Cancel the currently active run.
```typescript
agent.abort("User cancelled")
```
### subscribe(listener)
Register a listener for streaming events.
```typescript
const unsubscribe = agent.subscribe((event: AgentRuntimeEvent) => {
// handle event
})
// Later: stop listening
unsubscribe()
```
### snapshot()
Get the current runtime state including message history.
```typescript
const state: AgentRuntimeStateSnapshot = agent.snapshot()
```
### restore(messages)
Replace the agent's message history.
```typescript
agent.restore(previousMessages)
```
### hasRun
Boolean property indicating whether `run()` has been called at least once.
```typescript
if (agent.hasRun) {
await agent.continue(input)
} else {
await agent.run(input)
}
```
## AgentRunResult
Returned by `run()` and `continue()`.
```typescript
interface AgentRunResult {
agentId: string
agentRole?: string
runId: string
status: "completed" | "aborted" | "failed"
iterations: number
outputText: string
messages: readonly AgentMessage[]
usage: AgentUsage
error?: Error
}
```
### Status Values
- `"completed"` - Agent finished normally
- `"aborted"` - Cancelled via `abort()`
- `"failed"` - Unrecoverable error
## AgentMessage
```typescript
interface AgentMessage {
id: string
role: "user" | "assistant" | "tool"
content: AgentMessagePart[]
createdAt: number
metadata?: Record<string, unknown>
modelInfo?: { id: string; provider: string; family?: string }
metrics?: {
inputTokens: number
outputTokens: number
cacheReadTokens?: number
cacheWriteTokens?: number
cost?: number
}
}
```
## AgentUsage
```typescript
interface AgentUsage {
inputTokens: number
outputTokens: number
cacheReadTokens: number
cacheWriteTokens: number
totalInputTokens: number
totalOutputTokens: number
totalCost?: number
}
```
## AgentRuntimeHooks
```typescript
interface AgentRuntimeHooks {
beforeRun?(context): AgentStopControl | undefined
afterRun?(context): void
beforeModel?(context): AgentBeforeModelResult | undefined
afterModel?(context): AgentStopControl | undefined
beforeTool?(context): AgentBeforeToolResult | undefined
afterTool?(context): AgentAfterToolResult | undefined
onEvent?(event: AgentRuntimeEvent): void | Promise<void>
}
```
Hooks can intercept and modify behavior at each stage. Return a stop control from `beforeRun`, `afterModel`, or `beforeTool` to halt the agent loop.
`hooks.onEvent` receives the same `AgentRuntimeEvent` types as `agent.subscribe()`, but hook callbacks are awaited (can be async), while `subscribe()` listeners are called synchronously. Use `subscribe()` for UI streaming and `hooks.onEvent` for async side effects like logging to an external service.
## AgentRuntimeStateSnapshot
```typescript
interface AgentRuntimeStateSnapshot {
messages: readonly AgentMessage[]
usage: AgentUsage
iterations: number
status: string
}
```
## Factory: createAgentRuntime
Lower-level factory that returns the same `Agent` class:
```typescript
import { createAgentRuntime } from "@cline/sdk"
const runtime = createAgentRuntime(config)
```
## See Also
- `REFERENCE.md` - Overview and quick start
- `patterns.md` - Common patterns
- `../tools/REFERENCE.md` - Tool creation
- `../events/REFERENCE.md` - Event types
- `../providers/REFERENCE.md` - Provider setup
@@ -1,134 +0,0 @@
# Agent Gotchas
## Agent Loop Never Stops
If the agent keeps iterating without completing:
- Make sure at least one tool has `lifecycle: { completesRun: true }` if you want the agent to explicitly finish.
- Without any tools, the agent will complete after the model returns text without tool calls.
- If using tools, ensure the system prompt guides the model toward calling the completion tool when done.
- Check that `completesRun` tools return successfully (not throwing errors).
## Tool Errors Count as Mistakes
When a tool's `execute` function throws an exception, the SDK counts it as a "mistake." After too many mistakes, the agent stops with a `mistake_limit` finish reason.
Instead, return errors as structured data:
```typescript
// Bad: throwing
execute: async (input) => {
throw new Error("File not found")
}
// Good: returning error data
execute: async (input) => {
return { error: "File not found", path: input.path }
}
```
## run() vs continue()
- Call `run()` for the first interaction. It sets up the conversation.
- Call `continue()` for subsequent messages. It appends to the existing conversation.
- Calling `run()` a second time resets the conversation history.
- Use `agent.hasRun` to check which method to call.
## Browser Compatibility
`@cline/agents` (and by extension, the `Agent` class) is browser-safe with no Node.js dependencies. However, `@cline/core` and `ClineCore` require Node.js 22+. If you import from `@cline/sdk`, you get everything including the Node-only code. For browser usage, import directly from `@cline/agents`:
```typescript
import { Agent } from "@cline/agents"
```
## No Top-Level onEvent on Agent Config
`AgentRuntimeConfig` does not have a top-level `onEvent` field. Passing `onEvent` to `new Agent({ onEvent: ... })` has no effect. There are two ways to receive events:
```typescript
// Option 1: subscribe() - synchronous, best for UI streaming
const agent = new Agent({ ...config })
agent.subscribe((event) => {
if (event.type === "assistant-text-delta") {
process.stdout.write(event.text)
}
})
// Option 2: hooks.onEvent - awaited, best for async side effects
const agent = new Agent({
...config,
hooks: {
onEvent: async (event) => {
if (event.type === "assistant-text-delta") {
await logToService(event.text)
}
},
},
})
```
Both receive the same `AgentRuntimeEvent` types. Prefer `subscribe()` for streaming UI.
## Event Listener Timing
Register event listeners via `subscribe()` before calling `run()`:
```typescript
// Good: subscribe before run
agent.subscribe(handler)
const result = await agent.run(input)
// Bad: subscribing after run starts loses early events
const promise = agent.run(input)
agent.subscribe(handler) // may miss events
```
## Tool Input Schema Matters
The model uses the tool's `inputSchema` to decide what arguments to pass. A vague or missing schema leads to incorrect tool calls.
- Use `z.enum()` for fixed value sets, not free-form strings
- Describe every property with `.describe()` in Zod or `description` in JSON Schema
- Include constraints (rate limits, max values) in the tool description
## Memory and Long Conversations
The Agent holds all messages in memory. For long-running conversations, memory usage grows with each turn. Consider:
- Using `ClineCore` with compaction for long sessions
- Periodically creating a new agent with a summary of the conversation
- Monitoring `result.usage.totalInputTokens` to track context growth
## Abort Signal Handling in Tools
Long-running tools should respect the abort signal:
```typescript
execute: async (input, context) => {
for (const item of items) {
if (context.abortSignal?.aborted) {
return { partial: results, aborted: true }
}
results.push(await process(item))
}
return { results }
}
```
## Provider API Key
If you get authentication errors, check:
- `apiKey` is set in the config or via environment variables
- The key matches the `providerId` (e.g., Anthropic key for `providerId: "anthropic"`)
- For OpenAI-compatible providers, both `apiKey` and `baseUrl` are set
See `../providers/REFERENCE.md` for provider-specific setup.
## See Also
- `api.md` - Full API reference
- `patterns.md` - Common patterns
- `../tools/REFERENCE.md` - Tool creation
- `../clinecore/REFERENCE.md` - Use ClineCore for persistence
@@ -1,258 +0,0 @@
# Agent Patterns
## Interactive CLI Agent
A multi-turn conversational agent in the terminal with streaming output:
```typescript
import { Agent } from "@cline/sdk"
import * as readline from "node:readline"
const agent = new Agent({
providerId: "anthropic",
modelId: "claude-sonnet-4-6",
apiKey: process.env.ANTHROPIC_API_KEY,
systemPrompt: "You are a helpful assistant. Keep responses concise.",
tools: [],
})
agent.subscribe((event) => {
if (event.type === "assistant-text-delta") {
process.stdout.write(event.text)
}
})
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
})
function prompt(): void {
rl.question("\nYou: ", async (input) => {
const trimmed = input.trim()
if (!trimmed || trimmed === "exit") {
rl.close()
return
}
process.stdout.write("\nAssistant: ")
if (agent.hasRun) {
await agent.continue(trimmed)
} else {
await agent.run(trimmed)
}
process.stdout.write("\n")
prompt()
})
}
prompt()
```
## Conversational Agent (Slack Bot, Chat App)
Maintain per-thread agents with conversation memory:
```typescript
import { Agent } from "@cline/sdk"
const agents = new Map<string, Agent>()
async function handleMessage(threadId: string, message: string) {
let agent = agents.get(threadId)
if (!agent) {
agent = new Agent({
providerId: "anthropic",
modelId: "claude-sonnet-4-6",
systemPrompt: "You are a concise assistant.",
tools: [],
})
agents.set(threadId, agent)
}
const result = agent.hasRun
? await agent.continue(message)
: await agent.run(message)
return result.outputText
}
```
## Streaming UI
Build a real-time UI by handling events via `subscribe()`:
```typescript
const agent = new Agent({
providerId: "anthropic",
modelId: "claude-sonnet-4-6",
systemPrompt: "You are a helpful assistant.",
tools: [myTool],
})
agent.subscribe((event) => {
switch (event.type) {
case "assistant-text-delta":
ui.appendText(event.text)
break
case "assistant-message":
ui.endText()
break
case "turn-started":
ui.startTurn(event.iteration)
break
case "turn-finished":
if (event.toolCallCount > 0) ui.showToolCount(event.toolCallCount)
break
case "usage-updated":
ui.updateUsage(event.usage.inputTokens, event.usage.outputTokens)
break
}
})
const result = await agent.run("Hello!")
```
## Structured Output via Completion Tool
Use a tool with `completesRun: true` to extract structured data:
```typescript
import { Agent, createTool } from "@cline/sdk"
import { z } from "zod"
const submitReview = createTool({
name: "submit_review",
description: "Submit the final code review with structured feedback.",
inputSchema: z.object({
summary: z.string(),
issues: z.array(z.object({
file: z.string(),
line: z.number(),
severity: z.enum(["error", "warning", "info"]),
message: z.string(),
})),
approved: z.boolean(),
}),
lifecycle: { completesRun: true },
execute: async (input) => input,
})
const agent = new Agent({
providerId: "anthropic",
modelId: "claude-sonnet-4-6",
systemPrompt: "Review the code diff and submit structured feedback.",
tools: [submitReview],
})
const result = await agent.run(diffContent)
const review = result.toolCalls.find(tc => tc.name === "submit_review")
console.log(review?.output)
```
## Agent with Abort/Timeout
```typescript
const agent = new Agent({
providerId: "anthropic",
modelId: "claude-sonnet-4-6",
systemPrompt: "Analyze this data.",
tools: [],
})
const timeout = setTimeout(() => agent.abort("Timeout"), 30_000)
try {
const result = await agent.run(data)
if (result.status === "aborted") {
console.log("Agent was aborted")
} else {
console.log(result.outputText)
}
} finally {
clearTimeout(timeout)
}
```
## Agent with Plugins
```typescript
import { Agent } from "@cline/sdk"
import type { AgentPlugin } from "@cline/sdk"
const loggingPlugin: AgentPlugin = {
name: "logging",
manifest: { capabilities: ["hooks"] },
setup() {},
hooks: {
beforeTool({ toolCall }) {
console.log(`Calling tool: ${toolCall.toolName}`)
},
afterRun({ result }) {
console.log(`Completed in ${result.iterations} iterations`)
},
},
}
const agent = new Agent({
providerId: "anthropic",
modelId: "claude-sonnet-4-6",
systemPrompt: "You are a helpful assistant.",
tools: [myTool],
plugins: [loggingPlugin],
})
```
## Restoring State Across Sessions
Save and restore agent state manually:
```typescript
// Save state
const snapshot = agent.snapshot()
const serialized = JSON.stringify(snapshot.messages)
// Later: restore
const agent2 = new Agent({ ...config })
const messages = JSON.parse(serialized)
agent2.restore(messages)
const result = await agent2.continue("Continue where we left off")
```
For automatic persistence, use `ClineCore` instead.
## Pre-Built Model via Gateway
For advanced provider configuration:
```typescript
import { Agent } from "@cline/sdk"
import { createGateway } from "@cline/llms"
const gateway = createGateway({
providerConfigs: [
{ providerId: "anthropic", apiKey: process.env.ANTHROPIC_API_KEY },
{ providerId: "openai", apiKey: process.env.OPENAI_API_KEY },
],
})
const model = gateway.createAgentModel({
providerId: "anthropic",
modelId: "claude-opus-4-7",
})
const agent = new Agent({
model,
systemPrompt: "You are a helpful assistant.",
tools: [],
})
```
## See Also
- `api.md` - Full API reference
- `gotchas.md` - Common pitfalls
- `../tools/REFERENCE.md` - Creating tools
- `../plugins/REFERENCE.md` - Plugin system
@@ -1,131 +0,0 @@
# ClineCore Runtime
`ClineCore` is the full-featured runtime from `@cline/core`. It wraps the `Agent` loop with session persistence, built-in tools (bash, editor, file reading, search, web fetch), config discovery, plugin loading, and optional hub-backed multi-process support.
## When to Use ClineCore
| Use ClineCore when... | Use Agent instead when... |
|---|---|
| You need built-in tools (bash, editor, etc.) | You only need custom tools |
| You want session persistence to disk | Stateless is fine |
| You need config discovery from `.cline/` dirs | You handle config yourself |
| You want scheduled/automated agents | You don't need scheduling |
| You need multi-client session sharing | Single-process is fine |
| You're building a full application | You want minimal dependencies |
## Quick Start
```typescript
import { ClineCore } from "@cline/sdk"
const cline = await ClineCore.create({ clientName: "my-app" })
const session = await cline.start({
prompt: "Set up CI with GitHub Actions",
config: {
providerId: "anthropic",
modelId: "claude-sonnet-4-6",
apiKey: process.env.ANTHROPIC_API_KEY,
cwd: "/path/to/project",
enableTools: true,
},
})
console.log(session.result?.text)
await cline.dispose()
```
## Core Concepts
### Sessions
Every `cline.start()` call creates a session with a unique ID. Sessions persist their messages and metadata to SQLite. You can list, read, resume, and delete sessions.
### Built-in Tools
ClineCore provides these tools automatically when `enableTools: true`:
| Tool | Description |
|------|-------------|
| `bash` | Execute shell commands |
| `editor` | Edit files |
| `read_files` | Read file contents |
| `apply_patch` | Apply unified diffs |
| `search` | Search file contents and structure |
| `fetch_web` | HTTP requests and web content |
### Config Discovery
ClineCore watches `.cline/` directories for:
- Rules (system prompt additions)
- Skills (domain knowledge)
- Workflows (multi-step procedures)
- Hooks (lifecycle logic)
- Plugins (tool + hook bundles)
- MCP servers (external tool providers)
### Backend Modes
| Mode | Description |
|------|-------------|
| `"auto"` (default) | Tries to connect to a local hub; falls back to in-process if unavailable |
| `"local"` | In-process execution, local SQLite storage, no hub |
| `"hub"` | Requires a compatible local WebSocket hub; fails if unavailable |
| `"remote"` | Connects to an explicit remote hub endpoint |
The default mode is `"auto"`. For simple scripts and CLI tools, `"local"` avoids hub discovery overhead. Hub mode enables multi-client session sharing (e.g., a dashboard watching a running session from another process).
## Key APIs
- `ClineCore.create(options)` - Create and initialize
- `cline.start(input)` - Start a new session
- `cline.send({ sessionId, prompt })` - Send follow-up message
- `cline.subscribe(listener)` - Listen to session events
- `cline.list()` - List sessions
- `cline.get(sessionId)` - Get session metadata
- `cline.readMessages(sessionId)` - Read persisted messages
- `cline.getAccumulatedUsage(sessionId)` - Token/cost totals
- `cline.abort(sessionId)` - Abort a session
- `cline.delete(sessionId)` - Delete a session
- `cline.dispose()` - Clean up resources
See `api.md` for full API details.
## Event Streaming
`cline.subscribe()` emits `CoreSessionEvent` types. These are different from the `AgentRuntimeEvent` types emitted by the standalone `Agent` class -- see `../events/REFERENCE.md` for the full comparison.
```typescript
cline.subscribe((event) => {
switch (event.type) {
case "chunk":
if (event.payload.type === "text") {
process.stdout.write(event.payload.text)
}
break
case "ended":
console.log(`Session ended: ${event.payload.finishReason}`)
break
}
})
```
ClineCore results use `AgentResult` with `.text` (not `.outputText` like the standalone Agent's `AgentRunResult`).
## Session Persistence
Sessions are stored at:
```
~/.cline/data/sessions/
sessions.db # SQLite database
[session-id].json # Message history
```
## Next Steps
- `api.md` - Full ClineCore API reference
- `patterns.md` - Common patterns and best practices
- `gotchas.md` - Pitfalls and debugging
- `../tools/REFERENCE.md` - Custom tool creation
- `../plugins/REFERENCE.md` - Plugin system
- `../scheduling/REFERENCE.md` - Scheduled agents
@@ -1,304 +0,0 @@
# ClineCore API Reference
## Creating ClineCore
```typescript
import { ClineCore } from "@cline/sdk"
const cline = await ClineCore.create(options: ClineCoreOptions)
```
### ClineCoreOptions
```typescript
interface ClineCoreOptions {
clientName: string // identifies your app
distinctId?: string // user/instance identifier
backendMode?: "auto" | "local" | "hub" | "remote"
hub?: HubOptions
remote?: RemoteOptions
capabilities?: RuntimeCapabilities
toolPolicies?: Record<string, ToolPolicy>
automation?: boolean | ClineCoreAutomationOptions
fetch?: typeof fetch
}
```
### RuntimeCapabilities
```typescript
interface RuntimeCapabilities {
requestToolApproval?: (request: ToolApprovalRequest) => Promise<ToolApprovalResult>
// ... other capability callbacks
}
```
## Starting Sessions
### start(input)
```typescript
const session = await cline.start(input: ClineCoreStartInput)
```
Returns a `StartSessionResult`:
```typescript
interface StartSessionResult {
sessionId: string
manifest: SessionManifest
manifestPath: string
messagesPath: string
result?: AgentResult
}
```
### ClineCoreStartInput
```typescript
interface ClineCoreStartInput {
prompt: string
config: CoreSessionConfig
source?: string
interactive?: boolean
sessionMetadata?: Record<string, unknown>
initialMessages?: AgentMessage[]
toolPolicies?: Record<string, ToolPolicy>
capabilities?: RuntimeCapabilities
}
```
### CoreSessionConfig
```typescript
interface CoreSessionConfig {
cwd?: string // working directory
providerId: string // LLM provider
modelId: string // model identifier
apiKey?: string // provider API key
systemPrompt?: string // custom system prompt
tools?: readonly AgentTool[] // additional custom tools
enableTools?: boolean // enable built-in tools
hooks?: Partial<AgentRuntimeHooks> // runtime hooks
extensions?: AgentPlugin[] // plugins loaded inline
pluginPaths?: string[] // paths to plugin packages
extensionLoading?: "isolated" | "direct"
extensionContext?: { // context passed to plugin setup()
workspace?: { rootPath: string; cwd: string }
}
checkpointConfig?: CoreCheckpointConfig
compactionConfig?: CoreCompactionConfig
telemetry?: ITelemetryService
logger?: BasicLogger
enableSpawnAgent?: boolean // enable sub-agent spawning
enableAgentTeams?: boolean // enable team coordination
teamName?: string // team identifier
}
```
`extensions` passes plugin objects directly. `pluginPaths` points to directories with `package.json` containing a `cline.plugins` field. Set `extensionContext.workspace` so plugins receive `ctx.workspaceInfo` in their `setup()` call -- without it, `ctx.workspaceInfo` is undefined.
## Follow-Up Messages
### send({ sessionId, prompt })
Send a follow-up message to an existing session:
```typescript
const result = await cline.send({
sessionId: session.sessionId,
prompt: "Now add authentication",
})
```
Returns `AgentResult | undefined`.
## Event Subscription
### subscribe(listener, options?)
```typescript
const unsubscribe = cline.subscribe(
(event: CoreSessionEvent) => {
// handle events
},
{ sessionId: "optional-filter" }
)
```
### CoreSessionEvent
```typescript
type CoreSessionEvent =
| { type: "chunk"; payload: SessionChunkEvent }
| { type: "agent_event"; payload: { sessionId: string, event: AgentEvent } }
| { type: "ended"; payload: SessionEndedEvent }
| { type: "team_progress"; payload: SessionTeamProgressEvent }
| { type: "status"; payload: { sessionId: string, status: string } }
| { type: "hook"; payload: SessionToolEvent }
```
## Session Management
### list(limit?, options?)
```typescript
const sessions: SessionRecord[] = await cline.list(50)
```
### get(sessionId)
```typescript
const session: SessionRecord = await cline.get(sessionId)
```
### readMessages(sessionId)
```typescript
const messages: AgentMessage[] = await cline.readMessages(sessionId)
```
### getAccumulatedUsage(sessionId)
```typescript
const usage = await cline.getAccumulatedUsage(sessionId)
// usage.usage - root agent only
// usage.aggregateUsage - root + subagents/teammates
```
### update(sessionId, updates)
```typescript
await cline.update(sessionId, { title: "New title" })
```
### abort(sessionId, reason?)
```typescript
await cline.abort(sessionId, "User cancelled")
```
### stop(sessionId)
```typescript
await cline.stop(sessionId)
```
### delete(sessionId)
```typescript
await cline.delete(sessionId)
```
### restore(input)
Restore a session from a checkpoint:
```typescript
await cline.restore({ sessionId, checkpointId })
```
### dispose(reason?)
Clean up all resources. Always call this when done:
```typescript
await cline.dispose("Shutting down")
```
## AgentResult
Returned by session operations:
```typescript
interface AgentResult {
text: string
usage: LegacyAgentUsage
messages: MessageWithMetadata[]
toolCalls: ToolCallRecord[]
iterations: number
finishReason: "completed" | "max_iterations" | "aborted" | "mistake_limit" | "error"
model: { id: string; provider: string; info?: ModelInfo }
startedAt: Date
endedAt: Date
durationMs: number
}
```
## Tool Policies
Control tool access at the session level:
```typescript
const session = await cline.start({
prompt: "Review the code",
config: { ... },
toolPolicies: {
read_files: { autoApprove: true },
bash: { autoApprove: false },
editor: { enabled: false },
},
})
```
### ToolPolicy
```typescript
interface ToolPolicy {
enabled?: boolean // false = tool is hidden from the model
autoApprove?: boolean // false = requires approval callback
}
```
## Interactive Approval
```typescript
const cline = await ClineCore.create({
clientName: "my-app",
capabilities: {
requestToolApproval: async (request) => {
console.log(`Tool: ${request.toolName}, Input: ${JSON.stringify(request.input)}`)
const approved = await askUser(`Allow ${request.toolName}?`)
return { approved }
},
},
})
```
## Automation API
When `automation` is enabled in `ClineCore.create()`:
```typescript
const cline = await ClineCore.create({
clientName: "my-app",
automation: true,
})
// Access automation methods
cline.automation.start()
cline.automation.stop()
cline.automation.reconcile(specs)
cline.automation.ingestEvent(event)
cline.automation.listEvents()
cline.automation.listSpecs()
cline.automation.listRuns()
```
## Settings API
```typescript
// Read settings
const settings = await cline.settings.list()
// Toggle tools, plugins, MCP servers
await cline.settings.toggle({ type: "tool", name: "bash", enabled: true })
```
## See Also
- `REFERENCE.md` - Overview and quick start
- `patterns.md` - Common patterns
- `gotchas.md` - Pitfalls
- `../tools/REFERENCE.md` - Tool creation
- `../plugins/REFERENCE.md` - Plugin system
@@ -1,148 +0,0 @@
# ClineCore Gotchas
## Always Call dispose()
`ClineCore` holds resources (file watchers, database connections, hub connections). Failing to call `dispose()` can leave orphan processes and file locks.
```typescript
const cline = await ClineCore.create({ clientName: "my-app" })
try {
// ... use cline
} finally {
await cline.dispose()
}
```
## Node.js 22 Required
ClineCore and `@cline/core` require Node.js 22 or later. If you're on an older version, you'll get runtime errors. Check with `node --version`.
## Session Config vs Global Config
Tool policies can be set at two levels:
- Global: in `ClineCore.create({ toolPolicies })` -- applies to all sessions
- Per-session: in `cline.start({ toolPolicies })` -- overrides global for that session
Per-session policies take precedence.
## enableTools Must Be Explicit
Built-in tools (bash, editor, read_files, etc.) are not available unless you set `enableTools: true` in the session config:
```typescript
await cline.start({
prompt: "Read package.json",
config: {
providerId: "anthropic",
modelId: "claude-sonnet-4-6",
enableTools: true, // required for built-in tools
},
})
```
Without this, the agent only has access to custom tools you provide via `config.tools`.
## cwd Matters for Built-in Tools
Built-in tools like `bash`, `editor`, and `read_files` operate relative to `config.cwd`. If not set, they use the process working directory. Always set it explicitly for predictable behavior:
```typescript
config: {
cwd: "/absolute/path/to/project",
// ...
}
```
## Hub Startup Latency
With `backendMode: "auto"`, the first session may be slow if a hub daemon needs to be spawned. For immediate responsiveness:
- Use `backendMode: "local"` for in-process execution (fastest startup)
- Pre-warm the hub with `cline hub ensure` CLI command
- Accept the one-time startup cost and let subsequent sessions reuse the hub
## Session Storage Location
Sessions are stored at `~/.cline/data/sessions/`. This includes:
- `sessions.db` - SQLite database with session metadata
- `[session-id].json` - Individual message history files
If you're running in a container or ephemeral environment, these paths may not persist across restarts.
## requestToolApproval Blocks Execution
When a tool policy has `autoApprove: false` and you provide a `requestToolApproval` callback, the agent loop blocks until your callback resolves. If your callback never resolves (e.g., waiting for user input that never comes), the session hangs.
For automated pipelines, either:
- Set all tools to `autoApprove: true`
- Implement a timeout in your approval callback
## Plugin Discovery Paths
ClineCore discovers plugins from:
- Global: `~/.cline/plugins/`
- Workspace: `.cline/plugins/`
For SDK consumers, pass plugins via `extensions: [plugin]` or `pluginPaths: ["./path"]` in the session config.
If a plugin isn't loading, verify:
- The file is in one of the discovery directories, or passed via `extensions`/`pluginPaths`
- The file exports a default plugin object with a non-empty `manifest.capabilities` array
- Every `api.register*` call in `setup()` has a matching capability declared
- If `hooks` is present on the plugin, `"hooks"` is in `capabilities`
## extensionContext.workspace Is Required for Plugins
If your plugins use `ctx.workspaceInfo` (e.g., to resolve workspace paths), you must set `extensionContext.workspace` in the session config. Without it, `ctx.workspaceInfo` is undefined:
```typescript
await cline.start({
config: {
extensions: [myPlugin],
extensionContext: {
workspace: { rootPath: process.cwd(), cwd: process.cwd() },
},
},
})
```
The CLI sets this automatically, but SDK consumers must set it explicitly.
## send() Requires an Active Session
`cline.send()` only works on sessions that are still active. If a session has already completed, `send()` may return `undefined` or fail. Check session status with `cline.get(sessionId)` first.
## Result May Be Undefined
`session.result` can be `undefined` if the session was started but hasn't completed yet (e.g., in a non-blocking hub mode). Check for this:
```typescript
const session = await cline.start({ ... })
if (session.result) {
console.log(session.result.text)
} else {
console.log("Session started but not yet complete")
}
```
## Compaction and Long Sessions
For long-running sessions, message history grows and eventually exceeds the model's context window. ClineCore handles this via compaction, which summarizes older messages. Configure it via `compactionConfig`:
```typescript
config: {
compactionConfig: {
strategy: "summarize",
// ...
},
}
```
The default strategy works for most cases, but extremely long sessions may benefit from tuning.
## See Also
- `api.md` - Full API reference
- `patterns.md` - Common patterns
- `../agent/gotchas.md` - Agent-level gotchas
- `../tools/REFERENCE.md` - Tool troubleshooting
- `../providers/REFERENCE.md` - Provider troubleshooting
@@ -1,279 +0,0 @@
# ClineCore Patterns
## Basic Session with Built-in Tools
```typescript
import { ClineCore } from "@cline/sdk"
const cline = await ClineCore.create({ clientName: "my-app" })
const session = await cline.start({
prompt: "Read package.json and summarize the dependencies",
config: {
providerId: "anthropic",
modelId: "claude-sonnet-4-6",
apiKey: process.env.ANTHROPIC_API_KEY,
cwd: process.cwd(),
enableTools: true,
},
})
console.log(session.result?.text)
await cline.dispose()
```
## Streaming Session with UI Updates
```typescript
const cline = await ClineCore.create({ clientName: "my-app" })
cline.subscribe((event) => {
switch (event.type) {
case "chunk":
if (event.payload.type === "text") {
ui.appendText(event.payload.text)
}
break
case "ended":
ui.showComplete(event.payload.finishReason)
break
}
})
await cline.start({
prompt: "Refactor the auth module",
config: {
providerId: "anthropic",
modelId: "claude-sonnet-4-6",
cwd: "/path/to/project",
enableTools: true,
},
})
```
## Multi-Turn Session
```typescript
const cline = await ClineCore.create({ clientName: "my-app" })
const session = await cline.start({
prompt: "Create a new Express server",
config: {
providerId: "anthropic",
modelId: "claude-sonnet-4-6",
cwd: "/path/to/project",
enableTools: true,
},
})
// Follow-up
const result = await cline.send({
sessionId: session.sessionId,
prompt: "Now add a health check endpoint",
})
console.log(result?.text)
await cline.dispose()
```
## Tiered Permission Model
Auto-approve reads, require approval for writes:
```typescript
const cline = await ClineCore.create({
clientName: "my-app",
toolPolicies: {
read_files: { autoApprove: true },
search: { autoApprove: true },
fetch_web: { autoApprove: true },
bash: { autoApprove: false },
editor: { autoApprove: false },
apply_patch: { autoApprove: false },
},
capabilities: {
requestToolApproval: async (request) => {
const approved = await promptUser(
`Allow ${request.toolName}?\n${JSON.stringify(request.input, null, 2)}`
)
return { approved }
},
},
})
```
## Custom Tools Alongside Built-ins
```typescript
import { ClineCore, createTool } from "@cline/sdk"
import { z } from "zod"
const deployTool = createTool({
name: "deploy",
description: "Deploy the application to the specified environment.",
inputSchema: z.object({
environment: z.enum(["staging", "production"]),
}),
execute: async (input) => {
const result = await runDeployment(input.environment)
return { url: result.url, status: "deployed" }
},
})
const cline = await ClineCore.create({ clientName: "my-app" })
await cline.start({
prompt: "Deploy the app to staging",
config: {
providerId: "anthropic",
modelId: "claude-sonnet-4-6",
cwd: process.cwd(),
enableTools: true,
tools: [deployTool],
},
})
```
## Session with Plugins
Load plugins inline with `extensions` and provide workspace context so plugins can access `ctx.workspaceInfo`:
```typescript
import { ClineCore } from "@cline/sdk"
import myPlugin from "./my-plugin"
const cline = await ClineCore.create({
clientName: "my-app",
backendMode: "local",
})
await cline.start({
prompt: "Do the thing my plugin enables",
config: {
providerId: "anthropic",
modelId: "claude-sonnet-4-6",
cwd: process.cwd(),
enableTools: true,
extensions: [myPlugin],
extensionContext: {
workspace: { rootPath: process.cwd(), cwd: process.cwd() },
},
},
})
await cline.dispose()
```
For directory-based plugin packages, use `pluginPaths` instead:
```typescript
config: {
pluginPaths: ["./my-cline-plugin"],
extensionContext: {
workspace: { rootPath: process.cwd(), cwd: process.cwd() },
},
}
```
See `../plugins/REFERENCE.md` for the full plugin authoring guide.
## Session Listing and Replay
```typescript
const cline = await ClineCore.create({ clientName: "my-app" })
// List recent sessions
const sessions = await cline.list(10)
for (const session of sessions) {
console.log(`${session.id}: ${session.title}`)
}
// Read messages from a past session
const messages = await cline.readMessages(sessions[0].id)
for (const msg of messages) {
console.log(`[${msg.role}] ${msg.content}`)
}
// Check usage
const usage = await cline.getAccumulatedUsage(sessions[0].id)
console.log(`Total tokens: ${usage.aggregateUsage.totalInputTokens + usage.aggregateUsage.totalOutputTokens}`)
```
## Graceful Shutdown
```typescript
const cline = await ClineCore.create({ clientName: "my-app" })
process.on("SIGTERM", async () => {
await cline.dispose("SIGTERM received")
process.exit(0)
})
// Run sessions...
```
## Stateless Worker Pattern
For request/response workloads (API endpoints, queue consumers):
```typescript
import { ClineCore } from "@cline/sdk"
const cline = await ClineCore.create({
clientName: "worker",
backendMode: "local",
})
async function handleRequest(prompt: string, workspace: string) {
const session = await cline.start({
prompt,
config: {
providerId: "anthropic",
modelId: "claude-sonnet-4-6",
cwd: workspace,
enableTools: true,
},
})
return {
text: session.result?.text,
usage: session.result?.usage,
sessionId: session.sessionId,
}
}
```
## Hub-Backed Multi-Client
Multiple clients can attach to the same session:
```typescript
// Process 1: start session
const cline = await ClineCore.create({
clientName: "backend",
backendMode: "hub",
})
const session = await cline.start({
prompt: "Long running refactor task",
config: { ... },
})
// Process 2: attach and stream events
const viewer = await ClineCore.create({
clientName: "dashboard",
backendMode: "hub",
})
viewer.subscribe((event) => {
dashboard.render(event)
}, { sessionId: session.sessionId })
```
## See Also
- `api.md` - Full API reference
- `gotchas.md` - Common pitfalls
- `../tools/REFERENCE.md` - Tool creation
- `../plugins/REFERENCE.md` - Plugin system
- `../scheduling/REFERENCE.md` - Scheduled agents
@@ -1,269 +0,0 @@
# Events
The Cline SDK has three event layers. Which one you use depends on whether you're working with the standalone `Agent` class or `ClineCore`.
## Which Events Do I Get?
| If you use... | You subscribe with... | You receive... | Text streaming event |
|---|---|---|---|
| Standalone `Agent` | `agent.subscribe()` | `AgentRuntimeEvent` | `assistant-text-delta` |
| `ClineCore` | `cline.subscribe()` | `CoreSessionEvent` | `chunk` (with `payload.type === "text"`) |
These are different event types with different shapes. Do not mix them up.
## Layer 1: AgentRuntimeEvent (Standalone Agent)
Emitted by the `Agent` class via `agent.subscribe()`. This is what you get when using `new Agent(...)` directly. Every event includes a `snapshot` field with the current `AgentRuntimeStateSnapshot`.
### Run Lifecycle
```typescript
{ type: "run-started", snapshot }
{ type: "run-finished", snapshot, result: AgentRunResult }
{ type: "run-failed", snapshot, error: Error }
```
### Turns
```typescript
{ type: "turn-started", snapshot, iteration: number }
{ type: "turn-finished", snapshot, iteration: number, toolCallCount: number }
```
### Text Streaming
```typescript
// Streaming text delta (arrives as chunks during generation)
{ type: "assistant-text-delta", snapshot, iteration: number, text: string, accumulatedText: string }
// Streaming reasoning delta (when model uses extended thinking)
{ type: "assistant-reasoning-delta", snapshot, iteration: number, text: string }
// Complete assistant message after model finishes
{ type: "assistant-message", snapshot, iteration: number, message: AgentMessage, finishReason: string }
```
### Messages
```typescript
// Fired when any message (user or assistant) is added to conversation history
{ type: "message-added", snapshot, message: AgentMessage }
```
### Tool Events
```typescript
{ type: "tool-started", snapshot, toolCall: { toolName: string, toolCallId: string, input: unknown } }
{ type: "tool-updated", snapshot, toolCall: { toolName: string, toolCallId: string }, update: string }
{ type: "tool-finished", snapshot, toolCall: { toolName: string, toolCallId: string }, message: AgentMessage }
```
### Usage
```typescript
{
type: "usage-updated",
snapshot,
usage: {
inputTokens: number,
outputTokens: number,
cacheReadTokens?: number,
cacheWriteTokens?: number,
totalCost?: number,
},
}
```
### Notices
```typescript
{ type: "status-notice", snapshot, message: string, metadata?: Record<string, unknown> }
```
### Subscribing
Use `agent.subscribe()`. Register the listener before calling `run()` to avoid missing early events.
```typescript
const agent = new Agent({
providerId: "anthropic",
modelId: "claude-sonnet-4-6",
apiKey: process.env.ANTHROPIC_API_KEY,
systemPrompt: "You are a helpful assistant.",
tools: [],
})
agent.subscribe((event) => {
switch (event.type) {
case "assistant-text-delta":
process.stdout.write(event.text)
break
case "tool-started":
console.log(`\nUsing tool: ${event.toolCall.toolName}`)
break
case "usage-updated":
console.log(`Cost: $${event.usage.totalCost?.toFixed(4)}`)
break
case "run-finished":
console.log(`\nDone: ${event.result.status}`)
break
}
})
const result = await agent.run("Hello!")
```
You can also receive events through hooks (these are awaited, so they can be async):
```typescript
const agent = new Agent({
...config,
hooks: {
onEvent: async (event) => {
// Same AgentRuntimeEvent types as subscribe()
},
},
})
```
## Layer 2: AgentEvent (ClineCore Internal)
When using `ClineCore`, a `RuntimeEventAdapter` translates Layer 1 events into a legacy format called `AgentEvent`. You do not interact with this layer directly -- it is projected into `CoreSessionEvent` for subscribers. The key mappings:
| AgentRuntimeEvent (Layer 1) | AgentEvent (Layer 2) |
|---|---|
| `turn-started` | `iteration_start` |
| `turn-finished` | `iteration_end` |
| `assistant-text-delta` | `content_start` (text) |
| `assistant-message` | `content_end` (text) |
| `tool-started` | `content_start` (tool) |
| `tool-updated` | `content_update` (tool) |
| `tool-finished` | `content_end` (tool) |
| `usage-updated` | `usage` (with computed deltas) |
| `run-finished` | `done` |
| `run-failed` | `error` |
| `run-started`, `message-added` | (suppressed, not emitted) |
This layer exists for backwards compatibility. If you see event types like `content_update` or `iteration_start` in other documentation, they refer to this layer, not to what `agent.subscribe()` emits.
## Layer 3: CoreSessionEvent (ClineCore Subscriber)
Emitted by `ClineCore` via `cline.subscribe()`. These are higher-level session events.
```typescript
type CoreSessionEvent =
| { type: "chunk"; payload: SessionChunkEvent }
| { type: "agent_event"; payload: { sessionId: string, event: AgentEvent } }
| { type: "ended"; payload: SessionEndedEvent }
| { type: "team_progress"; payload: SessionTeamProgressEvent }
| { type: "status"; payload: { sessionId: string, status: string } }
| { type: "hook"; payload: SessionToolEvent }
```
### SessionChunkEvent
```typescript
interface SessionChunkEvent {
type: "text" | "reasoning"
text: string
sessionId: string
}
```
### SessionEndedEvent
```typescript
interface SessionEndedEvent {
sessionId: string
finishReason: "completed" | "max_iterations" | "aborted" | "mistake_limit" | "error"
result?: AgentResult
}
```
### Subscribing
```typescript
cline.subscribe((event) => {
switch (event.type) {
case "chunk":
if (event.payload.type === "text") {
process.stdout.write(event.payload.text)
}
break
case "ended":
console.log(`Finished: ${event.payload.finishReason}`)
break
}
})
```
Filter by session:
```typescript
cline.subscribe(handler, { sessionId: "specific-session-id" })
```
## Hub Events (Layer 3b)
When ClineCore runs in hub mode (via `backendMode: "hub"` or `"auto"` when a hub is available), events are projected over WebSocket using `HubEventName` types like `assistant.delta`, `iteration.started`, `tool.started`, etc. You do not interact with these directly -- `cline.subscribe()` still gives you `CoreSessionEvent` regardless of backend mode.
## Result Type Differences
The standalone Agent and ClineCore return different result types:
| API | Result type | Text property |
|---|---|---|
| `agent.run()` | `AgentRunResult` | `result.outputText` |
| `cline.start()` / `cline.send()` | `AgentResult` | `result.text` |
## Common Patterns
### Streaming Text (Standalone Agent)
```typescript
agent.subscribe((event) => {
if (event.type === "assistant-text-delta") {
process.stdout.write(event.text)
}
})
```
### Streaming Text (ClineCore)
```typescript
cline.subscribe((event) => {
if (event.type === "chunk" && event.payload.type === "text") {
process.stdout.write(event.payload.text)
}
})
```
### Usage Tracking (Standalone Agent)
```typescript
agent.subscribe((event) => {
if (event.type === "usage-updated" && event.usage.totalCost) {
console.log(`Running cost: $${event.usage.totalCost.toFixed(4)}`)
}
})
```
### Tool Call Logging (Standalone Agent)
```typescript
agent.subscribe((event) => {
if (event.type === "tool-started") {
console.log(`Tool started: ${event.toolCall.toolName}`)
}
if (event.type === "tool-finished") {
console.log(`Tool finished: ${event.toolCall.toolName}`)
}
})
```
## See Also
- `../agent/REFERENCE.md` - Agent runtime overview
- `../clinecore/REFERENCE.md` - ClineCore session management
- `../plugins/REFERENCE.md` - Plugin hooks for lifecycle events
- `../production/REFERENCE.md` - Observability in production
@@ -1,157 +0,0 @@
# Multi-Agent Coordination
The Cline SDK supports two models for multi-agent work: sub-agents (parent-child) and teams (peer-to-peer).
## Sub-Agents vs Teams
| Feature | Sub-Agents | Teams |
|---------|-----------|-------|
| Enable with | `enableSpawnAgent: true` | `enableAgentTeams: true` |
| Persistence | Session-scoped only | Across sessions |
| Coordination | Parent-child hierarchy | Peer-to-peer |
| Shared state | None | Task board, mailbox, mission log |
| Best for | One-off delegation | Complex multi-session projects |
## Sub-Agents
Sub-agents are spawned by a parent agent during a run. They execute independently and report results back.
### Enabling Sub-Agents
```typescript
const cline = await ClineCore.create({ clientName: "my-app" })
await cline.start({
prompt: "Refactor the auth module and update tests",
config: {
providerId: "anthropic",
modelId: "claude-sonnet-4-6",
enableSpawnAgent: true,
enableTools: true,
},
})
```
When `enableSpawnAgent` is true, the agent gets access to sub-agent tools:
| Tool | Description |
|------|-------------|
| `start_subagent` | Spawn a background agent with a task |
| `message_subagent` | Send a message to a running sub-agent |
| `handoff_to_agent` | Delegate the current task entirely |
| `submit_and_exit` | Signal completion |
### How Sub-Agents Work
1. The parent agent decides a subtask can be delegated
2. It calls `start_subagent` with a role, task description, and optionally a preset
3. The sub-agent runs independently in the background
4. The parent can check status or send follow-up messages
5. Sub-agent results are available to the parent when complete
## Teams
Teams provide persistent, cross-session coordination between agents.
### Enabling Teams
```typescript
await cline.start({
config: {
providerId: "anthropic",
modelId: "claude-sonnet-4-6",
enableAgentTeams: true,
teamName: "auth-sprint",
enableTools: true,
},
})
```
### Team Tools
When `enableAgentTeams` is true, the coordinator agent gets:
| Tool | Description |
|------|-------------|
| `team_spawn_teammate` | Create a new agent with a role and task |
| `team_delegate_task` | Assign a task to an existing teammate |
| `team_check_status` | Check on a delegated task's progress |
| `team_get_result` | Get the completed result from a teammate |
### Team Persistence
Teams store shared state in:
```
~/.cline/data/teams/[team-name]/
task-board.json # task assignments and status
mailbox.json # inter-agent messages
mission-log.json # coordination log
```
This state persists across sessions, so team members can pick up where they left off.
### CLI Team Access
```bash
cline --team-name auth-sprint "Continue the auth refactor"
```
## Choosing Between Sub-Agents and Teams
Use sub-agents when:
- You need one-off parallel execution within a single session
- Tasks are independent and don't need to communicate with each other
- Results only matter to the parent agent
Use teams when:
- Work spans multiple sessions over time
- Agents need to coordinate and share progress
- Tasks have dependencies between them
- You want a persistent record of multi-agent collaboration
## Patterns
### Parallel Research with Sub-Agents
A parent agent spawns multiple sub-agents to research different topics simultaneously:
```typescript
await cline.start({
prompt: `Research these three topics in parallel:
1. Current best practices for JWT auth
2. OAuth 2.0 provider comparison
3. Session management patterns
Spawn a sub-agent for each topic, then synthesize the results.`,
config: {
enableSpawnAgent: true,
enableTools: true,
// ...
},
})
```
### Team Sprint
A coordinator manages a multi-session project:
```typescript
await cline.start({
prompt: `You are the coordinator for the auth-sprint team.
Review the task board and delegate the next highest-priority task
to a teammate. Check status on any in-progress tasks.`,
config: {
enableAgentTeams: true,
teamName: "auth-sprint",
enableTools: true,
// ...
},
})
```
## See Also
- `../clinecore/REFERENCE.md` - ClineCore runtime
- `../clinecore/api.md` - Session config for teams
- `../tools/REFERENCE.md` - Tool system
- `../plugins/REFERENCE.md` - Plugin system
@@ -1,649 +0,0 @@
# Plugins
A Cline plugin is a TypeScript module that extends any agent built on the Cline SDK. The same plugin runs in the Cline CLI, VS Code and JetBrains extensions, and any custom app built on `@cline/core`.
A plugin can:
- Register tools the model can call.
- Hook into the agent loop before/after runs, model calls, and tool calls.
- Rewrite provider messages before they hit the model (custom compaction, redaction, context shaping).
- Register slash commands, prompt rules, providers, and automation event types.
A plugin ships in one of two shapes:
1. Single-file plugin -- one `.ts` file that exports a default plugin object. Drop it in a discovery folder and it loads.
2. Plugin package -- a directory with `package.json`, npm dependencies, and optionally bundled assets. Installable via `cline plugin install`.
Both shapes use the same plugin API.
## The Mental Model
When the host starts a session, it builds a registry of plugins and runs four phases:
1. resolve -- collect the plugin objects.
2. validate -- check each plugin's `manifest`. Capabilities must be non-empty; declared hook stages must have matching handlers; if `hooks` is present, `"hooks"` must be in `capabilities`.
3. setup -- call each plugin's `setup(api, ctx)` once. This is where you `registerTool`, `registerCommand`, etc.
4. activate -- registry is frozen, the agent loop starts, and your hooks/tools are live.
Two invariants the registry enforces:
- Every contribution requires a matching capability. Calling `api.registerRule(...)` without `"rules"` in `manifest.capabilities` throws.
- Capabilities and handlers must agree. Declaring `"hooks"` without a `hooks` object, or vice versa, fails validation.
After validation, registration is one-shot -- no dynamic register/unregister during the session.
## The Smallest Working Plugin
```typescript
import type { AgentPlugin } from "@cline/core"
import { createTool } from "@cline/core"
const plugin: AgentPlugin = {
name: "hello-plugin",
manifest: {
capabilities: ["tools"],
},
setup(api, ctx) {
api.registerTool(
createTool({
name: "say_hello",
description: "Greet a person by name.",
inputSchema: {
type: "object",
properties: { name: { type: "string" } },
required: ["name"],
},
async execute({ name }: { name: string }) {
return { greeting: `Hello, ${name}!` }
},
}),
)
},
}
export default plugin
```
The agent will see `say_hello` as a callable tool.
## The Manifest
```typescript
manifest: {
capabilities: ["tools", "hooks"], // required, non-empty array
paths?: string[], // optional, multi-entry packages
providerIds?: string[], // optional, provider plugins
modelIds?: string[], // optional, model plugins
}
```
### The Complete Capability List
| Capability | What It Unlocks in `api` |
|-----------|--------------------------|
| `"tools"` | `api.registerTool()` |
| `"commands"` | `api.registerCommand()` (slash commands in chat surfaces) |
| `"rules"` | `api.registerRule()` (string injected into the system prompt) |
| `"messageBuilders"` | `api.registerMessageBuilder()` (rewrites provider-bound messages) |
| `"providers"` | `api.registerProvider()` (custom model provider) |
| `"automationEvents"` | `api.registerAutomationEventType()` and `ctx.automation?.ingestEvent()` |
| `"hooks"` | The runtime `hooks` object on the plugin (lifecycle callbacks) |
You declare any combination -- most real plugins need 1-3 capabilities.
## setup(api, ctx) -- The Registration Phase
`setup()` runs once per session before the agent loop starts. Everything you register here is frozen for the lifetime of the session.
### The api Object
Each `register*` method requires the matching capability in your manifest:
```typescript
api.registerTool(tool) // requires "tools"
api.registerCommand({ name, description, handler }) // requires "commands"
api.registerRule({ id, content, source }) // requires "rules"
api.registerMessageBuilder({ name, build }) // requires "messageBuilders"
api.registerProvider({ name, description }) // requires "providers"
api.registerAutomationEventType({ eventType, source }) // requires "automationEvents"
```
### The ctx Object -- Host-Provided Session Context
The second argument carries everything the host knows about the current session. All fields are optional, so feature-detect before using them -- the same plugin must work in hosts that supply less context (unit tests, sandboxed plugin processes).
```typescript
ctx.session?.sessionId // string, stable core session id
ctx.client?.name // host: "cline-cli", "cline-vscode", etc.
ctx.user // authenticated user/org info, when available
ctx.workspaceInfo // { rootPath, hint, latestGitBranchName,
// latestGitCommitHash, associatedRemoteUrls }
ctx.automation?.ingestEvent // emit normalized automation events
ctx.logger?.log // structured logger scoped to this plugin
ctx.telemetry // ITelemetryService, only present in-process
```
Two rules about `ctx.workspaceInfo`:
1. Always prefer `ctx.workspaceInfo?.rootPath` over `process.cwd()`. The CLI may have been launched with `--cwd` without calling `chdir`, and VS Code workspaces don't share a single CWD. `workspaceInfo` is sourced from the session config and is always correct.
2. Don't use `import.meta.url` tricks to find "the workspace". That gives you the plugin's own location, not the user's project.
### Persisting State Across Hooks
`setup()` runs first; hooks fire later. The simplest way to share state is module-level variables:
```typescript
let sessionWorkspaceRoot: string | undefined
let sessionBranch: string | undefined
const plugin: AgentPlugin = {
name: "metrics",
manifest: { capabilities: ["hooks"] },
setup(api, ctx) {
sessionWorkspaceRoot = ctx.workspaceInfo?.rootPath
sessionBranch = ctx.workspaceInfo?.latestGitBranchName
},
hooks: {
beforeTool({ toolCall, input }) {
if (sessionBranch === "main" && toolCall.toolName === "run_commands") {
// inspect input, optionally block
}
return undefined
},
},
}
```
A single Node process may host multiple sessions concurrently. If your plugin will run in a multi-session host, key your state by `ctx.session?.sessionId`:
```typescript
const stateBySession = new Map<string, MyState>()
setup(api, ctx) {
const id = ctx.session?.sessionId
if (id) stateBySession.set(id, /* ... */)
}
```
## Runtime Hooks
Runtime hooks are typed in-process callbacks on the same hook layer the runtime uses internally. They run inside the agent loop with full type information -- no IPC, no JSON marshaling.
Declare `"hooks"` in `manifest.capabilities`, then add a `hooks` property:
```typescript
const plugin: AgentPlugin = {
name: "metrics",
manifest: { capabilities: ["hooks"] },
hooks: {
beforeRun(ctx) { /* ... */ },
beforeTool({ toolCall, input }) { /* ... */ },
afterTool({ toolCall, result }) { /* ... */ },
afterRun({ result }) { /* ... */ },
onEvent(event) { /* ... */ },
},
}
```
### The Seven Hooks
| Hook | Fires | Can Stop the Loop? | Common Uses |
|------|-------|--------------------|-------------|
| `beforeRun` | Before the runtime loop starts | Yes | Greet, log, attach session metadata |
| `afterRun` | After the runtime loop finishes (success, abort, or fail) | No | Notifications, metrics, persistent logs |
| `beforeModel` | Before each model request | Yes (mutate req) | Inject context, last-mile prompt edits |
| `afterModel` | After each model response, before tool execution | Yes | Block based on model output |
| `beforeTool` | Before each tool execution | Yes (`{ stop }`) | Audit, redact, block dangerous tools |
| `afterTool` | After each tool execution | Can replace result | Post-process, redact secrets in tool output |
| `onEvent` | On every `AgentRuntimeEvent` emitted by the runtime | No | Streaming UIs, telemetry pipes |
### Stopping the Loop from a Hook
Several hooks return an optional control object. The most common pattern is `beforeTool` blocking a destructive tool call:
```typescript
beforeTool({ toolCall, input }) {
if (toolCall.toolName === "run_commands") {
const { commands } = input as { commands?: string[] }
if (sessionBranch === "main" && commands?.some(c => c.startsWith("git push"))) {
return { stop: true, reason: "Blocked git push on protected branch" }
}
}
return undefined // explicit "continue"
}
```
Returning `undefined` (or omitting `return`) lets execution continue normally.
### afterRun Semantics
`afterRun` fires for every terminal status -- `completed`, `aborted`, `failed`. If you only want to act on success:
```typescript
afterRun({ result }) {
if (result.status !== "completed") return
// notify, log success metrics, etc.
}
```
### Plugin Hooks vs File Hooks
The runtime supports two hook systems:
- File hooks -- external scripts in `.cline/hooks/` invoked with serialized JSON. Right for user/workspace-specific scripts that don't ship with code.
- Plugin runtime hooks -- typed in-process callbacks. Right when the behavior belongs to a reusable extension and needs typed access to the runtime.
Core adapts file hooks onto the runtime hook layer, so you don't need both. If you're shipping a plugin, write it as runtime hooks.
## Message Builders
Message builders rewrite the provider-bound message list before the model call. They run after runtime messages are converted into SDK message blocks but before core's built-in safety builder.
Use them for:
- Custom compaction policies (replace middle history with a summary).
- Redacting PII or secrets before they reach the provider.
- Reshaping context for a specific model's strengths.
```typescript
api.registerMessageBuilder({
name: "summarize-middle-history",
build(messages) {
if (estimateTokens(messages) < THRESHOLD) return messages
return [...prefix, summary, ...recent]
},
})
```
Multiple builders run in registration order; the output of one is the input of the next.
When to use `beforeModel` instead: reach for the `beforeModel` hook only if you need the runtime snapshot or want to mutate the request object itself. Pure message rewrites belong in a builder.
## Automation Events
Plugins can declare normalized event types and emit them into Cline automation. Hosts that don't have automation enabled simply ignore both -- feature-detect `ctx.automation`.
```typescript
manifest: { capabilities: ["automationEvents"] },
setup(api, ctx) {
api.registerAutomationEventType({
eventType: "github.pull_request.opened",
source: "github",
description: "A new GitHub PR was opened",
attributesSchema: { /* JSON Schema for envelope.attributes */ },
})
if (!ctx.automation) return // host has no automation
ctx.automation.ingestEvent({
eventId: "pr-1234",
eventType: "github.pull_request.opened",
source: "github",
subject: "owner/repo#1234",
occurredAt: new Date().toISOString(),
attributes: { /* ... */ },
})
}
```
## Loading a Plugin
There are three ways a plugin gets into a session:
### Auto-Discovery (CLI)
The CLI scans these directories on startup:
- `<workspace>/.cline/plugins/` -- project-scoped plugins.
- `~/.cline/plugins/` -- user-scoped plugins.
Drop a `.ts` or `.js` file in, run `cline`, done:
```bash
mkdir -p .cline/plugins
cp my-plugin.ts .cline/plugins/
cline -i "do the thing my plugin enables"
```
### Explicit extensions in SDK Config
When you build your own host with `ClineCore`, pass the plugin object directly:
```typescript
import plugin from "./my-plugin"
import { ClineCore } from "@cline/core"
const host = await ClineCore.create({ backendMode: "local" })
await host.start({
config: {
providerId: "anthropic",
modelId: "claude-sonnet-4-6",
apiKey: process.env.ANTHROPIC_API_KEY ?? "",
cwd: process.cwd(),
enableTools: true,
systemPrompt: "You are a helpful assistant.",
extensions: [plugin],
extensionContext: {
workspace: { rootPath: process.cwd(), cwd: process.cwd() },
},
},
prompt: "...",
interactive: false,
})
```
### pluginPaths for Directory-Based Plugins
When the plugin is a directory with `package.json`, point `pluginPaths` at the directory:
```typescript
config: {
pluginPaths: ["./path/to/my-plugin-package"],
}
```
Or install with the CLI:
```bash
cline plugin install ./path/to/my-plugin-package
cline plugin install @scope/my-cline-plugin # from npm
cline plugin install --git github.com/owner/repo # from git
```
## Single-File Plugin Template
Save as `my-plugin.ts`, drop in `.cline/plugins/`:
```typescript
import { type AgentPlugin, ClineCore, createTool } from "@cline/core"
let sessionRoot: string | undefined
const plugin: AgentPlugin = {
name: "my-plugin",
manifest: {
capabilities: ["tools", "hooks"],
},
setup(api, ctx) {
sessionRoot = ctx.workspaceInfo?.rootPath
api.registerTool(
createTool({
name: "do_thing",
description: "Do the thing this plugin exists for.",
inputSchema: {
type: "object",
properties: { target: { type: "string" } },
required: ["target"],
},
async execute(input) {
const { target } = input as { target: string }
return { ok: true, target, root: sessionRoot }
},
}),
)
},
hooks: {
beforeRun() {
console.log("[my-plugin] run started")
},
afterRun({ result }) {
if (result.status !== "completed") return
console.log(`[my-plugin] done in ${result.iterations} iteration(s)`)
},
},
}
async function runDemo(): Promise<void> {
const host = await ClineCore.create({ backendMode: "local" })
try {
const result = await host.start({
config: {
providerId: "anthropic",
modelId: "claude-sonnet-4-6",
apiKey: process.env.ANTHROPIC_API_KEY ?? "",
cwd: process.cwd(),
enableTools: true,
systemPrompt: "You are a helpful assistant. Use tools when needed.",
extensions: [plugin],
extensionContext: {
workspace: { rootPath: process.cwd(), cwd: process.cwd() },
},
},
prompt: "Use do_thing on the target 'world'.",
interactive: false,
})
console.log(result.result?.text ?? "")
} finally {
await host.dispose()
}
}
if (import.meta.main) {
await runDemo()
}
export { plugin, runDemo }
export default plugin
```
Copy it, rename the tool, swap in your logic. The `runDemo()` function lets you test with `ANTHROPIC_API_KEY=sk-... bun run my-plugin.ts`.
## Plugin Package
Use a plugin package when you need npm dependencies, multiple entry points, bundled assets, or npm/git distribution.
### Layout
```
my-cline-plugin/
+-- package.json
+-- tsconfig.json (optional, for local typechecking)
+-- index.ts (the plugin entry point)
+-- README.md
+-- assets/ (optional, bundled content)
+-- templates/
+-- schemas/
```
### package.json -- The Discovery Contract
```json
{
"name": "my-cline-plugin",
"version": "0.1.0",
"private": true,
"description": "What this plugin does, in one sentence.",
"type": "module",
"exports": {
".": "./index.ts"
},
"cline": {
"plugins": [
{
"paths": ["./index.ts"],
"capabilities": ["tools", "hooks"]
}
]
},
"peerDependencies": {
"@cline/core": "*"
},
"peerDependenciesMeta": {
"@cline/core": { "optional": true }
},
"dependencies": {
"zod": "^4.1.5"
}
}
```
Key fields:
- `type: "module"` -- required. Cline plugins are ES modules.
- `cline.plugins` -- the discovery contract. Array of entries, each with `paths` (entry files) and `capabilities` (pre-declared, validated before importing).
- `peerDependencies` for `@cline/core` -- the host already provides it. Marking it optional lets you typecheck in isolation.
### Bundling Assets
Resolve asset paths with `import.meta.url`, not `process.cwd()`:
```typescript
import { dirname, join } from "node:path"
import { fileURLToPath } from "node:url"
import { readFileSync, existsSync } from "node:fs"
const MODULE_DIR = dirname(fileURLToPath(import.meta.url))
const TEMPLATES_DIR = join(MODULE_DIR, "assets", "templates")
function loadTemplate(name: string): string | undefined {
const path = join(TEMPLATES_DIR, `${name}.md`)
return existsSync(path) ? readFileSync(path, "utf8") : undefined
}
```
This is the only place `import.meta.url` is appropriate in a plugin -- locating files inside the plugin package. For workspace paths, always use `ctx.workspaceInfo?.rootPath`.
### The Override Pattern (Bundled / Global / Project)
A package can ship default assets and let users override them. The convention is a three-tier lookup, last write wins by `name`:
1. bundled -- files inside the plugin package (defaults shipped with the plugin).
2. global -- files under `~/.cline/data/settings/<kind>/` (user overrides).
3. project -- files under `<workspace>/.cline/<kind>/` (project overrides).
### Multiple Plugin Entries
If your package exposes more than one plugin, list each in `cline.plugins`:
```json
"cline": {
"plugins": [
{ "paths": ["./tools-plugin.ts"], "capabilities": ["tools"] },
{ "paths": ["./hooks-plugin.ts"], "capabilities": ["hooks"] }
]
}
```
Each entry file should `export default` its own plugin object.
## Testing Your Plugin
### Unit Tests
The plugin object is plain data. Drive `setup()` against a minimal context and exercise tools directly:
```typescript
import plugin from "../my-plugin"
const tools: unknown[] = []
const api = {
registerTool: (t: unknown) => tools.push(t),
registerCommand: () => {},
registerRule: () => {},
registerMessageBuilder: () => {},
registerProvider: () => {},
registerAutomationEventType: () => {},
}
await plugin.setup?.(api as never, {
workspaceInfo: { rootPath: "/tmp/fake-workspace" },
})
// Now `tools` contains the registered tools -- call tool.execute(input, ctx)
```
### End-to-End with runDemo()
Add a `runDemo()` in your plugin file (see the single-file template above) that boots a real `ClineCore` session:
```bash
ANTHROPIC_API_KEY=sk-... bun run my-plugin.ts
```
### CLI Smoke Test
```bash
mkdir -p .cline/plugins
cp my-plugin.ts .cline/plugins/
cline -i "trigger something that exercises the plugin"
```
For packages:
```bash
cline plugin install ./my-cline-plugin
cline -i "..."
```
If the plugin fails validation or setup, the CLI prints a clear error and continues without it.
## Common Gotchas
- "capabilities must be a non-empty array" -- you forgot `manifest.capabilities`, or it's `[]`.
- "registerRule requires the 'rules' capability" -- capability/handler drift. Add `"rules"` to capabilities, or stop calling `registerRule`.
- Tool not visible to the model -- check `enableTools: true` on the session config, and that you're declaring `"tools"` in capabilities.
- `ctx.workspaceInfo` is undefined in SDK tests -- the host didn't pass `extensionContext.workspace`. In SDK code, set it explicitly (see the ClineCore loading example above).
- State leaking across sessions -- module-level variables are shared across sessions in the same process. Key by `ctx.session?.sessionId` if your host runs multiple sessions concurrently.
- `afterRun` firing on aborts -- guard with `if (result.status !== "completed") return`.
- Heavy work in `setup()` -- `setup()` blocks session start. Defer expensive work into the first tool call or `beforeRun`.
- Importing host internals -- only import from `@cline/core`. Reaching into host-specific packages (e.g. CLI internals) will break in non-CLI hosts.
- Sandboxed plugins and `telemetry` -- telemetry is process-local. Feature-detect `ctx.telemetry` and expect it to be undefined in sandboxed plugin processes.
- Resolving bundled assets -- use `import.meta.url` + `fileURLToPath` to find files inside your package; never `process.cwd()`. For workspace paths, do the opposite: use `ctx.workspaceInfo?.rootPath`, never `import.meta.url`.
- Plugin name collisions -- `name` must be unique within a session. If two plugins share a name, validation fails. Namespace by package (`my-org-redactor`, not `redactor`).
## Decision Guide -- Which Extension Point?
| You want to... | Use |
|----------------|-----|
| Give the model a new capability | `registerTool` |
| Add a slash command in chat surfaces | `registerCommand` |
| Inject text into the system prompt | `registerRule` |
| Rewrite messages before they hit the provider | `registerMessageBuilder` |
| Add a custom model provider | `registerProvider` |
| Emit normalized cron/webhook events | `registerAutomationEventType` + `ctx.automation` |
| Observe or steer the agent loop | `hooks.*` |
| Block a dangerous tool call | `hooks.beforeTool` returning `{ stop: true }` |
| Notify on completion | `hooks.afterRun` (gate on `status === "completed"`) |
| Tweak each model request | `hooks.beforeModel` |
| Stream events to a UI | `hooks.onEvent` |
| Ship reusable templates with the plugin | Bundle assets next to `index.ts`, resolve via `import.meta.url` |
| Let users override defaults globally or per-project | Three-tier lookup: bundled / global / project |
## Pre-Ship Checklist
- `manifest.capabilities` is a non-empty array.
- Every `api.register*` call has a matching capability declared.
- If `hooks` is present, `"hooks"` is in `capabilities`.
- `ctx.workspaceInfo?.rootPath` is used for workspace paths (not `process.cwd()`).
- Optional `ctx` fields are feature-detected.
- Tool names are snake_case verbs; descriptions are written for the model.
- Tool inputs have JSON Schema with `required` set.
- `afterRun` handlers gate on `result.status === "completed"` if they only want successes.
- State that must not leak between concurrent sessions is keyed by `ctx.session?.sessionId`.
- (Package) `package.json` has `type: "module"`, `cline.plugins`, and `@cline/core` as an optional peer dep.
- (Package) Bundled assets resolved via `import.meta.url`, not `process.cwd()`.
- Smoke test: drop the plugin into `.cline/plugins/` (or `cline plugin install`), run `cline -i "..."`, watch it work.
## Plugin Examples from SDK
The SDK repo includes these example plugins:
| Plugin | Description |
|--------|-------------|
| `weather-metrics.ts` | Tool registration + lifecycle metrics |
| `mac-notify.ts` | macOS Notification Center alerts |
| `custom-compaction.ts` | Custom message compaction via message builders |
| `background-terminal.ts` | Detached shell job management |
| `automation-events.ts` | Plugin-emitted automation events |
| `gitignore-read-files-guard.ts` | File access policy enforcement via beforeTool |
| `web-search.ts` | Web search via Exa API |
| `typescript-lsp/` | TypeScript Language Service tools (plugin package) |
| `agents-squad/` | Multi-agent team orchestration (plugin package) |
## See Also
- `../tools/REFERENCE.md` - Tool creation
- `../events/REFERENCE.md` - Event system
- `../agent/REFERENCE.md` - Using plugins with Agent
- `../clinecore/REFERENCE.md` - Using plugins with ClineCore
@@ -1,253 +0,0 @@
# Going to Production
Guidelines for deploying Cline SDK agents in production environments.
## Error Handling
Always check the result status:
```typescript
const result = await agent.run(input)
switch (result.status) {
case "completed":
console.log("Success:", result.outputText)
break
case "aborted":
console.log("Cancelled:", result.error?.message)
break
case "failed":
console.error("Failed:", result.error)
break
}
```
For ClineCore, check `finishReason`:
```typescript
const session = await cline.start({ ... })
switch (session.result?.finishReason) {
case "completed":
// normal completion
break
case "max_iterations":
// agent hit iteration limit
break
case "aborted":
// manually cancelled
break
case "mistake_limit":
// too many tool errors
break
case "error":
// unrecoverable error
break
}
```
## Cost Control
### Token Limits
Set maximum tokens per turn and iteration limits:
```typescript
const agent = new Agent({
providerId: "anthropic",
modelId: "claude-sonnet-4-6",
maxTokensPerTurn: 4096,
maxIterations: 10,
tools: [...],
})
```
### Model Selection
Use cheaper models for simple tasks:
```typescript
// Simple classification or formatting
{ providerId: "anthropic", modelId: "claude-haiku-4-5" }
// Complex reasoning and code generation
{ providerId: "anthropic", modelId: "claude-sonnet-4-6" }
// Hardest tasks requiring deep reasoning
{ providerId: "anthropic", modelId: "claude-opus-4-7" }
```
### Usage Tracking
Monitor spending in real time:
```typescript
agent.subscribe((event) => {
if (event.type === "usage-updated" && event.usage.totalCost) {
if (event.usage.totalCost > MAX_BUDGET) {
agent.abort("Budget exceeded")
}
}
})
```
## Observability
### OpenTelemetry Integration
The SDK supports OpenTelemetry for traces, metrics, and logs:
```typescript
import { ClineCore } from "@cline/sdk"
const cline = await ClineCore.create({
clientName: "my-app",
// OpenTelemetry config is picked up from environment
// OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_SERVICE_NAME, etc.
})
```
### Structured Logging
Use the `BasicLogger` interface for injectable logging:
```typescript
import type { BasicLogger } from "@cline/sdk"
const logger: BasicLogger = {
debug: (msg, meta) => console.debug(msg, meta),
log: (msg, meta) => console.log(msg, meta),
error: (msg, meta) => console.error(msg, meta),
}
await cline.start({
config: {
logger,
// ...
},
})
```
### Custom Metrics via Plugins
```typescript
const metricsPlugin: AgentPlugin = {
name: "metrics",
manifest: { capabilities: ["hooks"] },
setup() {},
hooks: {
beforeRun() {
metrics.increment("agent.runs.started")
},
afterRun({ result }) {
metrics.increment("agent.runs.completed")
metrics.histogram("agent.iterations", result.iterations)
metrics.histogram("agent.tokens.output", result.usage.outputTokens)
},
beforeTool({ toolCall }) {
metrics.increment(`agent.tools.${toolCall.toolName}`)
},
},
}
```
## Security
### Sandbox Tool Execution
Validate tool inputs to prevent path traversal and injection:
```typescript
execute: async (input) => {
const safePath = path.resolve(WORKSPACE_ROOT, input.path)
if (!safePath.startsWith(WORKSPACE_ROOT)) {
return { error: "Path traversal attempt blocked" }
}
return await readFile(safePath, "utf-8")
}
```
### API Key Management
- Use environment variables, never hardcode keys
- Rotate keys regularly
- Use different keys for development and production
```typescript
{
providerId: "anthropic",
modelId: "claude-sonnet-4-6",
apiKey: process.env.ANTHROPIC_API_KEY, // never a literal string
}
```
### Tool Policy Hardening
Disable tools you don't need and require approval for dangerous ones:
```typescript
toolPolicies: {
read_files: { autoApprove: true },
search: { autoApprove: true },
bash: { autoApprove: false }, // require approval
editor: { autoApprove: false },
apply_patch: { autoApprove: false },
fetch_web: { enabled: false }, // disable entirely
}
```
## Deployment Patterns
### Stateless Worker
For request/response workloads (API endpoints, queue consumers):
```typescript
const cline = await ClineCore.create({
clientName: "worker",
backendMode: "local",
})
app.post("/agent", async (req, res) => {
const session = await cline.start({
prompt: req.body.prompt,
config: { ... },
})
res.json({ text: session.result?.text, usage: session.result?.usage })
})
```
### Persistent Service
For long-running services with session management:
```typescript
const cline = await ClineCore.create({
clientName: "service",
backendMode: "hub",
})
process.on("SIGTERM", async () => {
await cline.dispose("SIGTERM")
process.exit(0)
})
```
### Scheduled Automation
See `../scheduling/REFERENCE.md` for recurring agent tasks.
## Retry and Resilience
- Tool `execute` functions support `retryable: true` (default) and `maxRetries: 3` (default)
- Provider API calls are retried automatically on transient failures
- Use `timeoutMs` on tools to prevent hanging
- Monitor `mistake_limit` finish reason to detect systematic tool failures
## See Also
- `../agent/REFERENCE.md` - Agent overview
- `../clinecore/REFERENCE.md` - ClineCore overview
- `../tools/REFERENCE.md` - Tool configuration
- `../plugins/REFERENCE.md` - Metrics plugins
- `../scheduling/REFERENCE.md` - Scheduled agents
@@ -1,257 +0,0 @@
# Model Providers
The Cline SDK supports every major LLM provider out of the box via `@cline/llms`.
## Supported Providers
| Provider ID | Models |
|-------------|--------|
| `"anthropic"` | Claude Opus 4.7, Sonnet 4.6, Haiku 4.5 |
| `"openai"` | GPT-5.5, GPT-5.3 Codex |
| `"gemini"` | Gemini 3.1 Pro Preview, Gemini 3 Flash Preview |
| `"vertex"` | Google models via Vertex AI |
| `"bedrock"` | Claude, Llama via AWS Bedrock |
| `"mistral"` | Mistral Large, Codestral |
| `"openai-compatible"` | vLLM, Together, Fireworks, Groq, etc. |
## Basic Configuration
### With Agent
```typescript
import { Agent } from "@cline/sdk"
const agent = new Agent({
providerId: "anthropic",
modelId: "claude-sonnet-4-6",
apiKey: process.env.ANTHROPIC_API_KEY,
systemPrompt: "You are a helpful assistant.",
tools: [],
})
```
### With ClineCore
```typescript
import { ClineCore } from "@cline/sdk"
const cline = await ClineCore.create({ clientName: "my-app" })
await cline.start({
prompt: "Hello",
config: {
providerId: "anthropic",
modelId: "claude-sonnet-4-6",
apiKey: process.env.ANTHROPIC_API_KEY,
},
})
```
## Provider-Specific Configuration
### Anthropic
```typescript
{
providerId: "anthropic",
modelId: "claude-opus-4-7", // or "claude-sonnet-4-6", "claude-haiku-4-5"
apiKey: process.env.ANTHROPIC_API_KEY,
}
```
### OpenAI
```typescript
{
providerId: "openai",
modelId: "gpt-5.5",
apiKey: process.env.OPENAI_API_KEY,
}
```
### Google (Gemini)
```typescript
{
providerId: "gemini",
modelId: "gemini-3.1-pro-preview",
apiKey: process.env.GOOGLE_API_KEY,
}
```
### Google (Vertex AI)
```typescript
{
providerId: "vertex",
modelId: "gemini-3.1-pro-preview",
// Uses application default credentials or service account
}
```
### AWS Bedrock
```typescript
{
providerId: "bedrock",
modelId: "anthropic.claude-sonnet-4-6",
// Uses AWS credential chain (env vars, config file, IAM role)
// Set AWS_REGION, AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY
}
```
### Mistral
```typescript
{
providerId: "mistral",
modelId: "mistral-large-latest",
apiKey: process.env.MISTRAL_API_KEY,
}
```
### OpenAI-Compatible
For any provider with an OpenAI-compatible API:
```typescript
{
providerId: "openai-compatible",
modelId: "my-model",
apiKey: process.env.API_KEY,
baseUrl: "https://api.together.xyz/v1",
}
```
Works with: vLLM, Together AI, Fireworks, Groq, Ollama, LiteLLM, etc.
## Custom Base URL
Override the API endpoint for any provider:
```typescript
{
providerId: "anthropic",
modelId: "claude-sonnet-4-6",
apiKey: process.env.API_KEY,
baseUrl: "https://my-proxy.example.com/v1",
}
```
## Custom Headers
Pass additional headers to API requests:
```typescript
{
providerId: "openai",
modelId: "gpt-5.5",
apiKey: process.env.API_KEY,
headers: {
"X-Custom-Header": "value",
},
}
```
## Gateway API
For advanced multi-provider setups, use the Gateway directly:
```typescript
import { createGateway, DefaultGateway } from "@cline/llms"
const gateway = createGateway({
providerConfigs: [
{ providerId: "anthropic", apiKey: process.env.ANTHROPIC_API_KEY },
{ providerId: "openai", apiKey: process.env.OPENAI_API_KEY },
],
})
// Create a model for a specific provider
const model = gateway.createAgentModel({
providerId: "anthropic",
modelId: "claude-opus-4-7",
})
// Use with Agent
const agent = new Agent({ model, systemPrompt: "...", tools: [] })
```
### Gateway Methods
```typescript
gateway.registerProvider(registration) // add a custom provider
gateway.configureProvider(config) // update provider settings
gateway.listProviders() // list available providers
gateway.listModels(providerId?) // list available models
gateway.createAgentModel(selection) // create model for agent
gateway.stream(request) // raw streaming (AsyncIterable)
```
## Provider Registry
Query and register providers programmatically:
```typescript
import {
getAllProviders,
getProviderIds,
getProvider,
getModelsForProvider,
registerProvider,
registerModel,
createHandler,
} from "@cline/llms"
// List all registered providers
const providers = getAllProviders()
// Get models for a provider
const models = getModelsForProvider("anthropic")
// Register a custom provider
registerProvider({
id: "my-provider",
name: "My Custom Provider",
handler: createHandler({ ... }),
})
```
## Model Metadata
Access model info (context window, pricing, capabilities):
```typescript
import { getModelsForProvider } from "@cline/llms"
const models = getModelsForProvider("anthropic")
for (const model of models) {
console.log(`${model.id}: context=${model.contextWindow}, input=$${model.inputPrice}/MTok`)
}
```
## Cost Tracking
Track per-request and cumulative costs:
```typescript
// Via events
agent.subscribe((event) => {
if (event.type === "usage-updated") {
console.log(`Cost: $${event.usage.totalCost?.toFixed(4)}`)
}
})
// Via result
const result = await agent.run("...")
console.log(`Total cost: $${result.usage.totalCost?.toFixed(4)}`)
// Via ClineCore accumulated usage
const usage = await cline.getAccumulatedUsage(sessionId)
```
## See Also
- `../agent/REFERENCE.md` - Using providers with Agent
- `../clinecore/REFERENCE.md` - Using providers with ClineCore
- `../production/REFERENCE.md` - Cost control in production
@@ -1,227 +0,0 @@
# Scheduling and Automation
The Cline SDK supports scheduled, one-off, and event-driven agent execution through the automation subsystem in `@cline/core`.
## Overview
Three trigger types:
| Trigger | Description |
|---------|-------------|
| `schedule` | Recurring jobs via cron expressions |
| `one_off` | Single execution tasks |
| `event` | Triggered by external events (GitHub, Linear, custom) |
## CLI Schedule Management
```bash
# Create a recurring schedule
cline schedule create "Daily standup" \
--cron "0 9 * * MON-FRI" \
--prompt "Summarize open PRs and blockers" \
--workspace /path/to/project \
--model anthropic/claude-sonnet-4-6
# List schedules
cline schedule list
# Trigger a schedule immediately
cline schedule trigger <schedule-id>
# Pause/resume
cline schedule pause <schedule-id>
cline schedule resume <schedule-id>
# Delete
cline schedule delete <schedule-id>
# View past executions
cline schedule executions <schedule-id>
```
## Cron Expressions
| Expression | Meaning |
|-----------|---------|
| `0 9 * * MON-FRI` | 9 AM weekdays |
| `0 */6 * * *` | Every 6 hours |
| `0 8 * * MON` | Mondays at 8 AM |
| `*/30 * * * *` | Every 30 minutes |
| `0 0 1 * *` | First of every month |
## File-Based Specs
Create Markdown files in `~/.cline/cron/` (global) or `.cline/cron/` (workspace):
### Recurring Schedule
```markdown
---
trigger: schedule
schedule: "0 9 * * MON-FRI"
timezone: America/New_York
mode: exclusive
prompt: "Check for dependency updates and create PRs for any outdated packages."
modelSelection:
providerId: anthropic
modelId: claude-sonnet-4-6
tools:
enabled: true
---
Additional context or instructions for the agent go in the body.
```
### One-Off Task
```markdown
---
trigger: one_off
prompt: "Generate a comprehensive test coverage report."
modelSelection:
providerId: anthropic
modelId: claude-sonnet-4-6
---
```
### Event-Driven
```markdown
---
trigger: event
eventType: github.pull_request.opened
filters:
repository: myorg/myrepo
debounceMs: 5000
cooldownMs: 60000
prompt: "Review the PR for security issues and code quality."
modelSelection:
providerId: anthropic
modelId: claude-sonnet-4-6
---
```
## CronSpec Types
```typescript
interface CronScheduleSpec {
trigger: "schedule"
schedule: string // cron expression
timezone?: string
mode?: "exclusive" | "concurrent"
prompt: string
modelSelection?: { providerId: string; modelId?: string }
extensionLoading?: "isolated" | "direct"
configExtensions?: RuntimeConfigExtensionKind[]
tools?: { enabled?: boolean; names?: string[] }
}
interface CronOneOffSpec {
trigger: "one_off"
prompt: string
modelSelection?: { providerId: string; modelId?: string }
}
interface CronEventSpec {
trigger: "event"
eventType: string // e.g., "github.pull_request.opened"
filters?: Record<string, unknown>
debounceMs?: number
cooldownMs?: number
prompt: string
modelSelection?: { providerId: string; modelId?: string }
}
```
## Programmatic Automation API
```typescript
const cline = await ClineCore.create({
clientName: "my-app",
automation: true,
})
// Start automation service
cline.automation.start()
// Ingest an external event
cline.automation.ingestEvent({
eventId: "evt-123",
eventType: "github.pull_request.opened",
source: "github",
timestamp: Date.now(),
payload: { pr: { number: 42, title: "..." } },
})
// List specs, runs, events
const specs = await cline.automation.listSpecs()
const runs = await cline.automation.listRuns()
const events = await cline.automation.listEvents()
// Reconcile specs from directory
await cline.automation.reconcile(specDirectory)
// Stop automation
cline.automation.stop()
```
## Event Ingestion from Plugins
Plugins can declare and emit automation events:
```typescript
const webhookPlugin: AgentPlugin = {
name: "webhook-events",
manifest: { capabilities: ["automationEvents"] },
setup(api) {
api.registerAutomationEventType({
type: "webhook.received",
description: "External webhook received",
})
},
}
```
Submit events via the plugin context:
```typescript
ctx.automation.ingestEvent({
eventId: "evt-456",
eventType: "webhook.received",
source: "custom",
timestamp: Date.now(),
payload: { ... },
})
```
## Concurrency Control
| Mode | Behavior |
|------|----------|
| `"exclusive"` | Skip if previous run still active |
| `"concurrent"` | Allow overlapping runs |
## Run Reports
Each completed run writes a Markdown report to `.cline/cron/reports/<run-id>.md` with:
- Run metadata (spec, trigger, timing)
- Summary of agent output
- Usage (tokens, cost)
- Tool calls made
- Trigger event context (for event-driven runs)
## Use Cases
- Daily standup summaries
- Automated dependency update checks
- PR review on open
- Codebase health reports
- Scheduled security scans
- Event-driven CI/CD workflows
## See Also
- `../clinecore/REFERENCE.md` - ClineCore runtime
- `../clinecore/api.md` - Automation API details
- `../plugins/REFERENCE.md` - Plugin events
- `../production/REFERENCE.md` - Production deployment
@@ -1,259 +0,0 @@
# Tools
Tools are how agents interact with the world. The Cline SDK supports both built-in tools (via ClineCore) and custom tools you define yourself.
## Creating Custom Tools
Use `createTool()` from `@cline/sdk` (or `@cline/shared`):
```typescript
import { createTool } from "@cline/sdk"
const myTool = createTool({
name: "search_issues",
description: "Search GitHub issues by query. Returns up to 10 results.",
inputSchema: {
type: "object",
properties: {
query: { type: "string", description: "Search query" },
state: { type: "string", enum: ["open", "closed", "all"] },
},
required: ["query"],
},
execute: async (input) => {
const issues = await github.searchIssues(input.query, input.state)
return { issues, count: issues.length }
},
})
```
### With Zod Schema
```typescript
import { createTool } from "@cline/sdk"
import { z } from "zod"
const deployTool = createTool({
name: "deploy",
description: "Deploy the app to the specified environment.",
inputSchema: z.object({
environment: z.enum(["staging", "production"]).describe("Target environment"),
version: z.string().optional().describe("Version tag, defaults to latest"),
}),
execute: async (input) => {
const result = await deploy(input.environment, input.version)
return { url: result.url, status: "deployed" }
},
})
```
### Tool Config Options
```typescript
createTool({
name: string, // snake_case, unique per agent
description: string, // what the tool does (model reads this)
inputSchema: JSONSchema | ZodSchema, // input validation
execute: async (input, context, onChange?) => output,
timeoutMs?: number, // default: 30000
retryable?: boolean, // default: true
maxRetries?: number, // default: 3
lifecycle?: {
completesRun?: boolean // true = ends agent loop on success
},
})
```
### AgentToolContext
The second argument to `execute` provides runtime context:
```typescript
interface AgentToolContext {
agentId: string
conversationId: string
iteration: number
abortSignal?: AbortSignal
metadata?: Record<string, unknown>
}
```
## Tool Naming Rules
- Names must be `snake_case` (e.g., `search_issues`, `deploy_app`)
- Names must be unique within a single agent's tool set
- Choose descriptive names since the model uses them to decide which tool to call
## Tool Descriptions Matter
The model reads the tool description to decide when and how to use it. Write clear, specific descriptions:
```typescript
// Bad: vague
description: "Does deployment stuff"
// Good: specific with constraints
description: "Deploy the application to staging or production. " +
"Staging deployments are immediate. Production requires a passing CI build. " +
"Returns the deployment URL and status."
```
Include constraints, rate limits, and expected behavior in the description.
## Error Handling in Tools
Return errors as structured data instead of throwing:
```typescript
// Good: return error data
execute: async (input) => {
const file = await readFile(input.path).catch(() => null)
if (!file) {
return { error: "File not found", path: input.path }
}
return { content: file }
}
```
Thrown exceptions count as "mistakes" against the agent's mistake limit. Returned error data lets the agent adjust its approach.
## Completion Tools
Tools with `lifecycle: { completesRun: true }` end the agent loop when they execute successfully:
```typescript
const submitAnswer = createTool({
name: "submit_answer",
description: "Submit the final answer and end the task.",
inputSchema: z.object({
answer: z.string(),
confidence: z.number().min(0).max(1),
}),
lifecycle: { completesRun: true },
execute: async (input) => input,
})
```
The model sees the tool result and the run ends. Access the output via `result.toolCalls`.
## Built-in Tools (ClineCore Only)
When using `ClineCore` with `enableTools: true`, these tools are available automatically:
| Tool | Name | What It Does |
|------|------|-------------|
| Shell | `bash` | Execute shell commands in the session workspace |
| Editor | `editor` | Create and edit files |
| Read | `read_files` | Read file contents |
| Patch | `apply_patch` | Apply unified diffs to files |
| Search | `search` | Search file contents and directory structure |
| Web | `fetch_web` | Fetch web content via HTTP |
Built-in tools respect the `cwd` setting in `CoreSessionConfig`.
## Tool Policies
Control which tools are available and whether they require approval:
```typescript
// In Agent config
const agent = new Agent({
tools: [toolA, toolB, toolC],
toolPolicies: {
tool_a: { autoApprove: true }, // runs without asking
tool_b: { autoApprove: false }, // requires approval
tool_c: { enabled: false }, // hidden from model
},
})
// In ClineCore session
await cline.start({
prompt: "...",
config: { ... },
toolPolicies: {
bash: { autoApprove: true },
editor: { autoApprove: false },
},
})
```
### Policy Options
| Policy | Effect |
|--------|--------|
| `{ autoApprove: true }` | Tool runs without approval |
| `{ autoApprove: false }` | Triggers approval callback before running |
| `{ enabled: false }` | Tool is hidden from the model entirely |
| No policy set | Defaults to enabled and auto-approved |
## Abort Signal in Long-Running Tools
Respect the abort signal for tools that take a long time:
```typescript
execute: async (input, context) => {
const results = []
for (const item of input.items) {
if (context.abortSignal?.aborted) {
return { results, aborted: true, processed: results.length }
}
results.push(await processItem(item))
}
return { results, processed: results.length }
}
```
## Streaming Tool Output
Use the `onChange` callback (third argument) to stream partial results:
```typescript
execute: async (input, context, onChange) => {
let progress = 0
for (const step of steps) {
progress++
onChange?.(`Processing step ${progress}/${steps.length}...`)
await processStep(step)
}
return { completed: true }
}
```
## Testing Tools
Tools are plain async functions, so they're straightforward to test:
```typescript
import { describe, it, expect } from "vitest"
describe("deploy tool", () => {
it("deploys to staging", async () => {
const context = { agentId: "test", conversationId: "test", iteration: 1 }
const result = await deployTool.execute({ environment: "staging" }, context)
expect(result.status).toBe("deployed")
})
})
```
## MCP Tool Integration
ClineCore can connect to MCP (Model Context Protocol) servers for additional tools. Configure in `.cline/mcp-servers.json`:
```json
{
"servers": {
"my-server": {
"command": "node",
"args": ["./mcp-server.js"]
}
}
}
```
MCP tools appear alongside built-in and custom tools automatically.
## See Also
- `../agent/REFERENCE.md` - Using tools with Agent
- `../clinecore/REFERENCE.md` - Using tools with ClineCore
- `../plugins/REFERENCE.md` - Packaging tools as plugins
-211
View File
@@ -1,211 +0,0 @@
---
name: create-pull-request
description: Create a GitHub pull request following project conventions. Use when the user asks to create a PR, submit changes for review, or open a pull request. Handles commit analysis, branch management, PR template usage, and PR creation using the gh CLI tool.
---
# Create Pull Request
This skill guides you through creating a well-structured GitHub pull request that follows project conventions and best practices.
## Prerequisites Check
Before proceeding, verify the following:
### 1. Check if `gh` CLI is installed
```bash
gh --version
```
If not installed, inform the user:
> The GitHub CLI (`gh`) is required but not installed. Please install it:
> - macOS: `brew install gh`
> - Other: https://cli.github.com/
### 2. Check if authenticated with GitHub
```bash
gh auth status
```
If not authenticated, guide the user to run `gh auth login`.
### 3. Verify clean working directory
```bash
git status
```
If there are uncommitted changes, ask the user whether to:
- Commit them as part of this PR
- Stash them temporarily
- Discard them (with caution)
## Gather Context
### 1. Identify the current branch
```bash
git branch --show-current
```
Ensure you're not on `main` or `master`. If so, ask the user to create or switch to a feature branch.
### 2. Find the base branch
```bash
git remote show origin | grep "HEAD branch"
```
This is typically `main` or `master`.
### 3. Analyze recent commits relevant to this PR
```bash
git log origin/main..HEAD --oneline --no-decorate
```
Review these commits to understand:
- What changes are being introduced
- The scope of the PR (single feature/fix or multiple changes)
- Whether commits should be squashed or reorganized
### 4. Review the diff
```bash
git diff origin/main..HEAD --stat
```
This shows which files changed and helps identify the type of change.
## Information Gathering
Before creating the PR, you need the following information. Check if it can be inferred from:
- Commit messages
- Branch name (e.g., `fix/issue-123`, `feature/new-login`)
- Changed files and their content
If any critical information is missing, use `ask_followup_question` to ask the user:
### Required Information
1. **Related Issue Number**: Look for patterns like `#123`, `fixes #123`, or `closes #123` in commit messages
2. **Description**: What problem does this solve? Why were these changes made?
3. **Type of Change**: Bug fix, new feature, breaking change, refactor, cosmetic, documentation, or workflow
4. **Test Procedure**: How was this tested? What could break?
### Example clarifying question
If the issue number is not found:
> I couldn't find a related issue number in the commit messages or branch name. What GitHub issue does this PR address? (Enter the issue number, e.g., "123" or "N/A" for small fixes)
## Git Best Practices
Before creating the PR, consider these best practices:
### Commit Hygiene
1. **Atomic commits**: Each commit should represent a single logical change
2. **Clear commit messages**: Follow conventional commit format when possible
3. **No merge commits**: Prefer rebasing over merging to keep history clean
### Branch Management
1. **Rebase on latest main** (if needed):
```bash
git fetch origin
git rebase origin/main
```
2. **Squash if appropriate**: If there are many small "WIP" commits, consider interactive rebase:
```bash
git rebase -i origin/main
```
Only suggest this if commits appear messy and the user is comfortable with rebasing.
### Push Changes
Ensure all commits are pushed:
```bash
git push origin HEAD
```
If the branch was rebased, you may need:
```bash
git push origin HEAD --force-with-lease
```
## Create the Pull Request
**IMPORTANT**: Read and use the PR template at `.github/pull_request_template.md`. The PR body format must **strictly match** the template structure. Do not deviate from the template format.
When filling out the template:
- Replace `#XXXX` with the actual issue number, or keep as `#XXXX` if no issue exists (for small fixes)
- Fill in all sections with relevant information gathered from commits and context
- Mark the appropriate "Type of Change" checkbox(es)
- Complete the "Pre-flight Checklist" items that apply
### Create PR with gh CLI
**Use a temporary file for the PR body** to avoid shell escaping issues, newline problems, and other command-line flakiness:
1. Write the PR body to a temporary file:
```
/tmp/pr-body.md
```
2. Create the PR using the file:
```bash
gh pr create --title "PR_TITLE" --body-file /tmp/pr-body.md --base main
```
3. Clean up the temporary file:
```bash
rm /tmp/pr-body.md
```
For draft PRs:
```bash
gh pr create --title "PR_TITLE" --body-file /tmp/pr-body.md --base main --draft
```
**Why use a file?** Passing complex markdown with newlines, special characters, and checkboxes directly via `--body` is error-prone. The `--body-file` flag handles all content reliably.
## Post-Creation
After creating the PR:
1. **Display the PR URL** so the user can review it
2. **Remind about CI checks**: Tests and linting will run automatically
3. **Suggest next steps**:
- Add reviewers if needed: `gh pr edit --add-reviewer USERNAME`
- Add labels if needed: `gh pr edit --add-label "bug"`
## Error Handling
### Common Issues
1. **No commits ahead of main**: The branch has no changes to submit
- Ask if the user meant to work on a different branch
2. **Branch not pushed**: Remote doesn't have the branch
- Push the branch first: `git push -u origin HEAD`
3. **PR already exists**: A PR for this branch already exists
- Show the existing PR: `gh pr view`
- Ask if they want to update it instead
4. **Merge conflicts**: Branch conflicts with base
- Guide user through resolving conflicts or rebasing
## Summary Checklist
Before finalizing, ensure:
- [ ] `gh` CLI is installed and authenticated
- [ ] Working directory is clean
- [ ] All commits are pushed
- [ ] Branch is up-to-date with base branch
- [ ] Related issue number is identified, or placeholder is used
- [ ] PR description follows the template exactly
- [ ] Appropriate type of change is selected
- [ ] Pre-flight checklist items are addressed
+8
View File
@@ -0,0 +1,8 @@
# Changesets
Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works
with multi-package repos, or single-package repos to help you version and publish your code. You can
find the full documentation for it [in our repository](https://github.com/changesets/changesets)
We have a quick list of common questions to get you started engaging with this project in
[our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md)
+11
View File
@@ -0,0 +1,11 @@
{
"$schema": "https://unpkg.com/@changesets/config@3.0.5/schema.json",
"changelog": "@changesets/cli/changelog",
"commit": false,
"fixed": [],
"linked": [],
"access": "restricted",
"baseBranch": "main",
"updateInternalDependencies": "patch",
"ignore": []
}
+7
View File
@@ -0,0 +1,7 @@
---
"claude-dev": patch
---
Add endpoint configuration file support for on-premise deployments
Enterprise customers can now configure custom API endpoints by creating a `~/.cline/endpoints.json` file with custom URLs for `appBaseUrl`, `apiBaseUrl`, and `mcpBaseUrl`. When this file is present, Cline runs in on-premise mode with the custom endpoints.
@@ -0,0 +1,7 @@
---
"claude-dev": patch
---
fix: prevent infinite retry loops when replace_in_file fails repeatedly
Add safeguards to prevent the LLM from getting stuck in infinite retry loops when `replace_in_file` operations fail repeatedly. The system now detects repeated failures and provides better guidance to break out of retry cycles.
@@ -0,0 +1,7 @@
---
"claude-dev": patch
---
fix: skip diff error UI handling during streaming to prevent flickering
Suppress diff view error notifications while content is actively streaming to prevent visual flickering and improve user experience. Error handling is deferred until streaming completes.
+7
View File
@@ -0,0 +1,7 @@
---
"claude-dev": patch
---
fix(extract-text): strip notebook outputs to reduce context size
Strip notebook cell outputs when extracting text content from Jupyter notebooks, significantly reducing the amount of context sent to the LLM while preserving the essential code and markdown content.
+7
View File
@@ -0,0 +1,7 @@
---
"claude-dev": patch
---
fix: throttle diff view updates during streaming
Add throttling to diff view updates during content streaming to reduce UI flickering and improve performance. Updates are now batched at reasonable intervals instead of firing on every token received.
+5
View File
@@ -0,0 +1,5 @@
---
"claude-dev": minor
---
Disable PostHog telemetry, error tracking, and feature flags in self-hosted mode
-5
View File
@@ -1,5 +0,0 @@
---
"claude-dev": patch
---
fix: use correct base URL for Vertex AI global endpoint with Claude models
+5
View File
@@ -0,0 +1,5 @@
---
"claude-dev": patch
---
Remove deprecated zai-glm-4.6 model from Cerebras provider
+5
View File
@@ -0,0 +1,5 @@
---
"claude-dev": patch
---
Make Sonnet 4.5 the default Amazon Bedrock model id
+5
View File
@@ -0,0 +1,5 @@
---
"claude-dev": patch
---
Disable PostHog and build-time OpenTelemetry telemetry in self-hosted/on-premise mode. Enterprise customers running self-hosted deployments will no longer send any telemetry to Cline's collectors. Runtime environment OTEL and remote config OTEL remain available for enterprises to configure their own telemetry collection.
+26
View File
@@ -0,0 +1,26 @@
changesDir: .changes
unreleasedDir: unreleased
headerPath: header.tpl.md
changelogPath: CHANGELOG.md
versionExt: md
versionFormat: '## {{.Version}} - {{.Time.Format "2006-01-02"}}'
kindFormat: "### {{.Kind}}"
changeFormat: "* {{.Body}}"
kinds:
- label: Added
auto: minor
- label: Changed
auto: major
- label: Deprecated
auto: minor
- label: Removed
auto: major
- label: Fixed
auto: patch
- label: Security
auto: patch
newlines:
afterChangelogHeader: 1
beforeChangelogVersion: 1
endOfVersion: 1
envPrefix: CHANGIE_
-1
View File
@@ -1 +0,0 @@
../../.agents/skills/cline-sdk
+196
View File
@@ -0,0 +1,196 @@
---
name: create-pull-request
description: Create a GitHub pull request following project conventions. Use when the user asks to create a PR, submit changes for review, or open a pull request. Handles commit analysis, branch management, and PR creation using the gh CLI tool.
---
# Create Pull Request
This skill guides you through creating a well-structured GitHub pull request that follows project conventions and best practices.
## Prerequisites Check
Before proceeding, verify the following:
### 1. Check if `gh` CLI is installed
```bash
gh --version
```
If not installed, inform the user:
> The GitHub CLI (`gh`) is required but not installed. Please install it:
> - macOS: `brew install gh`
> - Other: https://cli.github.com/
### 2. Check if authenticated with GitHub
```bash
gh auth status
```
If not authenticated, guide the user to run `gh auth login`.
### 3. Verify clean working directory
```bash
git status
```
If there are uncommitted changes, ask the user whether to:
- Commit them as part of this PR
- Stash them temporarily
- Discard them (with caution)
## Gather Context
### 1. Identify the current branch
```bash
git branch --show-current
```
Ensure you're not on `main` or `master`. If so, ask the user to create or switch to a feature branch.
### 2. Find the base branch
```bash
git remote show origin | grep "HEAD branch"
```
This is typically `main` or `master`.
### 3. Analyze recent commits relevant to this PR
```bash
git log origin/main..HEAD --oneline --no-decorate
```
Review these commits to understand:
- What changes are being introduced
- The scope of the PR (single feature/fix or multiple changes)
- Whether commits should be squashed or reorganized
### 4. Review the diff
```bash
git diff origin/main..HEAD --stat
```
This shows which files changed and helps identify the type of change.
## Information Gathering
Before creating the PR, you need the following information. Check if it can be inferred from:
- Commit messages
- Branch name (e.g., `fix/issue-123`, `feature/new-login`)
- Changed files and their content
If any critical information is missing, use `ask_followup_question` to ask the user:
### Required Information
1. **Related Issue Number**: Look for patterns like `#123`, `fixes #123`, or `closes #123` in commit messages
2. **Description**: What problem does this solve? Why were these changes made?
3. **Type of Change**: Bug fix, new feature, breaking change, refactor, cosmetic, documentation, or workflow
4. **Test Procedure**: How was this tested? What could break?
### Example clarifying question
If the issue number is not found:
> I couldn't find a related issue number in the commit messages or branch name. What GitHub issue does this PR address? (Enter the issue number, e.g., "123" or "N/A" for small fixes)
## Git Best Practices
Before creating the PR, consider these best practices:
### Commit Hygiene
1. **Atomic commits**: Each commit should represent a single logical change
2. **Clear commit messages**: Follow conventional commit format when possible
3. **No merge commits**: Prefer rebasing over merging to keep history clean
### Branch Management
1. **Rebase on latest main** (if needed):
```bash
git fetch origin
git rebase origin/main
```
2. **Squash if appropriate**: If there are many small "WIP" commits, consider interactive rebase:
```bash
git rebase -i origin/main
```
Only suggest this if commits appear messy and the user is comfortable with rebasing.
### Push Changes
Ensure all commits are pushed:
```bash
git push origin HEAD
```
If the branch was rebased, you may need:
```bash
git push origin HEAD --force-with-lease
```
## Create the Pull Request
**IMPORTANT**: Read and use the PR template at `.github/pull_request_template.md`. The PR body format must **strictly match** the template structure. Do not deviate from the template format.
When filling out the template:
- Replace `#XXXX` with the actual issue number, or keep as `#XXXX` if no issue exists (for small fixes)
- Fill in all sections with relevant information gathered from commits and context
- Mark the appropriate "Type of Change" checkbox(es)
- Complete the "Pre-flight Checklist" items that apply
### Create PR with gh CLI
```bash
gh pr create --title "PR_TITLE" --body "PR_BODY" --base main
```
Alternatively, create as draft if the user wants review before marking ready:
```bash
gh pr create --title "PR_TITLE" --body "PR_BODY" --base main --draft
```
## Post-Creation
After creating the PR:
1. **Display the PR URL** so the user can review it
2. **Remind about CI checks**: Tests and linting will run automatically
3. **Suggest next steps**:
- Add reviewers if needed: `gh pr edit --add-reviewer USERNAME`
- Add labels if needed: `gh pr edit --add-label "bug"`
## Error Handling
### Common Issues
1. **No commits ahead of main**: The branch has no changes to submit
- Ask if the user meant to work on a different branch
2. **Branch not pushed**: Remote doesn't have the branch
- Push the branch first: `git push -u origin HEAD`
3. **PR already exists**: A PR for this branch already exists
- Show the existing PR: `gh pr view`
- Ask if they want to update it instead
4. **Merge conflicts**: Branch conflicts with base
- Guide user through resolving conflicts or rebasing
## Summary Checklist
Before finalizing, ensure:
- [ ] `gh` CLI is installed and authenticated
- [ ] Working directory is clean
- [ ] All commits are pushed
- [ ] Branch is up-to-date with base branch
- [ ] Related issue number is identified, or placeholder is used
- [ ] PR description follows the template exactly
- [ ] Appropriate type of change is selected
- [ ] Pre-flight checklist items are addressed
+33
View File
@@ -0,0 +1,33 @@
# CLI Development
The CLI lives in `cli-ts/` and uses React Ink for terminal UI.
- If needed, look at `cli-ts/src/constants/colors.ts` for re-used terminal colors, e.g. `COLORS.primaryBlue` highlight color (selections, spinners, success states).
- Never use `dimColor` with gray (e.g. `<Text color="gray" dimColor>`) - it's too hard to read. Use `color="gray"` for secondary text and normal foreground (no color) for primary text.
- When thinking about how to handle state or messages from core, look at webview for how it communicates with the vs code extension.
- When updating the webview, consider and suggest to the user to update the CLI TUI since we want to provide a similar experience to our terminal users as we do our vs code extension users.
## Adding New API Providers
When adding a new API provider to the extension, you must also update the CLI:
1. **Update `cli-ts/src/components/ModelPicker.tsx`**: Add the provider to the `providerModels` map so `getDefaultModelId()` returns the correct default model. Import the models and default ID from `@shared/api`:
```typescript
import { newProviderDefaultModelId, newProviderModels } from "@/shared/api"
export const providerModels = {
// ...existing providers
"new-provider": { models: newProviderModels, defaultId: newProviderDefaultModelId },
}
```
2. **Use `applyProviderConfig()` for auth flows**: When implementing OAuth or other auth flows for the provider, use the shared utility at `cli-ts/src/utils/provider-config.ts`:
```typescript
import { applyProviderConfig } from "../utils/provider-config"
// After successful auth:
await applyProviderConfig({ providerId: "new-provider", controller })
```
This handles setting provider, default model, API key mapping, state persistence, and rebuilding the API handler.
3. **Provider-specific auth**: If the provider uses OAuth (like `openai-codex`), add handling in `SettingsPanelContent.tsx`'s `handleProviderSelect` callback. See the existing Codex OAuth flow as a reference.
+1 -12
View File
@@ -14,7 +14,7 @@ This file is the secret sauce for working effectively in this codebase. It captu
## Miscellaneous
- This is a VS Code extension—check `package.json` for available scripts before trying to verify builds (e.g., `npm run compile`, not `npm run build`).
- When creating PRs, contributors should not create changelog-entry files. Maintainers handle release versioning and changelog curation during the release process.
- When creating PRs, if the change is user-facing and significant enough to warrant a changelog entry, run `npm run changeset` and create a patch changeset. Never create minor or major version bumps. Skip changesets for trivial fixes, internal refactors, or minor UI tweaks that users wouldn't notice.
- When adding new feature flags, see this PR as a reference https://github.com/cline/cline/pull/7566
- Additional instructions about making requests: @.clinerules/network.md
@@ -147,17 +147,6 @@ Required steps:
Common mistake: Adding only the return value without the `context.globalState.get()` call. This compiles but the value is always `undefined` on load.
Settings plumbing gotcha: if a key is user-toggleable from settings, wire both controller update paths:
- `src/core/controller/state/updateSettings.ts` for webview `updateSetting(...)`
- `src/core/controller/state/updateSettingsCli.ts` for CLI/ACP settings updates
Missing one path causes a toggle to appear to change in one surface while the backend state stays unchanged.
Webview toggle gotcha: settings changes must also round-trip back in state payloads.
- Add the field to `UpdateSettingsRequest` in `proto/cline/state.proto` (for webview update requests), then run `npm run protos`
- Include the key in `Controller.getStateToPostToWebview()` (`src/core/controller/index.ts`)
- Ensure `ExtensionState` and webview defaults include the key (`src/shared/ExtensionMessage.ts`, `webview-ui/src/context/ExtensionStateContext.tsx`)
If this round-trip wiring is missing, the backend value can update but the toggle in webview appears stuck or reverts.
## StateManager Cache vs Direct globalState Access
StateManager uses an in-memory cache populated during `StateManager.initialize(context)` in `common.ts`. For most state, use `controller.stateManager.setGlobalState()`/`getGlobalStateKey()`.
+19
View File
@@ -0,0 +1,19 @@
#!/usr/bin/env bash
echo "PostToolUse running inside local cline/.clinerules/hooks/ directory"
input=$(cat)
echo $input | jq .
for i in {1..5}; do
sleep 1
echo "$i"
done
cat <<EOF
{
"cancel": false,
"contextModification": "PostToolUse response from the local cline/.clinerules/hooks/ directory.",
"errorMessage": "PostToolUse hook custom errorMessage"
}
EOF
+19
View File
@@ -0,0 +1,19 @@
#!/usr/bin/env bash
echo "PreToolUse running inside local cline/.clinerules/hooks/ directory"
input=$(cat)
echo $input | jq .
for i in {1..5}; do
sleep 1
echo "$i"
done
cat <<EOF
{
"cancel": false,
"contextModification": "PreToolUse response from the local cline/.clinerules/hooks/ directory.",
"errorMessage": "PreToolUse hook custom errorMessage"
}
EOF
+19
View File
@@ -0,0 +1,19 @@
#!/usr/bin/env bash
echo "TaskCancel running inside local cline/.clinerules/hooks/ directory"
input=$(cat)
echo $input | jq .
for i in {1..5}; do
sleep 1
echo "$i"
done
cat <<EOF
{
"cancel": false,
"contextModification": "TaskCancel response from the local cline/.clinerules/hooks/ directory.",
"errorMessage": "TaskCancel hook custom errorMessage"
}
EOF
+19
View File
@@ -0,0 +1,19 @@
#!/usr/bin/env bash
echo "TaskResume running inside local cline/.clinerules/hooks/ directory"
input=$(cat)
echo $input | jq .
for i in {1..5}; do
sleep 1
echo "$i"
done
cat <<EOF
{
"cancel": false,
"contextModification": "TaskResume response from the local cline/.clinerules/hooks/ directory.",
"errorMessage": "TaskResume hook custom errorMessage"
}
EOF
+19
View File
@@ -0,0 +1,19 @@
#!/usr/bin/env bash
echo "TaskStart running inside local cline/.clinerules/hooks/ directory"
input=$(cat)
echo $input | jq .
for i in {1..5}; do
sleep 1
echo "$i"
done
cat <<EOF
{
"cancel": false,
"contextModification": "TaskStart response from the local cline/.clinerules/hooks/ directory.",
"errorMessage": "TaskStart hook custom errorMessage"
}
EOF
+19
View File
@@ -0,0 +1,19 @@
#!/usr/bin/env bash
echo "UserPromptSubmit running inside local cline/.clinerules/hooks/ directory"
input=$(cat)
echo $input | jq .
for i in {1..5}; do
sleep 1
echo "$i"
done
cat <<EOF
{
"cancel": false,
"contextModification": "UserPromptSubmit response from the local cline/.clinerules/hooks/ directory.",
"errorMessage": "UserPromptSubmit hook custom errorMessage"
}
EOF
-64
View File
@@ -1,64 +0,0 @@
# Storage Architecture
Global settings, secrets and workspace state are stored in **file-backed JSON stores** under `~/.cline/data/`. This is the shared storage layer used by VSCode, CLI, and JetBrains.
## Key Abstractions
### `StorageContext` (src/shared/storage/storage-context.ts)
The entry point. Created via `createStorageContext()` and passed to `StateManager.initialize()`. Contains three `ClineFileStorage` instances:
- `globalState``~/.cline/data/globalState.json`
- `secrets``~/.cline/data/secrets.json` (mode 0o600)
- `workspaceState``~/.cline/data/workspaces/<hash>/workspaceState.json`
### `ClineFileStorage` (src/shared/storage/ClineFileStorage.ts)
Synchronous JSON key-value store backed by a single file. Supports `get()`, `set()`, `setBatch()`, `delete()`. Writes are atomic (write-then-rename).
### `StateManager` (src/core/storage/StateManager.ts)
In-memory cache on top of `StorageContext`. All runtime reads hit the cache; writes update cache immediately and debounce-flush to disk.
## ⚠️ Do NOT Use VSCode's ExtensionContext for Storage
**Do not** read from or write to `context.globalState`, `context.workspaceState`, or `context.secrets` for persistent data. These are VSCode-specific and not available on CLI or JetBrains.
Instead, use:
```typescript
// Reading state
StateManager.get().getGlobalStateKey("myKey")
StateManager.get().getSecretKey("mySecretKey")
StateManager.get().getWorkspaceStateKey("myWsKey")
// Writing state
StateManager.get().setGlobalState("myKey", value)
StateManager.get().setSecret("mySecretKey", value)
StateManager.get().setWorkspaceState("myWsKey", value)
```
Remember that your data may be read by a different client than the one that wrote it. For example, a value written by Cline in JetBrains may be read by Cline CLI.
## VSCode Migration (src/hosts/vscode/vscode-to-file-migration.ts)
On VSCode startup, a migration copies data from VSCode's `ExtensionContext` storage into the file-backed stores. This runs in `src/common.ts` before `StateManager.initialize()`.
- **Sentinel**: `__vscodeMigrationVersion` key in global state and workspace state — prevents re-migration.
- **Merge strategy**: File store wins. Existing values are never overwritten.
- **Safe downgrade**: VSCode storage is NOT cleared, so older extension versions still work.
## Adding New Storage Keys
1. Add to `src/shared/storage/state-keys.ts` (see existing patterns)
2. Read/write via `StateManager` (NOT via `context.globalState`)
3. If adding a secret, add to `SecretKeys` array in `state-keys.ts`
## File Layout
```
~/.cline/
data/
globalState.json # Global settings & state
secrets.json # API keys (mode 0o600)
tasks/
taskHistory.json # Task history (separate file)
workspaces/
<hash>/
workspaceState.json # Per-workspace toggles
```
+1 -1
View File
@@ -19,7 +19,7 @@ Review and address all comments on the current branch's PR.
- Inline comments: `gh api repos/{owner}/{repo}/pulls/{pr_number}/comments`
- General comments: `gh pr view {pr_number} --json comments,reviews`
4. Present a summary of all comments with your recommendation for each (apply, skip, or respond). Ignore bot noise (release automation, CI status, etc.).
4. Present a summary of all comments with your recommendation for each (apply, skip, or respond). Ignore bot noise (changeset-bot, CI status, etc.).
5. **Wait for my approval** before proceeding.
+549
View File
@@ -0,0 +1,549 @@
The goal of this workflow is to take a changeset for a release of Cline, an autonomous coding agent extension that plugs right into your IDE, and write the updated announcement component, and the updated changelog.
For reference, here are some examples of how we converted previous changesets to announcement components / changelogs.
- 3.14
<changeset>
This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and publish to npm yourself or setup this action to publish automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.
Releases
claude-dev@3.14.0
Minor Changes
77c9863: create clinerules folder if its currently a file and creating new rule
0ffb7dd: disabling shift hint for now & improving tooltip behavior
79b76fd: Add support for custom model ID in AWS Bedrock provider, enabling use of Application Inference Profile.
eb6e481: Full support for LaTeX rendering
df37f29: Add support for custom API request timeout. Previously, timeouts were hardcoded to 30 seconds for providers like Ollama or 15 seconds for OpenRouter and Cline. Now users can set a custom timeout value in milliseconds through the settings interface.
e4d26be: allow cursorrules and windsurfrules
c5de50f: Fix Handle @withRetry() SyntaxError when running extension locally issue
61d2f42: enabled pricing calculation for gemini and vertex + more robust caching & cache tracking for gemini & vertex
aed152b: add truncation notice when truncating manually
2fe2405: Migrate Cline Tools Section to new docs
19cc8bc: Add a timeout setting for the terminal connection, allowing users to adjust this if they are having timeout issues
03d4410: Added copy button to code blocks.
c78fe23: addressed race condition in terminal command usage
91e222f: add checkpoints after more messages
14230e7: add newrule slash command
1c7d33a: Add remote config with posthog allowing for disabling new features until they're reading, making for a better developer experience.
4196c14: add cache ui for open router and cline provider
d97424f: showing expanded task by default
5294e78: Refactor to not pass a message for showing the MCP View from the servers modal
70cc437: Fix Windows path issue: Correct handling of import.meta.url to avoid leading slash in pathname
4b697d8: Migrate the addRemoteServer to protobus
Patch Changes
c63d9a1: updated drag and drop text to say "drop" instead of "drag"
459adf0: Add markdown copy to chat
74ec823: Minor UX improvement to drag and drop ux
b0961f4: Remove linear pull request action
e9ce384: searchCommits protobus migration
5802b68: createRuleFile protobus migration
df7f9fc: Add dependsOn to more blocks in the tasks.json
41ae732: Fix for git commit mentions in repos with no git commits
7e78445: Adding args to allow Cursor to open workspaces (for checkpoint testing/development)
bdfda6f: feat(bedrock): Introduce Amazon Nova Premier
65243ad: Introduce UI library for future UI development
4565e06: checkIsImageURL migrated to protobus
5a8e9d8: protobus migration for openImage
deeda6e: Lowering Gemini cache TTL time
db0b022: Adding UI to show openrouter balance next to provider
4650ffa: deleteRuleFile protobus migration
d4bd755: fix cost calculation
</changeset>
<changelog>
## [3.14.0]
- Add UI to show openrouter balance next to provider
- Add support for custom model ID in AWS Bedrock provider, enabling use of Application Inference Profile (Thanks @clicube!)
- Add more robust caching & cache tracking for gemini & vertex providers
- Add support for LaTeX rendering
- Add support for custom API request timeout. Timeouts were 15-30s, but can now be configured via settings for OpenRouter/Cline & Ollama (Thanks @WingsDrafterwork!)
- Add truncation notice when truncating manually
- Add a timeout setting for the terminal connection, allowing users to set a time to wait for terminal startup
- Add copy button to code blocks
- Add copy button to markdown blocks (Thanks @weshoke!)
- Add checkpoints to more messages
- Add slash command to create a new rules file (/newrule)
- Add cache ui for open router and cline provider
- Add Amazon Nova Premier model to Bedrock (Thanks @watany!)
- Add support for cursorrules and windsurfrules
- Add support for batch history deletion (Thanks @danix800!)
- Improve Drag & Drop experience
- Create clinerules folder creating new rule if it's needed
- Enable pricing calculation for gemini and vertex providers
- Refactor message handling to not show the MCP View of the server modal
- Migrate the addRemoteServer to protobus (Thanks @DaveFres!)
- Update task header to be expanded by default
- Update Gemini cache TTL time to 15 minutes
- Fix race condition in terminal command usage
- Fix to correctly handle `import.meta.url`, avoiding leading slash in pathname for Windows (Thanks @DaveFres!)
- Fix @withRetry() decoration syntax error when running extension locally (Thanks @DaveFres!)
- Fix for git commit mentions in repos with no git commits
- Fix cost calculation (Thanks @BarreiroT!)
</changelog>
<announcement-component>
const Announcement = ({ version, hideAnnouncement }: AnnouncementProps) => {
const minorVersion = version.split(".").slice(0, 2).join(".") // 2.0.0 -> 2.0
return (
<div style={containerStyle}>
<VSCodeButton appearance="icon" onClick={hideAnnouncement} style={closeIconStyle}>
<span className="codicon codicon-close"></span>
</VSCodeButton>
<h3 style={h3TitleStyle}>
🎉{" "}New in v{minorVersion}
</h3>
<ul style={ulStyle}>
<li>
<b>Gemini prompt caching:</b> Gemini and Vertex providers now support prompt caching and price tracking for
Gemini models.
</li>
<li>
<b>Copy Buttons:</b> Buttons were added to Markdown and Code blocks that allow you to copy their contents
easily.
</li>
<li>
<b>/newrule command:</b> New slash command to have cline write your .clinerules for you based on your
workflow.
</li>
<li>
<b>Drag and drop improvements:</b> Don't forget to hold shift while dragging files!
</li>
<li>Added more checkpoints across the task, allowing you to restore from more than just file changes.</li>
<li>Added support for rendering LaTeX in message responses. (Try asking Cline to show the quadratic formula)</li>
</ul>
<Accordion isCompact className="pl-0">
<AccordionItem
key="1"
aria-label="Previous Updates"
title="Previous Updates:"
classNames={{
trigger: "bg-transparent border-0 pl-0 pb-0 w-fit",
title: "font-bold text-(--vscode-foreground)",
indicator:
"text-(--vscode-foreground) mb-0.5 -rotate-180 data-[open=true]:-rotate-90 rtl:rotate-0 rtl:data-[open=true]:-rotate-90",
}}>
<ul style={ulStyle}>
<li>
<b>Global Cline Rules:</b> store multiple rules files in Documents/Cline/Rules to share between
projects.
</li>
<li>
<b>Cline Rules Popup:</b> New button in the chat area to view workspace and global cline rules files
to plug and play specific rules for the task
</li>
<li>
<b>Slash Commands:</b> Type <code>/</code> in chat to see the list of quick actions, like starting a
new task (more coming soon!)
</li>
<li>
<b>Edit Messages:</b> You can now edit a message you sent previously by clicking on it. Optionally
restore your project when the message was sent!
</li>
</ul>
</AccordionItem>
</Accordion>
{/*
// Leave this here for an example of how to structure the announcement
<ul style={{ margin: "0 0 8px", paddingLeft: "12px" }}>
<li>
OpenRouter now supports prompt caching! They also have much higher rate limits than other providers,
so I recommend trying them out.
<br />
{!apiConfiguration?.openRouterApiKey && (
<VSCodeButtonLink
href={getOpenRouterAuthUrl(vscodeUriScheme)}
style={{
transform: "scale(0.85)",
transformOrigin: "left center",
margin: "4px -30px 2px 0",
}}>
Get OpenRouter API Key
</VSCodeButtonLink>
)}
{apiConfiguration?.openRouterApiKey && apiConfiguration?.apiProvider !== "openrouter" && (
<VSCodeButton
onClick={() => {
vscode.postMessage({
type: "apiConfiguration",
apiConfiguration: { ...apiConfiguration, apiProvider: "openrouter" },
})
}}
style={{
transform: "scale(0.85)",
transformOrigin: "left center",
margin: "4px -30px 2px 0",
}}>
Switch to OpenRouter
</VSCodeButton>
)}
</li>
<li>
<b>Edit Cline's changes before accepting!</b> When he creates or edits 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 "<code>{"// rest of code here"}</code>" shenanigans)
</li>
<li>
New <code>search_files</code> tool that lets Cline perform regex searches in your project, letting
him refactor code, address TODOs and FIXMEs, remove dead code, and more!
</li>
<li>
When Cline runs commands, you can now type directly in the terminal (+ support for Python
environments)
</li>
</ul>*/}
<div style={hrStyle} />
<p style={linkContainerStyle}>
Join us on{" "}
<VSCodeLink style={linkStyle} href="https://x.com/cline">
X,
</VSCodeLink>{" "}
<VSCodeLink style={linkStyle} href="https://discord.gg/cline">
discord,
</VSCodeLink>{" "}
or{" "}
<VSCodeLink style={linkStyle} href="https://www.reddit.com/r/cline/">
r/cline
</VSCodeLink>
for more updates!
</p>
</div>
)
}
</announcement-component>
- 3.13
<changeset>
Minor Changes
2964388: Added copy button to MermaidBlock component
75143a7: Add the ability to fetch from global cline rules files
Patch Changes
a0252e7: convert inline style to tailwind css of file SettingsView.tsx
ab59bd9: Add stream options back to xai provider
7276f50: Icons to indicate an action is occuring outside of the users workspace
0b19ba6: update to NEW model
</changeset>
<changelog>
## [3.13.0]
- Add Cline rules popover under the chat field, allowing you to easily add, enable & disable workspace level or global rule files
- Add new slash command menu letting you type “/“ to do quick actions like creating new tasks
- Add ability to edit past messages, with options to restore your workspace back to that point
- Allow sending a message when selecting an option provided by the question or plan tool
- Add command to jump to Cline's chat input
- Add support for OpenAI o3 & 4o-mini (Thanks @PeterDaveHello and @arafatkatze!)
- Add baseURL option for Google Gemini provider (Thanks @owengo and @olivierhub!)
- Add support for Azure's DeepSeek model. (Thanks @yt3trees!)
- Add ability for models that support it to receive image responses from MCP servers (Thanks @rikaaa0928!)
- Improve search and replace diff editing by making it more flexible with models that fail to follow structured output instructions. (Thanks @chi-cat!)
- Add detection of Ctrl+C termination in terminal, improving output reading issues
- Fix issue where some commands with large output would cause UI to freeze
- Fix token usage tracking issues with vertex provider (Thanks @mzsima!)
- Fix issue with xAI reasoning content not being parsed (Thanks @mrubens!)
</changelog>
<announcement-component>
const Announcement = ({ version, hideAnnouncement }: AnnouncementProps) => {
const minorVersion = version.split(".").slice(0, 2).join(".") // 2.0.0 -> 2.0
return (
<div style={containerStyle}>
<VSCodeButton appearance="icon" onClick={hideAnnouncement} style={closeIconStyle}>
<span className="codicon codicon-close"></span>
</VSCodeButton>
<h3 style={h3TitleStyle}>
🎉{" "}New in v{minorVersion}
</h3>
<ul style={ulStyle}>
<li>
<b>Global Cline Rules:</b> store multiple rules files in Documents/Cline/Rules to share between projects.
</li>
<li>
<b>Cline Rules Popup:</b> New button in the chat area to view workspace and global cline rules files to plug
and play specific rules for the task
</li>
<li>
<b>Slash Commands:</b> Type <code>/</code> in chat to see the list of quick actions, like starting a new task
(more coming soon!)
</li>
<li>
<b>Edit Messages:</b> You can now edit a message you sent previously by clicking on it. Optionally restore
your project when the message was sent!
</li>
</ul>
<h4 style={{ margin: "5px 0 5px" }}>Previous Updates:</h4>
<ul style={ulStyle}>
<li>
<b>Model Favorites:</b> You can now mark your favorite models when using Cline & OpenRouter providers for
quick access!
</li>
<li>
<b>Faster Diff Editing:</b> Improved animation performance for large files, plus a new indicator in chat
showing the number of edits Cline makes.
</li>
<li>
<b>New Auto-Approve Options:</b> Turn off Cline's ability to read and edit files outside your workspace.
</li>
</ul>
{/*
// Leave this here for an example of how to structure the announcement
<ul style={{ margin: "0 0 8px", paddingLeft: "12px" }}>
<li>
OpenRouter now supports prompt caching! They also have much higher rate limits than other providers,
so I recommend trying them out.
<br />
{!apiConfiguration?.openRouterApiKey && (
<VSCodeButtonLink
href={getOpenRouterAuthUrl(vscodeUriScheme)}
style={{
transform: "scale(0.85)",
transformOrigin: "left center",
margin: "4px -30px 2px 0",
}}>
Get OpenRouter API Key
</VSCodeButtonLink>
)}
{apiConfiguration?.openRouterApiKey && apiConfiguration?.apiProvider !== "openrouter" && (
<VSCodeButton
onClick={() => {
vscode.postMessage({
type: "apiConfiguration",
apiConfiguration: { ...apiConfiguration, apiProvider: "openrouter" },
})
}}
style={{
transform: "scale(0.85)",
transformOrigin: "left center",
margin: "4px -30px 2px 0",
}}>
Switch to OpenRouter
</VSCodeButton>
)}
</li>
<li>
<b>Edit Cline's changes before accepting!</b> When he creates or edits 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 "<code>{"// rest of code here"}</code>" shenanigans)
</li>
<li>
New <code>search_files</code> tool that lets Cline perform regex searches in your project, letting
him refactor code, address TODOs and FIXMEs, remove dead code, and more!
</li>
<li>
When Cline runs commands, you can now type directly in the terminal (+ support for Python
environments)
</li>
</ul>*/}
<div style={hrStyle} />
<p style={linkContainerStyle}>
Join us on{" "}
<VSCodeLink style={linkStyle} href="https://x.com/cline">
X,
</VSCodeLink>{" "}
<VSCodeLink style={linkStyle} href="https://discord.gg/cline">
discord,
</VSCodeLink>{" "}
or{" "}
<VSCodeLink style={linkStyle} href="https://www.reddit.com/r/cline/">
r/cline
</VSCodeLink>
for more updates!
</p>
</div>
)
}
</announcement-component>
We have a changeset PR that automatically generated as new unreleased PRs are merged into main, the PR is always called "Changeset version bump" and the author is github-actions.
The Changeset PR description looks something like this:
<changeset-pr-description>
This PR was opened by the [Changesets release](https://github.com/changesets/action) GitHub action. When you're ready to do a release, you can merge this and publish to npm yourself or [setup this action to publish automatically](https://github.com/changesets/action#with-publishing). If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.
# Releases
## claude-dev@3.16.0
### Minor Changes
- c6e8b04: Recent task list is now collapsible, allowing users to hide their recent tasks (e.g. when sharing their screen).
- aabe4ae: Add detection for new users to display special components
- 6c18d51: adds global endpoint for vertex ai users
- 080ed7c: Add Tailwind CSS IntelliSense to the the recommended extensions list
- 5147e28: new workflow feature
### Patch Changes
- c0b3c69: fix eternal loading states when the last message is a checkpoint
- 570ece3: selectImages protos migration
- 8d8452e: askResponse protobus migration
- cd1ff2a: Finishing the migration of Vscode Advanced settings to Settings Webview
</changeset-pr-description>
The changeset pr is ALWAYS on the following branch: `changeset-release/main`.
I have the `gh` command line tool set up and authenticated, so you have everything you need.
The first step is to get the full diff from the changeset PR to look at the changes that were automatically made to the `CHANGELOG.md` file. By default it will automatically add a new section to the changelog.md file with the new version. The problem with the automatically generated section is that it just takes the text that the developers threw into their changeset files for each corresponding PR, and they can be pretty vague and bad. Additionally there's some stuff that is totally irrelevant for the end user, like minor refactoring changes. So I manually typically go in and update this section to be a proper changelog that will show up in our patchnotes. You can look at how the rest of the file is done because those are all good examples of us updating this to use good language for the end user. We usually put new features up top (and the most exciting flagship features at the very top), and then bug fixes/improvements at the bottom. Having some basic organization to the ordering of the bullet points by content is nice. But use common sense.
To handle this process effectively, do the following:
For each of the automatically generated bullet points in the Changelog.md, you should
1. Take the commit hash at the start of the bullet point, and use the `gh` command line tool find the PR that it was associated with.
2. Use the `gh` command to get the PR title/description/discussion to understand the context surrounding the PR.
3. Use the `gh` command line tool to get the full PR diff to fully understand the changes made in the code.
4. Synthesize that knowledge to determine (a) whether or not this change is relevant to end users and (b) what the text & ordering of the line should be.
5. Update the `CHANGELOG.md` accordingly
Do this for every single item in the list from the autogenerated bullet points. We want to be diligent and have a full understanding of every feature so we can make the best changelog ever!
Here are some principles for good changelogs from keepchangelog.com, a handy guide:
<keepachangelog-pinciples-for-good-changelogs>
### Guiding Principles
- Changelogs are for humans, not machines.
- There should be an entry for every single version.
- The same types of changes should be grouped.
- The latest version comes first.
### Bullet points in the changelog should follow these principles:
- Types of changes
- Added for new features.
- Changed for changes in existing functionality.
- Deprecated for soon-to-be removed features.
- Removed for now removed features.
- Fixed for any bug fixes.
- Security in case of vulnerabilities.
</keepachangelog-pinciples-for-good-changelogs>
Lastly, when developers make a PR, they typically make a changeset. And they have 3 options when making the changeset:
1. Patch
2. Minor
3. Major
Sometimes they label something as minor when really it should just be a patch. Or vice versa. Because of this, the automatic version bump may be incorrect. So when starting out this workflow, you should use the <ask_followup_question> tool to confirm with me whether or not this should be a patch bump (show the old version number and what the proposed new version number would be) or a minor bump. Part of the release process is making sure the version in package.json that is automatically changed actually corresponds with what we decided the bump should actually be based on the features. ALL these modifications happen in the `changeset-release/main` branch btw.
<important_note>
Before doing any of this, make sure you check out the `changeset-release/main` and pull the most recent up to date changes. Then perform all this work in that branch.
New announcement banners should ONLY be made for minor version bumps or higher. That's another reason why double checking if the changelog warrants the bump is important.
Also, SUPER important: For any external contributors that aren't part of the cline github organization, we always want to add a (Thanks @username!) at the end of the changelog to attribute them properly. We're an open source project and it's ethical to do this.
</important_note>
Once the changelog looks good, and the version number looks good, we gotta double check that the version number in the changelog has the brackets around it. And as a final step, double check the package.json version number matches the latest number in the changelog. And as the ultimate final step we run `npm run install:all` to make sure the package version number permiates through the lock file.
<detailed_sequence_of_steps>
# Cline Release Process - Detailed Sequence of Steps
## Before Starting
1. First, examine the changeset PR without checking it out:
```bash
gh pr view changeset-release/main
```
2. View the PR diff to see the auto-generated CHANGELOG.md changes:
```bash
gh pr diff changeset-release/main > changeset-diff.txt
cat changeset-diff.txt | grep -A 50 "CHANGELOG.md"
```
## Initial Setup
3. Once you're ready to start, checkout and update the changeset release branch:
```bash
git checkout changeset-release/main
git pull origin changeset-release/main
```
## Analyzing Each Change
4. For each commit hash in the auto-generated changelog entries:
a. Find the PR number associated with a commit hash:
```bash
gh pr list --search "<commit-hash>" --state merged
```
b. Get PR details for better context:
```bash
gh pr view <PR-number>
```
c. Check if the contributor is external to determine if attribution is needed:
```bash
# Extract username from PR
USERNAME=$(gh pr view <PR-number> --json author --jq .author.login)
# Check if user is a member of the Cline organization
# this command is a bit finnicky, but it 100% works.
# if you see a `Error executing command: The command ran successfully, but we couldn't capture its output. Please proceed accordingly.` error, just retry it until you actually get the output
# don't make any assumptions, just retry the command to actually get the output and determine if they're external or not.
# no output means they are an external contributor, otherwise if there is output they are an internal contributor (part of our github org)
gh api "orgs/cline/members" --jq "map(.login)" | grep -i "pashpashpash"
```
d. View the full PR diff to understand code changes:
```bash
gh pr diff <PR-number> > pr-diff-<PR-number>.txt
cat pr-diff-<PR-number>.txt
```
## Updating the Changelog
5. Based on PR analysis, update the CHANGELOG.md with user-friendly descriptions:
- Use the `<replace_in_file>` tool to edit the CHANGELOG.md file
- Group by feature type (Added, Changed, Fixed)
- Put most exciting features at the top
- Move bug fixes and small improvements to the bottom
- Use clear, end-user focused language
- For external contributors, add attribution at the end of the relevant entry: `(Thanks @username!)`
## Version Number Verification
6. Confirm the version bump is appropriate:
- Check package.json to verify the auto-generated version number:
```bash
cat package.json | grep "\"version\""
```
- If the feature set doesn't warrant a minor bump, use the `<replace_in_file>` tool to modify package.json
7. Ensure the version in CHANGELOG.md has brackets around it:
```
## [3.16.0]
```
## Creating the Announcement (for minor/major versions only)
8. If this is a minor version bump, create/update the announcement component:
- Use the `<replace_in_file>` tool to edit the src/views/components/announcement.tsx file
- Update the highlights based on key features
- Move previous version highlights to the "Previous Updates" section
- Use the previous announcement components as reference for structure
## Finalizing the Release
9. Update dependencies with the new version number:
```bash
npm run install:all
```
10. Commit your changes:
```bash
git add CHANGELOG.md package.json package-lock.json src/views/components/announcement.tsx
git commit -m "Update CHANGELOG.md and announcement for version 3.16.0"
```
11. Push your changes to the changeset branch:
```bash
git push origin changeset-release/main
```
12. Check that your changes pushed successfully:
```bash
git status
```
</detailed_sequence_of_steps>
+11 -4
View File
@@ -89,9 +89,16 @@ On the main branch, create a commit that updates:
2. **package.json** - Update the version field to the new version
3. No changelog-entry file cleanup is needed. Contributors do not create changelog-entry files in this repo.
3. **Delete changesets** for the commits being included in the hotfix. This prevents the changeset bot from including duplicate entries in the next regular release.
**Skip running `npm run install:all`** - release automation handles lockfile consistency as needed.
Find and delete the changeset files associated with the selected commits:
```bash
ls .changeset/
```
Each changeset file in `.changeset/` corresponds to a PR. Read them to identify which ones belong to the commits you're hotfixing, then delete those files.
**Skip running `npm run install:all`** - the automation handles outdated lockfiles.
Commit with message format: `v{VERSION} Release Notes (hotfix)`
@@ -100,7 +107,7 @@ In the commit body, mention:
- List the cherry-picked commits that will be included
```bash
git add CHANGELOG.md package.json
git add CHANGELOG.md package.json .changeset/
git commit -m "v3.40.1 Release Notes (hotfix)
Hotfix release including:
@@ -176,7 +183,7 @@ Present a final summary:
- Slack message copied to clipboard: yes
Remind the user to:
1. Manually trigger the publish release GitHub Action at: https://github.com/cline/cline/actions/workflows/ext-vscode-publish-stable.yml (paste `v{VERSION}` as the tag)
1. Manually trigger the publish release GitHub Action at: https://github.com/cline/cline/actions/workflows/publish.yml (paste `v{VERSION}` as the tag)
2. Post the Slack message to announce the hotfix
## Important Notes
+2
View File
@@ -347,6 +347,8 @@ A few notes:
So until the settings page is update, and this is added to settings in a way that's clean and doesn't confuse new users, I don't think we can merge this. Please bear with us.
</request_changes_comment>
<request_changes_comment>
Also, don't forget to add a changeset since this fixes a user-facing bug.
The architectural change is solid - moving the focus logic to the command handlers makes sense. Just don't want to introduce subtle timing issues by removing those timeouts.
</request_changes_comment>
</example_comments_that_i_have_written_before>
+208 -40
View File
@@ -1,64 +1,232 @@
# Release
Prepare and publish a release directly from `main`.
Prepare and publish a release from the open changeset PR.
## Overview
This workflow helps you:
1. Select/confirm the target version
2. Curate `CHANGELOG.md` entries manually for end users
3. Ensure `package.json` version matches the changelog
4. Create and push a release commit + tag
5. Trigger publish workflow
6. Update GitHub release notes and share a summary
1. Find and checkout the open changeset PR
2. Clean up the changelog (fix version format, wordsmith entries)
3. Push changes back to the PR branch
4. Merge with proper commit message format
5. Tag and push the release (after verifying the commit)
6. Trigger the publish workflow
7. Update GitHub release notes
8. Provide final summary with Slack announcement
## Process
## Step 1: Find the Changeset PR
### 1) Sync and determine version
Look for the open changeset PR:
```bash
gh pr list --search "Changeset version bump" --state open --json number,title,headRefName,url
```
If no PR is found, inform the user there's no changeset PR ready. They may need to:
- Merge PRs with changesets to main first
- Manually trigger the Changeset Converter workflow at: https://github.com/cline/cline/actions/workflows/changeset-converter.yml
## Step 2: Gather PR Information
Get the PR details:
```bash
PR_NUMBER=<number from step 1>
gh pr view $PR_NUMBER --json body,files,headRefName
```
Checkout the PR branch:
```bash
git fetch origin changeset-release/main
git checkout changeset-release/main
```
If the branch has diverged from remote, reset to the remote version:
```bash
git reset --hard origin/changeset-release/main
```
## Step 3: Analyze the Changes
Read the current CHANGELOG.md to see what the automation generated:
```bash
head -50 CHANGELOG.md
```
Get the version from package.json:
```bash
cat package.json | grep '"version"'
```
**Present to the user:**
- The version number that will be released
- The raw changelog entries from the changeset PR
- Whether this is a patch, minor, or major release
## Step 4: Clean Up the Changelog
The changelog needs these fixes:
1. **Add brackets to version number**: Change `## 3.44.1` to `## [3.44.1]`
2. **No category headers**: Don't use `### Added`, `### Fixed`, etc. Just a flat list of bullet points.
3. **Order entries from most important to least important**:
- Lead with major new features or significant fixes users care about
- End with minor fixes or internal changes
4. **Write user-friendly descriptions**:
- This is for end users, not developers—explain what changed in plain language
- Remove commit hashes from the beginning of lines (the automation adds these)
- Look at the actual commit diffs (`git show <hash>`) and PRs to understand what changed
- Write colorful descriptions that explain the value and impact, not just technical details
- Consolidate related changes into single entries when appropriate
**Ask the user** to review the proposed changelog changes before applying them. Show them:
- Current (raw) changelog section
- Proposed (cleaned) changelog section
Once approved, apply the changes to CHANGELOG.md.
## Step 5: Commit and Push Changes
After making changelog edits:
```bash
git add CHANGELOG.md
git commit -m "Clean up changelog formatting"
git push origin changeset-release/main
```
## Step 6: Merge the PR
**Ask the user to confirm** they're ready to merge.
Merge the PR with the proper commit message format:
```bash
VERSION=<version from package.json>
gh pr merge $PR_NUMBER --squash --subject "v${VERSION} Release Notes" --body ""
```
**If merge is blocked by branch protection:**
- Users with admin privileges can add the `--admin` flag to bypass
- Users without admin privileges need to get the PR approved through normal review first before merging
## Step 7: Tag the Release
After the merge completes, checkout main and pull:
```bash
git checkout main
git pull origin main
cat package.json | grep '"version"'
```
Confirm the release version with the maintainer (patch/minor/major).
### 2) Curate changelog and version
- Edit `CHANGELOG.md` for the target version using human-friendly release notes.
- Ensure version headers use bracket format, e.g. `## [3.66.1]`.
- Update `package.json` version to the same value.
### 3) Commit and tag
**IMPORTANT: Verify the latest commit is the release commit before tagging:**
```bash
git add CHANGELOG.md package.json package-lock.json
git commit -m "v<version> Release Notes"
git push origin main
git tag v<version>
git push origin v<version>
git log -1 --oneline
```
### 4) Trigger publish workflow
Confirm the commit message matches `v{VERSION} Release Notes` (e.g., `v3.44.1 Release Notes`). Do NOT blindly tag HEAD without verification.
Tell the maintainer to run:
https://github.com/cline/cline/actions/workflows/ext-vscode-publish-stable.yml
Use `v<version>` as the release tag.
### 5) Update GitHub release notes
After publish completes:
Once verified, tag and push:
```bash
gh release view v<version> --json body --jq '.body'
gh release edit v<version> --notes "<final curated release notes>"
VERSION=<version>
git tag v${VERSION}
git push origin v${VERSION}
```
### 6) Final summary
## Step 8: Trigger Publish Workflow
Provide:
- Released version/tag
- Link to release page
- Summary of top end-user changes
**Copy the tag to clipboard** so the user can easily paste it into the GitHub Actions workflow:
```bash
echo -n "v{VERSION}" | pbcopy
```
**Tell the user to trigger the publish workflow:**
1. Go to: https://github.com/cline/cline/actions/workflows/publish.yml
2. Select **"release"** for release-type
3. Paste **`v{VERSION}`** as the tag (already in clipboard)
**Wait for the user** to confirm the publish workflow has completed before proceeding.
## Step 9: Update GitHub Release Notes
Once the user confirms the publish workflow is done, fetch the auto-generated release content:
```bash
VERSION=<version>
gh release view v${VERSION} --json body --jq '.body'
```
The auto-generated release has:
- `## What's Changed` - PR list (we'll replace this with our changelog)
- `## New Contributors` - First-time contributors (keep this if present)
- `**Full Changelog**` - Comparison link (keep this)
Build the new release body:
1. Start with `## What's Changed` header
2. Add our changelog content (from CHANGELOG.md for this version)
3. Keep the `## New Contributors` section if it exists
4. Keep the `**Full Changelog**` link
Update the release:
```bash
gh release edit v${VERSION} --notes "<new body content>"
```
Verify the release was updated:
```bash
gh release view v${VERSION}
```
## Step 10: Final Summary
**Copy a Slack announcement message to clipboard** (include the full changelog, not just highlights):
```bash
echo "VS Code v{VERSION} Released
- Changelog entry 1
- Changelog entry 2
- Changelog entry 3" | pbcopy
```
**Present a final summary:**
- Version released: v{VERSION}
- PR merged: #{PR_NUMBER}
- Tag pushed: v{VERSION}
- Release: https://github.com/cline/cline/releases/tag/v{VERSION}
- Slack message copied to clipboard
**Final reminder:**
Post the Slack message to announce the release
## Handling Edge Cases
### No changesets found
If the changeset PR body shows no changes, inform the user they need to merge PRs with changesets first.
### Merge conflicts
If there are conflicts on the changeset branch, help the user resolve them:
```bash
git fetch origin main
git rebase origin/main
# resolve conflicts
git push origin changeset-release/main --force-with-lease
```
### User wants to add more changes
If the user wants to include additional PRs before releasing:
1. Ask them to merge those PRs to main first
2. The changeset automation will update the PR automatically
3. Re-run this workflow after the PR is updated
-50
View File
@@ -1,50 +0,0 @@
# THIS IS AUTOGENERATED. DO NOT EDIT MANUALLY
version = 1
name = "cline"
[setup]
script = '''
if [ ! -d "node_modules" ]; then
MAIN_WORKTREE="$(git worktree list | head -n1 | awk '{print $1}')"
ln -s "$MAIN_WORKTREE/node_modules" node_modules
ln -s "$MAIN_WORKTREE/webview-ui/node_modules" webview-ui/node_modules
fi
'''
[[actions]]
name = "VS Code"
icon = "run"
command = "chmod +x ./scripts/run-extension-host.sh && ./scripts/run-extension-host.sh production"
[[actions]]
name = "CLI"
icon = "run"
command = '''
cd sdk
bun install
bun run cli
'''
[[actions]]
name = "npm install"
icon = "tool"
command = '''
rm node_modules
rm webview-ui/node_modules
npm run install:all
'''
[[actions]]
name = "pull main"
icon = "tool"
command = '''
git fetch origin main
if ! git merge-base --is-ancestor main origin/main; then
echo "Local main has commits not on origin/main. Aborting..."
exit 1
fi
git update-ref refs/heads/main refs/remotes/origin/main
echo "main updated to $(git rev-parse --short main)"
'''
@@ -16,6 +16,13 @@
TELEMETRY_SERVICE_API_KEY=your-posthog-telemetry-api-key
ERROR_SERVICE_API_KEY=your-posthog-error-tracking-api-key
# ============================================================================
# TELEMETRY PROVIDER CONTROL
# ============================================================================
# Control which telemetry providers are active
POSTHOG_TELEMETRY_ENABLED=true # Enable PostHog telemetry (default: true)
# Set to false to disable Telemetry completely
# ============================================================================
# OPENTELEMETRY (Optional - for advanced telemetry)
# ============================================================================
+3 -2
View File
@@ -1,2 +1,3 @@
/.github/ @saoudrizwan @arafatkatze @maxpaulus43 @dominiccooney
/README.md @saoudrizwan @juanpflores
/docs/
/.github/ @saoudrizwan @garoth @sjf
/README.md @saoudrizwan @nickbaumann98
-6
View File
@@ -26,12 +26,6 @@ body:
placeholder: 'e.g., 1.2.3'
validations:
required: true
- type: checkboxes
id: beta
attributes:
label: Beta version
options:
- label: I am using a beta version of Cline
- type: textarea
id: what-happened
attributes:
-57
View File
@@ -1,57 +0,0 @@
# Copilot Instructions for Cline
This is a VS Code extension. Read `.clinerules/general.md` for tribal knowledge and nuanced patterns.
## Architecture
- **Core** (`src/`): `extension.ts``WebviewProvider``Controller` (single source of truth) → `Task` (agent loop).
- **Webview** (`webview-ui/`): React/Vite app. State via `ExtensionStateContext.tsx`, synced through message passing.
- **Communication**: Protobuf-defined gRPC-like protocol over VS Code message passing. Schemas in `proto/`.
- **MCP**: `src/services/mcp/McpHub.ts`.
## Build & Test (Critical — non-obvious commands)
- **Build**: `npm run compile` — NOT `npm run build`.
- **Watch**: `npm run watch` (extension + webview).
- **Protos**: `npm run protos` — run **immediately** after any `.proto` change. Generates into `src/shared/proto/`, `src/generated/`.
- **Tests**: `npm run test:unit`. After prompt/tool changes: `UPDATE_SNAPSHOTS=true npm run test:unit`.
## Protobuf RPC Workflow (4 steps)
1. **Define** in `proto/cline/*.proto`. Naming: `PascalCaseService`, `camelCase` RPCs, `PascalCase` Messages. Use `common.proto` shared types for simple data.
2. **Generate**: `npm run protos`.
3. **Backend handler**: `src/core/controller/<domain>/`.
4. **Frontend call**: `UiServiceClient.myMethod(Request.create({...}))`.
- Adding enums (e.g. `ClineSay`) → also update `src/shared/proto-conversions/cline-message.ts`.
## Adding API Providers (silent failure risk)
Three proto conversion updates are **required** or the provider silently resets to Anthropic:
1. `proto/cline/models.proto` — add to `ApiProvider` enum.
2. `convertApiProviderToProto()` in `src/shared/proto-conversions/models/api-configuration-conversion.ts`.
3. `convertProtoToApiProvider()` in the same file.
Also update: `src/shared/api.ts`, `src/shared/providers/providers.json`, `src/core/api/index.ts`, `webview-ui/.../providerUtils.ts`, `webview-ui/.../validate.ts`, `webview-ui/.../ApiOptions.tsx`.
For Responses API providers: add to `isNextGenModelProvider()` in `src/utils/model-utils.ts` and set `apiFormat: ApiFormat.OPENAI_RESPONSES` on models.
## Adding Tools to System Prompt (5+ file chain)
1. Add enum to `ClineDefaultTool` in `src/shared/tools.ts`.
2. Create definition in `src/core/prompts/system-prompt/tools/` (export `[GENERIC]` minimum).
3. Register in `src/core/prompts/system-prompt/tools/init.ts`.
4. Whitelist in `src/core/prompts/system-prompt/variants/*/config.ts` for each model family.
5. Handler in `src/core/task/tools/handlers/`, wire in `ToolExecutor.ts`.
6. If tool has UI: add `ClineSay` enum in proto → `ExtensionMessage.ts``cline-message.ts``ChatRow.tsx`.
7. Regenerate snapshots: `UPDATE_SNAPSHOTS=true npm run test:unit`.
## Modifying System Prompt
Modular: `components/` (shared) + `variants/` (model-specific) + `templates/` (`{{PLACEHOLDER}}`). Variants override components via `componentOverrides` in `config.ts` or custom `template.ts`. XS variant is heavily condensed inline. Always regenerate snapshots after changes.
## Global State Keys (silent failure risk)
Adding a key requires: type in `src/shared/storage/state-keys.ts`, read via `context.globalState.get()` in `src/core/storage/utils/state-helpers.ts` `readGlobalStateFromDisk()`, and add to return object. Missing the `.get()` call compiles fine but value is always `undefined`.
## Slash Commands (3 places)
- `src/core/slash-commands/index.ts` — definitions.
- `src/core/prompts/commands.ts` — system prompt integration.
- `webview-ui/src/utils/slash-commands.ts` — webview autocomplete.
## Conventions
- **Paths**: Always use `src/utils/path` helpers (`toPosixString`) for cross-platform compatibility.
- **Logging**: `src/shared/services/Logger.ts`.
- **Feature flags**: See PR #7566 as reference pattern.
+2 -2
View File
@@ -2,7 +2,7 @@ version: 2
updates:
# Main extension dependencies
- package-ecosystem: "npm"
directory: "/apps/vscode"
directory: "/"
schedule:
interval: "weekly"
# Group all updates into a single PR
@@ -20,7 +20,7 @@ updates:
# Webview UI dependencies
- package-ecosystem: "npm"
directory: "/apps/vscode/webview-ui"
directory: "/webview-ui"
schedule:
interval: "weekly"
groups:
+1
View File
@@ -60,6 +60,7 @@ We're not looking for exhaustive documentation - just evidence that you've thoug
- [ ] 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,79 @@
"""
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
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"
unformmatted_prev_version_pattern = f"## {PREV_VERSION}\n"
prev_version_pattern = f"## [{PREV_VERSION}]\n"
print(f"latest version: {VERSION}")
print(f"prev_version: {PREV_VERSION}")
notes_start_index = changelog_text.find(version_pattern) + 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 or unformmatted_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")
filtered_lines = []
for line in changeset_lines:
# If the previous line is a changeset format
if len(filtered_lines) > 1 and filtered_lines[-1].startswith("### "):
# Remove the last two lines from the filted_lines
filtered_lines.pop()
filtered_lines.pop()
else:
filtered_lines.append(line.strip())
# Prepend a new line to the first line of filtered_lines
if filtered_lines:
filtered_lines[0] = "\n" + filtered_lines[0]
# Print filted_lines wiht a "\n" at the end of each line
for line in filtered_lines:
print(line.strip())
parsed_lines = "\n".join(line for line in filtered_lines)
updated_changelog = changelog_text[:notes_start_index] + parsed_lines + changelog_text[notes_end_index:]
return updated_changelog
with open(CHANGELOG_PATH, 'r') as f:
changelog_content = f.read()
new_changelog = overwrite_changelog_section(changelog_content, NEW_CONTENT)
# print("----------------------------------------------------------------------------------")
# print(new_changelog)
# print("----------------------------------------------------------------------------------")
# Write back to CHANGELOG.md
with open(CHANGELOG_PATH, 'w') as f:
f.write(new_changelog)
print(f"{CHANGELOG_PATH} updated successfully!")
+113
View File
@@ -0,0 +1,113 @@
name: Changeset Converter
run-name: Changeset Conversion
on:
workflow_dispatch:
pull_request:
types: [closed]
env:
REPO_PATH: ${{ github.repository }}
GIT_REF: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || 'main' }}
NODE_VERSION: 20.18.1
jobs:
# Job 1: Create version bump PR when changesets are merged to main
changeset-pr-version-bump:
if: |
github.event_name == 'workflow_dispatch' ||
(
github.event_name == 'pull_request' &&
github.event.pull_request.merged == true &&
github.event.pull_request.base.ref == 'main' &&
github.actor != 'github-actions'
)
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
- name: Check user for team affiliation
id: team_check
if: github.event_name == 'workflow_dispatch'
uses: morfien101/actions-authorized-user@4a3cfbf0bcb3cafe4a71710a278920c5d94bb38b
with:
username: ${{ github.actor }}
org: ${{ github.repository_owner }}
team: "deployer"
github_token: ${{ secrets.GITHUB_TOKEN }}
- name: Check if user is authorized
if: github.event_name == 'workflow_dispatch'
run: |
if [ "${{ steps.team_check.outputs.authorized }}" != "true" ]; then
echo "User is not authorized to run this workflow."
exit 1
fi
- name: Git Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
ref: ${{ env.GIT_REF }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: "npm"
- name: Install Dependencies
run: npm ci
# Check if there are any new changesets to process
- name: Check for changesets
id: check-changesets
run: |
NEW_CHANGESETS=$(find .changeset -name "*.md" ! -name "README.md" | wc -l | tr -d ' ')
echo "Changesets diff with previous version: $NEW_CHANGESETS"
echo "new_changesets=$NEW_CHANGESETS" >> $GITHUB_OUTPUT
# Create version bump PR using changesets/action if there are new changesets
- name: Create Changeset Pull Request
if: steps.check-changesets.outputs.new_changesets != '0'
uses: changesets/action@v1
with:
commit: "changeset version bump"
title: "Changeset version bump"
version: npm run version-packages # This performs the changeset version bump
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# Get current and previous versions to edit changelog entry
- name: Get version
id: get_version
run: |
VERSION=$(git show HEAD:package.json | jq -r '.version')
echo "version=$VERSION" >> $GITHUB_OUTPUT
PREV_VERSION=$(git show origin/main:package.json | jq -r '.version')
echo "prev_version=$PREV_VERSION" >> $GITHUB_OUTPUT
echo "version=$VERSION"
echo "prev_version=$PREV_VERSION"
# Update CHANGELOG.md with proper format
- name: Update Changelog Format
env:
VERSION: ${{ steps.get_version.outputs.version }}
PREV_VERSION: ${{ steps.get_version.outputs.prev_version }}
run: python .github/scripts/overwrite_changeset_changelog.py
# Commit and push changelog updates
- name: Push Changelog updates to Pull Request
run: |
git config user.name "github-actions"
git config user.email github-actions@github.com
echo "Running git add and commit..."
git add CHANGELOG.md
git commit -m "Updating CHANGELOG.md format"
git status
echo "--------------------------------------------------------------------------------"
echo "Pushing to remote..."
echo "--------------------------------------------------------------------------------"
CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD)
git push origin $CURRENT_BRANCH
+173
View File
@@ -0,0 +1,173 @@
name: Claude Issue Triage
on:
issues:
types: [opened]
# Manual trigger for backfilling existing issues. Run from terminal:
# gh workflow run claude-issue-triage.yml -f issue_number=1234
# Or batch process:
# gh issue list --state open --limit 10 --json number --jq '.[].number' | while read num; do
# gh workflow run claude-issue-triage.yml -f issue_number=$num
# sleep 60
# done
workflow_dispatch:
inputs:
issue_number:
description: 'Issue number to triage'
required: true
type: string
jobs:
claude-issue-triage:
runs-on: ubuntu-latest
timeout-minutes: 120
# SECURITY: These permissions are intentionally restrictive.
# - contents: read -> Claude can read the codebase but CANNOT write/push any code
# - issues: write -> Claude can comment and add labels (the only write access needed)
# - pull-requests: read -> Claude can view PR context but CANNOT create PRs
# This ensures that even if a malicious user attempts prompt injection via issue content,
# Claude cannot modify repository code, create branches, or open PRs.
permissions:
contents: read
issues: write
pull-requests: read
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Run Issue Response & Triage
id: triage
uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
github_token: ${{ secrets.GITHUB_TOKEN }}
allowed_non_write_users: "*"
# Allow all tools - security is enforced by GitHub permissions above (contents: read, issues: write)
claude_args: --model claude-opus-4-5-20251101 --allowedTools "Bash,Read,Write,Edit,Glob,Grep,WebFetch,WebSearch"
prompt: |
You're a GitHub issue first responder for the open source Cline repository.
**Issue:** #${{ github.event.issue.number || inputs.issue_number }}
**Title:** ${{ github.event.issue.title || 'See issue details below' }}
**Author:** @${{ github.event.issue.user.login || 'See issue details below' }}
## Your job
Investigate this issue thoroughly, then post a single helpful comment that helps the user and gives maintainers the context they need.
## Investigation
Start by reading the full issue:
gh issue view ${{ github.event.issue.number || inputs.issue_number }}
### Search for duplicates and related issues
Search thoroughly for existing issues that match this one:
gh issue list --search "<keywords from the issue>" --state all --limit 30
gh issue list --search "<error messages>" --state all --limit 20
gh issue list --search "<affected feature/component>" --state all --limit 20
For each relevant issue you find, read it including its comments:
gh issue view <number> --comments
You're looking for:
- **Duplicates**: Issues describing the same problem. Link to them and explain why you think they're duplicates. If closed, check how they were resolved - the solution might apply here.
- **Related issues**: Similar problems or context that could help. Pull useful information from their comments (workarounds others found, debugging steps that helped, maintainer explanations). Link to them and explain the connection.
If there are closed issues with solutions, surface those solutions prominently - this might immediately solve the user's problem.
### Analyze recent changes (ALWAYS DO THIS)
Many issues are regressions from recent releases. **Always** check what changed recently:
gh release list --limit 10
gh pr list --state merged --limit 50 --json number,title,mergedAt,author,body
Look for PRs merged in the last few weeks that might correlate with the issue. If you find a likely connection:
gh pr view <number>
gh pr diff <number>
git log --since="1 month ago" --oneline -- <relevant paths>
git show <commit>
**Always include your findings in your comment:**
- If you find a regression, call it out explicitly: which PR/commit likely caused it, who authored it, what changed, and suggest a fix direction if you can see one.
- If you don't find anything related, still mention it: "I analyzed recent PRs and releases but didn't find any changes that seem related to this issue."
### Search the codebase
Find the relevant code:
- Use grep/find to locate code related to the issue
- Key areas: `src/api/` (providers/models), `src/core/prompts/` (tools/prompts), platform-specific code for VS Code vs JetBrains
### Find documentation
Cline docs are at **https://docs.cline.bot/** and built with Mintlify from the `docs/` directory.
The URL structure maps directly to the file structure:
- `docs/getting-started/selecting-your-model.mdx` → https://docs.cline.bot/getting-started/selecting-your-model
- `docs/troubleshooting.mdx` → https://docs.cline.bot/troubleshooting
- Headings become anchors: `## Which Model` → `#which-model`
Search the `docs/` directory to find relevant documentation, then construct URLs to link users to:
```bash
ls docs/
grep -r "keyword" docs/ --include="*.mdx" -l
```
### Identify subject matter experts
For issues that clearly need engineering attention:
git log --since="6 months ago" --format="%an" -- <relevant paths> | sort | uniq -c | sort -rn | head -5
Cross-reference with GitHub usernames. Include in your response (@mention, do NOT assign):
| SME | Reason |
|-----|--------|
| @username1 | Authored PR #X which modified this area |
| @username2 | Primary contributor to affected file |
## Weak model detection
Many issues are caused by users running small or non-frontier models that don't tool-call reliably. Signs include:
- Model failing to use tools correctly
- Nonsensical or malformed responses
- User is running a small/local model or older model version
If this looks like a weak model issue, kindly suggest they try reproducing with Claude Sonnet and report back if it persists. Link to https://docs.cline.bot/getting-started/selecting-your-model if helpful. Still label and triage normally.
## Your comment
Write a single comment as a helpful community member. Be conversational, not robotic. Include what's relevant:
- **Helpful response** - Answer their question, suggest a fix, provide a workaround. If you found solutions in related closed issues, surface those prominently.
- **Duplicates and related issues** - Link to any you found and explain why they're duplicates/related. Summarize useful context from their comments.
- **Regression analysis** - If this looks like a regression, explain what change likely caused it, link to the PR/commit, and tag the author.
- **Clarifying questions** - If you need more info, ask specific questions. Don't ask for things already provided.
- **SME table** - Include the table above if this needs engineering attention. Don't tag people for questions with obvious answers or weak-model issues.
- **Context for maintainers** - Relevant code paths, what you found. Keep it concise.
- **Docs links** - If there's relevant documentation, link to it naturally in your response as a recommendation (e.g., "For more details, check out [the Ollama setup guide](url)"). Do NOT add a "Sources" section at the end - integrate doc links into your response where they're helpful.
- **Possible Duplicates section** - ALWAYS include a "Possible Duplicates" section at the end of your comment listing issues that might be duplicates so maintainers can quickly close if appropriate. If none found, say "No obvious duplicates found."
## Labels
First, retrieve all available labels and read their descriptions to understand what each is for:
gh label list --json name,description --limit 100
Then apply the appropriate labels based on your analysis. Only use labels from the list above—do not create new labels.
gh issue edit ${{ github.event.issue.number || inputs.issue_number }} --add-label "label1,label2"
If your regression analysis found a likely culprit (a recent PR/commit that probably caused this issue), add the "Regression" label:
gh issue edit ${{ github.event.issue.number || inputs.issue_number }} --add-label "Regression"
IMPORTANT: After posting your comment, add the "Bot Responded" label to indicate this issue has received an automated response:
gh issue edit ${{ github.event.issue.number || inputs.issue_number }} --add-label "Bot Responded"
## Remember
- **This is a one-time automated response** - you will NOT see their reply or respond again. Never say things like "I can help you", "let me know", "once I have that info", or "I can give you more targeted help" - you won't be there to follow up. If you ask clarifying questions, frame them for the maintainers who will follow up, e.g., "If you can share X, that would help the maintainers diagnose this."
- Don't be formulaic. Respond to what the issue actually needs.
- Surface solutions from past issues - often the fastest path to helping.
- Connecting regressions to specific changes is extremely valuable.
- Link issues with #number so they're clickable.
+272
View File
@@ -0,0 +1,272 @@
name: Claude PR Review
on:
pull_request:
types: [opened, ready_for_review]
# Manual trigger for backfilling existing PRs. Run from terminal:
# gh workflow run claude-pr-review.yml -f pr_number=1234
# Or batch process open PRs:
# gh pr list --state open --limit 10 --json number --jq '.[].number' | while read num; do
# gh workflow run claude-pr-review.yml -f pr_number=$num
# sleep 60
# done
workflow_dispatch:
inputs:
pr_number:
description: 'PR number to review'
required: true
type: string
jobs:
claude-pr-review:
# Runs on PR opened/ready_for_review (skips drafts) or manual trigger for backfilling
if: |
(github.event_name == 'pull_request' && github.event.pull_request.draft == false) ||
github.event_name == 'workflow_dispatch'
runs-on: ubuntu-latest
timeout-minutes: 120
# SECURITY: These permissions are intentionally restrictive.
# - contents: read -> Claude can read the codebase but CANNOT write/push any code
# - pull-requests: write -> Claude can post reviews and inline suggestions
# - issues: read -> Claude can search for related issues
# NOTE: Even with pull-requests: write, Claude CANNOT merge PRs because branch protection
# requires 1 approval from a Code Owner. The GITHUB_TOKEN cannot bypass this.
permissions:
contents: read
pull-requests: write
issues: read
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Get PR number
id: pr
run: |
if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
echo "number=${{ inputs.pr_number }}" >> $GITHUB_OUTPUT
else
echo "number=${{ github.event.pull_request.number }}" >> $GITHUB_OUTPUT
fi
- name: Run PR Review
id: review
uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
github_token: ${{ secrets.GITHUB_TOKEN }}
allowed_non_write_users: "*"
claude_args: --model claude-opus-4-5-20251101 --allowedTools "Bash,Read,Write,Edit,Glob,Grep,WebFetch,WebSearch"
prompt: |
You're a GitHub PR reviewer for the open source Cline repository. Your goal is to give the PR author helpful feedback and give maintainers the context they need to review efficiently.
PR: #${{ steps.pr.outputs.number }}
## Gather context
```bash
# Get full PR details
gh pr view ${{ steps.pr.outputs.number }} --json number,title,body,author,createdAt,updatedAt,isDraft,labels,commits,files,additions,deletions,changedFiles,baseRefName,headRefName,mergeable,reviewDecision
# Get the diff
gh pr diff ${{ steps.pr.outputs.number }}
# Check CI status
gh pr checks ${{ steps.pr.outputs.number }}
# Get existing review comments (to understand context and your previous feedback)
gh api repos/${{ github.repository }}/pulls/${{ steps.pr.outputs.number }}/comments --jq '.[] | {user: .user.login, body: .body, path: .path, created_at: .created_at}'
# Get conversation comments
gh pr view ${{ steps.pr.outputs.number }} --comments
```
If this is a re-review (workflow_dispatch event):
Read your previous comments carefully. Understand what you asked for before.
Check if new commits or comments address your previous feedback.
## Check contributing guidelines
Flag (but don't block) if:
- Missing changeset - For user-facing changes, check if there's a `.changeset/` file:
```bash
gh pr diff ${{ steps.pr.outputs.number }} --name-only | grep '.changeset/' || echo "No changeset found"
```
If missing, ask them to run `npm run changeset`
- Missing tests - New features should have tests
## Find related issues and PRs
Search thoroughly for context that might help with the review:
```bash
# Find related issues for context
gh issue list --search "<keywords from the PR>" --state all --limit 30
gh issue list --search "<error messages or feature names>" --state all --limit 20
# Find similar PRs for reference
gh pr list --search "<keywords>" --state all --limit 30
```
For each relevant issue or PR you find, read it including comments:
```bash
gh issue view <number> --comments
gh pr view <number> --comments
```
Look for:
- Open issues this PR might fix that weren't linked in the description
- Similar PRs that went through review - what feedback did they get? What patterns did they follow?
- Context from maintainer discussions that could inform your review
## Find subject matter experts
For files changed in this PR, find who knows the code best:
```bash
# Get files changed
gh pr diff ${{ steps.pr.outputs.number }} --name-only
# For each relevant path, find contributors
git log --since="6 months ago" --format="%an" -- <path> | sort | uniq -c | sort -rn | head -5
```
Cross-reference git authors with GitHub usernames. Include an SME table in your response:
| SME | Reason |
|-----|--------|
| @username1 | Authored PR #X which modified this area |
| @username2 | Primary contributor to affected file (15 commits in 6 months) |
| @username3 | Reviewed similar PR #Y with extensive feedback |
## Deep code review
This is the most important part. Don't just look for syntax issues - understand what the PR is trying to achieve and whether the implementation is the right approach.
Step 1: Understand the intent
Read the PR description and understand what the author is trying to accomplish. What problem are they solving? What feature are they adding?
Step 2: Form your own opinion first
Before analyzing their code, think about how YOU would implement this feature or fix. What files would you touch? What patterns would you follow? What edge cases would you handle?
Step 3: Compare approaches
Now look at their implementation. How does it compare to what you would have done?
- Is their approach better in some ways? Note what they did well.
- Is their approach missing something? Be specific about what and why.
- Are there edge cases they haven't considered?
- Does it follow the patterns established in similar parts of the codebase?
Step 4: Look at the bigger picture
- What other files or systems does this change interact with?
- Could this break anything else?
- Is there additional work needed beyond this PR to complete the feature?
- Does this fit well with the overall architecture?
Step 5: Find reference implementations
Look for similar changes in the codebase:
```bash
git log --oneline --all --grep="<relevant keywords>" | head -20
git log --oneline -- <similar files> | head -20
```
If this is adding a new API provider, look at how other providers are implemented.
If this is adding a new feature, look at how similar features were added.
Note where their implementation aligns with or diverges from established patterns.
Step 6: Standard code review checks
- DRY: Is there duplicated code that could be extracted?
- Error handling: Are errors handled appropriately?
- Security: Any injection risks, credential exposure, unsafe dependencies?
- Performance: Any obvious inefficiencies, memory leaks, N+1 patterns?
- Types: Is TypeScript used correctly? Any unsafe type assertions?
- Naming: Are variables and functions named clearly?
- Comments: Is complex logic explained? Are there outdated comments?
## Inline code suggestions
For specific code improvements, use GitHub's suggestion syntax via `gh api`.
This creates suggestions the author can commit with one click.
Single-line suggestion:
```bash
gh api repos/${{ github.repository }}/pulls/${{ steps.pr.outputs.number }}/reviews \
-X POST \
-f commit_id="$(gh pr view ${{ steps.pr.outputs.number }} --json headRefOid -q .headRefOid)" \
-f event="COMMENT" \
-f body="" \
-F comments='[
{
"path": "src/example.ts",
"line": 42,
"body": "Consider simplifying:\n\n```suggestion\nconst result = items.filter(Boolean);\n```"
}
]'
```
Multi-line suggestion (replacing lines 40-45):
```bash
gh api repos/${{ github.repository }}/pulls/${{ steps.pr.outputs.number }}/reviews \
-X POST \
-f commit_id="$(gh pr view ${{ steps.pr.outputs.number }} --json headRefOid -q .headRefOid)" \
-f event="COMMENT" \
-f body="" \
-F comments='[
{
"path": "src/example.ts",
"start_line": 40,
"line": 45,
"body": "This can be simplified:\n\n```suggestion\nconst simplified = doThing();\n```"
}
]'
```
Use inline suggestions for concrete improvements. Use regular comments for questions or broader feedback.
## Post your review
After your investigation, post a single helpful comment that helps the author and gives maintainers context.
Start with a warm thank you for their contribution. Be conversational, not robotic.
Include what's relevant:
- In-depth explanation of what the PR does - Be comprehensive. A maintainer should be able to read this section and fully understand the author's intent, why they made the changes, how they implemented it, and what files/systems are affected. Don't just summarize - explain.
- Related issues/PRs you found that provide useful context (link to them)
- Your review findings (issues to address, suggestions, etc.)
- Clear next steps for the author
Include a "For Maintainers" section with:
- Anything else useful to help the maintainer resolve this PR
- Related issues/PRs with context on why they're relevant
- Open issues this PR might fix that weren't linked in the description
- Your recommendation: merge as-is, needs changes, needs discussion, close, etc.
- SME table - who should review this and why
For the SME table:
| SME | Reason |
|-----|--------|
| @username | Primary contributor to affected files |
## Update labels
Add appropriate labels based on your analysis:
```bash
gh label list --json name,description --limit 100
gh pr edit ${{ steps.pr.outputs.number }} --add-label "label1,label2"
```
When done, add the reviewed label:
```bash
gh pr edit ${{ steps.pr.outputs.number }} --add-label "Bot Reviewed"
```
## Remember
- This is a one-time automated response - you will NOT see their reply or respond again. Never say things like "let me know if you have questions", "I can help you with", or "feel free to ask" - you won't be there to follow up. Frame any questions for the maintainers who will follow up.
- Be helpful and welcoming - Many contributors are new to the project
- Be specific - Point to exact lines and suggest fixes, don't give vague feedback
- Think deeply - Don't just surface-level review, understand the intent and evaluate the approach
- Use inline suggestions - Make it easy for authors to accept changes
- You're a first-pass reviewer - A human maintainer will do final approval
-435
View File
@@ -1,435 +0,0 @@
name: cli-publish
on:
schedule:
- cron: "0 12 * * *"
workflow_dispatch:
inputs:
publish_target:
description: "Which publish flow to run"
required: true
default: "main"
type: choice
options:
- main
- nightly
git_tag:
description: "Existing release tag to publish when publish_target=main, for example cli-v0.1.0"
required: false
type: string
confirm_publish:
description: 'Required when publish_target=main. Type "publish" to confirm release publish.'
required: false
type: string
force_nightly_publish:
description: "Force nightly publish even with no commits in last 24h"
required: false
type: boolean
default: false
permissions:
contents: read
id-token: write
defaults:
run:
working-directory: .
jobs:
publish-main:
name: Publish cline
permissions:
contents: write
id-token: write
if: |
github.repository == 'cline/cline' &&
github.ref == 'refs/heads/main' &&
github.event_name == 'workflow_dispatch' &&
github.event.inputs.publish_target == 'main' &&
github.event.inputs.confirm_publish == 'publish' &&
!endsWith(github.actor, '[bot]')
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
ref: ${{ github.event.inputs.git_tag }}
fetch-depth: 0
fetch-tags: true
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: "1.3.13"
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "24.x"
registry-url: "https://registry.npmjs.org"
- name: Verify publish tooling
run: |
NPM_VERSION=$(npm --version)
echo "npm ${NPM_VERSION}"
IFS=. read -r major minor patch <<EOF
${NPM_VERSION}
EOF
if [ "$major" -lt 11 ] || { [ "$major" -eq 11 ] && [ "$minor" -lt 5 ]; } || { [ "$major" -eq 11 ] && [ "$minor" -eq 5 ] && [ "$patch" -lt 1 ]; }; then
echo "npm 11.5.1 or newer is required for trusted publishing"
exit 1
fi
- name: Install dependencies
run: bun install
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Validate release tag
id: version
env:
TAG: ${{ github.event.inputs.git_tag }}
run: |
if [ -z "$TAG" ]; then
echo "git_tag is required when publish_target=main"
exit 1
fi
if ! printf "%s\n" "$TAG" | grep -Eq '^cli-v[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?$'; then
echo "git_tag must look like cli-vX.Y.Z, got: ${TAG}"
exit 1
fi
VERSION="${TAG#cli-v}"
PACKAGE_VERSION=$(node -p "require('./apps/cli/package.json').version")
if [ "$PACKAGE_VERSION" != "$VERSION" ]; then
echo "apps/cli/package.json version ${PACKAGE_VERSION} does not match ${TAG}"
exit 1
fi
if ! printf "%s\n" "$VERSION" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?$'; then
echo "apps/cli/package.json has invalid version: ${VERSION}"
exit 1
fi
TAG_COMMIT=$(git rev-parse "${TAG}^{commit}")
HEAD_COMMIT=$(git rev-parse HEAD)
if [ "$TAG_COMMIT" != "$HEAD_COMMIT" ]; then
echo "${TAG} does not point at the checked out commit"
exit 1
fi
git fetch origin +main:refs/remotes/origin/main
if ! git merge-base --is-ancestor "$HEAD_COMMIT" origin/main; then
echo "${TAG} is not reachable from origin/main"
exit 1
fi
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
echo "tag=${TAG}" >> "$GITHUB_OUTPUT"
- name: Build SDK packages
run: bun run build:sdk
env:
TELEMETRY_SERVICE_API_KEY: ${{ secrets.TELEMETRY_SERVICE_API_KEY }}
ERROR_SERVICE_API_KEY: ${{ secrets.ERROR_SERVICE_API_KEY }}
OTEL_TELEMETRY_ENABLED: ${{ secrets.OTEL_TELEMETRY_ENABLED }}
OTEL_LOGS_EXPORTER: otlp
OTEL_METRICS_EXPORTER: otlp
OTEL_EXPORTER_OTLP_PROTOCOL: ${{ secrets.OTEL_EXPORTER_OTLP_PROTOCOL }}
OTEL_EXPORTER_OTLP_ENDPOINT: ${{ secrets.OTEL_EXPORTER_OTLP_ENDPOINT }}
OTEL_EXPORTER_OTLP_HEADERS: ${{ secrets.OTEL_EXPORTER_OTLP_HEADERS }}
- name: Run tests
run: bun run test
- name: Build platform binaries
run: bun script/build.ts --install-native-variants --skip-sdk-build
working-directory: apps/cli
env:
TELEMETRY_SERVICE_API_KEY: ${{ secrets.TELEMETRY_SERVICE_API_KEY }}
ERROR_SERVICE_API_KEY: ${{ secrets.ERROR_SERVICE_API_KEY }}
OTEL_TELEMETRY_ENABLED: ${{ secrets.OTEL_TELEMETRY_ENABLED }}
OTEL_LOGS_EXPORTER: otlp
OTEL_METRICS_EXPORTER: otlp
OTEL_EXPORTER_OTLP_PROTOCOL: ${{ secrets.OTEL_EXPORTER_OTLP_PROTOCOL }}
OTEL_EXPORTER_OTLP_ENDPOINT: ${{ secrets.OTEL_EXPORTER_OTLP_ENDPOINT }}
OTEL_EXPORTER_OTLP_HEADERS: ${{ secrets.OTEL_EXPORTER_OTLP_HEADERS }}
- name: Verify build output
env:
VERSION: ${{ steps.version.outputs.version }}
run: |
EXPECTED=(
"@cline/cli-darwin-arm64"
"@cline/cli-darwin-x64"
"@cline/cli-linux-arm64"
"@cline/cli-linux-x64"
"@cline/cli-windows-arm64"
"@cline/cli-windows-x64"
)
for package_name in "${EXPECTED[@]}"; do
dir="apps/cli/dist/${package_name#@cline/}"
if [ ! -f "$dir/package.json" ]; then
echo "Missing package manifest: $dir/package.json"
exit 1
fi
actual_name=$(node -p "require('./$dir/package.json').name")
actual_version=$(node -p "require('./$dir/package.json').version")
if [ "$actual_name" != "$package_name" ]; then
echo "Expected $package_name, got $actual_name"
exit 1
fi
if [ "$actual_version" != "$VERSION" ]; then
echo "Expected $package_name@$VERSION, got $actual_version"
exit 1
fi
ls -lh "$dir/bin/"
done
- name: Publish to NPM with latest tag
env:
NPM_CONFIG_PROVENANCE: "true"
run: bun script/publish-npm.ts --tag latest
working-directory: apps/cli
- name: Get Previous CLI Tag
id: prev_tag
env:
CURRENT_TAG: ${{ steps.version.outputs.tag }}
run: |
PREV_TAG=$(git describe --tags --abbrev=0 --match 'cli-v*' "$CURRENT_TAG^" 2>/dev/null || echo "")
echo "prev_tag=$PREV_TAG" >> $GITHUB_OUTPUT
- name: Get Changelog Entry
id: changelog
run: |
# Grab content between the first "## " header and the next one in apps/cli/CHANGELOG.md
CONTENT=$(awk '/^## [0-9]/{if(found) exit; found=1; next} found{print}' apps/cli/CHANGELOG.md)
echo "content<<EOF" >> $GITHUB_OUTPUT
echo "$CONTENT" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
- name: Create GitHub Release
uses: softprops/action-gh-release@v1
with:
tag_name: ${{ steps.version.outputs.tag }}
name: "CLI v${{ steps.version.outputs.version }}"
body: |
${{ steps.changelog.outputs.content }}
${{ steps.prev_tag.outputs.prev_tag != '' && format('**Full Changelog**: https://github.com/{0}/compare/{1}...{2}', github.repository, steps.prev_tag.outputs.prev_tag, steps.version.outputs.tag) || '' }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Summary
env:
VERSION: ${{ steps.version.outputs.version }}
run: |
echo "Published cline@${VERSION} to npm with dist-tag 'latest'"
echo "Install with: npm install -g cline"
- name: Post release to Slack
uses: slackapi/slack-github-action@v3.0.1
with:
method: chat.postMessage
token: ${{ secrets.SLACK_RELEASE_BOT_TOKEN }}
payload: |
channel: "C0APVKGGZFC"
text: "Cline CLI v${{ steps.version.outputs.version }}"
blocks:
- type: "section"
text:
type: "mrkdwn"
text: "Cline CLI v${{ steps.version.outputs.version }}"
- type: "section"
text:
type: "mrkdwn"
text: ${{ toJSON(steps.changelog.outputs.content) }}
- type: "context"
elements:
- type: "mrkdwn"
text: "<https://www.npmjs.com/package/cline/v/${{ steps.version.outputs.version }}|View on npm>${{ steps.prev_tag.outputs.prev_tag != '' && format(' | Full Changelog: https://github.com/{0}/compare/{1}...{2}', github.repository, steps.prev_tag.outputs.prev_tag, steps.version.outputs.tag) || '' }}"
publish-nightly:
name: Publish cline nightly
permissions:
contents: read
id-token: write
if: |
github.repository == 'cline/cline' &&
github.ref == 'refs/heads/main' &&
(
github.event_name == 'schedule' ||
(
github.event_name == 'workflow_dispatch' &&
github.event.inputs.publish_target == 'nightly'
)
)
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Check for recent commits
id: check_commits
env:
FORCE_PUBLISH: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.force_nightly_publish == 'true' }}
run: |
if [ "$FORCE_PUBLISH" = "true" ]; then
echo "force_nightly_publish enabled, proceeding with publish"
echo "skip=false" >> "$GITHUB_OUTPUT"
exit 0
fi
if [ "$(git rev-list --count HEAD --since='24 hours ago')" -eq 0 ]; then
echo "No commits in last 24 hours, skipping publish"
echo "skip=true" >> "$GITHUB_OUTPUT"
else
echo "Found recent commits, proceeding with publish"
echo "skip=false" >> "$GITHUB_OUTPUT"
fi
- name: Setup Bun
if: steps.check_commits.outputs.skip != 'true'
uses: oven-sh/setup-bun@v2
with:
bun-version: "1.3.13"
- name: Setup Node.js
if: steps.check_commits.outputs.skip != 'true'
uses: actions/setup-node@v4
with:
node-version: "24.x"
registry-url: "https://registry.npmjs.org"
- name: Verify publish tooling
if: steps.check_commits.outputs.skip != 'true'
run: |
NPM_VERSION=$(npm --version)
echo "npm ${NPM_VERSION}"
IFS=. read -r major minor patch <<EOF
${NPM_VERSION}
EOF
if [ "$major" -lt 11 ] || { [ "$major" -eq 11 ] && [ "$minor" -lt 5 ]; } || { [ "$major" -eq 11 ] && [ "$minor" -eq 5 ] && [ "$patch" -lt 1 ]; }; then
echo "npm 11.5.1 or newer is required for trusted publishing"
exit 1
fi
- name: Install dependencies
if: steps.check_commits.outputs.skip != 'true'
run: bun install
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Build SDK packages
if: steps.check_commits.outputs.skip != 'true'
run: bun run build:sdk
env:
TELEMETRY_SERVICE_API_KEY: ${{ secrets.TELEMETRY_SERVICE_API_KEY }}
ERROR_SERVICE_API_KEY: ${{ secrets.ERROR_SERVICE_API_KEY }}
OTEL_TELEMETRY_ENABLED: ${{ secrets.OTEL_TELEMETRY_ENABLED }}
OTEL_LOGS_EXPORTER: otlp
OTEL_METRICS_EXPORTER: otlp
OTEL_EXPORTER_OTLP_PROTOCOL: ${{ secrets.OTEL_EXPORTER_OTLP_PROTOCOL }}
OTEL_EXPORTER_OTLP_ENDPOINT: ${{ secrets.OTEL_EXPORTER_OTLP_ENDPOINT }}
OTEL_EXPORTER_OTLP_HEADERS: ${{ secrets.OTEL_EXPORTER_OTLP_HEADERS }}
- name: Run tests
if: steps.check_commits.outputs.skip != 'true'
run: bun run test
- name: Generate nightly version
if: steps.check_commits.outputs.skip != 'true'
id: version
run: |
BASE_VERSION=$(node -p "require('./apps/cli/package.json').version")
TIMESTAMP=$(date +%s)
VERSION="${BASE_VERSION}-nightly.${TIMESTAMP}"
echo "Base version: ${BASE_VERSION}"
echo "Generated nightly version: ${VERSION}"
echo "base_version=${BASE_VERSION}" >> "$GITHUB_OUTPUT"
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
- name: Update nightly package version
if: steps.check_commits.outputs.skip != 'true'
env:
VERSION: ${{ steps.version.outputs.version }}
run: |
node -e '
const fs = require("node:fs");
const path = "apps/cli/package.json";
const pkg = JSON.parse(fs.readFileSync(path, "utf8"));
pkg.version = process.env.VERSION;
fs.writeFileSync(path, `${JSON.stringify(pkg, null, "\t")}\n`);
'
cat apps/cli/package.json | grep '"version"'
- name: Build platform binaries
if: steps.check_commits.outputs.skip != 'true'
run: bun script/build.ts --install-native-variants --skip-sdk-build
working-directory: apps/cli
env:
TELEMETRY_SERVICE_API_KEY: ${{ secrets.TELEMETRY_SERVICE_API_KEY }}
ERROR_SERVICE_API_KEY: ${{ secrets.ERROR_SERVICE_API_KEY }}
OTEL_TELEMETRY_ENABLED: ${{ secrets.OTEL_TELEMETRY_ENABLED }}
OTEL_LOGS_EXPORTER: otlp
OTEL_METRICS_EXPORTER: otlp
OTEL_EXPORTER_OTLP_PROTOCOL: ${{ secrets.OTEL_EXPORTER_OTLP_PROTOCOL }}
OTEL_EXPORTER_OTLP_ENDPOINT: ${{ secrets.OTEL_EXPORTER_OTLP_ENDPOINT }}
OTEL_EXPORTER_OTLP_HEADERS: ${{ secrets.OTEL_EXPORTER_OTLP_HEADERS }}
- name: Verify build output
if: steps.check_commits.outputs.skip != 'true'
env:
VERSION: ${{ steps.version.outputs.version }}
run: |
EXPECTED=(
"@cline/cli-darwin-arm64"
"@cline/cli-darwin-x64"
"@cline/cli-linux-arm64"
"@cline/cli-linux-x64"
"@cline/cli-windows-arm64"
"@cline/cli-windows-x64"
)
for package_name in "${EXPECTED[@]}"; do
dir="apps/cli/dist/${package_name#@cline/}"
if [ ! -f "$dir/package.json" ]; then
echo "Missing package manifest: $dir/package.json"
exit 1
fi
actual_name=$(node -p "require('./$dir/package.json').name")
actual_version=$(node -p "require('./$dir/package.json').version")
if [ "$actual_name" != "$package_name" ]; then
echo "Expected $package_name, got $actual_name"
exit 1
fi
if [ "$actual_version" != "$VERSION" ]; then
echo "Expected $package_name@$VERSION, got $actual_version"
exit 1
fi
ls -lh "$dir/bin/"
done
- name: Publish to NPM with nightly tag
if: steps.check_commits.outputs.skip != 'true'
env:
NPM_CONFIG_PROVENANCE: "true"
run: bun script/publish-npm.ts --tag nightly
working-directory: apps/cli
- name: Summary
if: steps.check_commits.outputs.skip != 'true'
env:
VERSION: ${{ steps.version.outputs.version }}
run: |
echo "Published cline@${VERSION} to npm with dist-tag 'nightly'"
echo "Install with: npm install -g cline@nightly"
+312
View File
@@ -0,0 +1,312 @@
name: Cline PR Code Review
on:
pull_request:
types:
[opened, ready_for_review]
# Manual trigger for backfilling existing PRs. Run from terminal:
# gh workflow run cline-pr-review.yml -f pr_number=1234
# Or batch process open PRs:
# gh pr list --state open --limit 10 --json number --jq '.[].number' | while read num; do
# gh workflow run cline-pr-review.yml -f pr_number=$num
# sleep 60
# done
workflow_dispatch:
inputs:
pr_number:
description: "PR number to review"
required: true
type: string
concurrency:
group: pr-review-${{ github.event.pull_request.number || inputs.pr_number }}
cancel-in-progress: true
jobs:
cline-pr-review:
# Runs on PR opened/ready_for_review (skips drafts) or manual trigger for backfilling
if: |
(github.event_name == 'pull_request' && github.event.pull_request.draft == false) ||
github.event_name == 'workflow_dispatch'
runs-on: ubuntu-latest
timeout-minutes: 60
# SECURITY: These permissions are intentionally restrictive.
# - contents: read -> cline can read the codebase but CANNOT write/push any code
# - pull-requests: write -> cline can post reviews and inline suggestions
# - issues: read -> cline can search for related issues
# NOTE: Even with pull-requests: write, cline CANNOT merge PRs because branch protection
# requires 1 approval from a Code Owner. The GITHUB_TOKEN cannot bypass this.
permissions:
contents: read
pull-requests: write
issues: read
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 22
cache: "npm"
- name: Install and Verify Cline CLI
run: |
npx cline version # verify installation
- name: Configure Cline with Anthropic
run: |
npx cline auth --provider anthropic \
--apikey "${{ secrets.ANTHROPIC_API_KEY }}" \
--modelid claude-opus-4-5-20251101
- name: Get PR number
id: pr
run: |
if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
echo "number=${{ inputs.pr_number }}" >> $GITHUB_OUTPUT
else
echo "number=${{ github.event.pull_request.number }}" >> $GITHUB_OUTPUT
fi
- name: Review PR with Cline
env:
PR_NUMBER: ${{ steps.pr.outputs.number }}
GITHUB_REPO: ${{ github.repository }}
GH_TOKEN: ${{ github.token }}
CLINE_COMMAND_PERMISSIONS: |
{
"allow": [
"gh pr diff *",
"gh pr view *",
"gh pr checks *",
"gh pr list *",
"gh label list *",
"gh issue list *",
"gh issue view *",
"git log *",
"gh pr comment ${{ steps.pr.outputs.number }} *",
"gh pr edit ${{ steps.pr.outputs.number }} *",
"gh api repos/${{ github.repository }}/pulls/${{ steps.pr.outputs.number }}/comments *",
"gh api repos/${{ github.repository }}/pulls/${{ steps.pr.outputs.number }}/reviews *"
]
}
run: |
npx cline --yolo 'You'\''re a GitHub PR reviewer for the open source Cline repository. Your goal is to give the PR author helpful feedback and give maintainers the context they need to review efficiently.
PR: #'"${PR_NUMBER}"'
## Gather context
```bash
# Get full PR details
gh pr view '"${PR_NUMBER}"' --json number,title,body,author,createdAt,updatedAt,isDraft,labels,commits,files,additions,deletions,changedFiles,baseRefName,headRefName,mergeable,reviewDecision
# Get the diff
gh pr diff '"${PR_NUMBER}"'
# Check CI status
gh pr checks '"${PR_NUMBER}"'
# Get existing review comments (to understand context and your previous feedback)
gh api repos/'"${GITHUB_REPO}"'/pulls/'"${PR_NUMBER}"'/comments --jq '\''.[] | {user: .user.login, body: .body, path: .path, created_at: .created_at}'\''
# Get conversation comments
gh pr view '"${PR_NUMBER}"' --comments
```
If this is a re-review (workflow_dispatch event):
Read your previous comments carefully. Understand what you asked for before.
Check if new commits or comments address your previous feedback.
## Check contributing guidelines
Flag (but don'\''t block) if:
- Missing changeset - For user-facing changes, check if there'\''s a `.changeset/` file:
```bash
gh pr diff '"${PR_NUMBER}"' --name-only | grep '\''.changeset/'\'' || echo '\''No changeset found'\''
```
If missing, ask them to run `npm run changeset`
- Missing tests - New features should have tests
## Find related issues and PRs
Search thoroughly for context that might help with the review:
```bash
# Find related issues for context
gh issue list --search '\''<keywords from the PR>'\'' --state all --limit 30
gh issue list --search '\''<error messages or feature names>'\'' --state all --limit 20
# Find similar PRs for reference
gh pr list --search '\''<keywords>'\'' --state all --limit 30
```
For each relevant issue or PR you find, read it including comments:
```bash
gh issue view <number> --comments
gh pr view <number> --comments
```
Look for:
- Open issues this PR might fix that weren'\''t linked in the description
- Similar PRs that went through review - what feedback did they get? What patterns did they follow?
- Context from maintainer discussions that could inform your review
## Find subject matter experts
For files changed in this PR, find who knows the code best:
```bash
# Get files changed
gh pr diff '"${PR_NUMBER}"' --name-only
# For each relevant path, find contributors
git log --since='\''6 months ago'\'' --format='\''%an'\'' -- <path> | sort | uniq -c | sort -rn | head -5
```
Cross-reference git authors with GitHub usernames. Include an SME table in your response:
| SME | Reason |
|-----|--------|
| @username1 | Authored PR #X which modified this area |
| @username2 | Primary contributor to affected file (15 commits in 6 months) |
| @username3 | Reviewed similar PR #Y with extensive feedback |
## Bash command usage
Don'\''t use operators like `|`, `&&`, or `;` - run each command separately and analyze the output.
When referencing command outputs, quote them properly to avoid formatting issues.
## Deep code review
This is the most important part. Don'\''t just look for syntax issues - understand what the PR is trying to achieve and whether the implementation is the right approach.
Step 1: Understand the intent
Read the PR description and understand what the author is trying to accomplish. What problem are they solving? What feature are they adding?
Step 2: Form your own opinion first
Before analyzing their code, think about how YOU would implement this feature or fix. What files would you touch? What patterns would you follow? What edge cases would you handle?
Step 3: Compare approaches
Now look at their implementation. How does it compare to what you would have done?
- Is their approach better in some ways? Note what they did well.
- Is their approach missing something? Be specific about what and why.
- Are there edge cases they haven'\''t considered?
- Does it follow the patterns established in similar parts of the codebase?
Step 4: Look at the bigger picture
- What other files or systems does this change interact with?
- Could this break anything else?
- Is there additional work needed beyond this PR to complete the feature?
- Does this fit well with the overall architecture?
Step 5: Find reference implementations
Look for similar changes in the codebase:
```bash
git log --oneline --all --grep='\''<relevant keywords>'\'' | head -20
git log --oneline -- <similar files> | head -20
```
If this is adding a new API provider, look at how other providers are implemented.
If this is adding a new feature, look at how similar features were added.
Note where their implementation aligns with or diverges from established patterns.
Step 6: Standard code review checks
- DRY: Is there duplicated code that could be extracted?
- Error handling: Are errors handled appropriately?
- Security: Any injection risks, credential exposure, unsafe dependencies?
- Performance: Any obvious inefficiencies, memory leaks, N+1 patterns?
- Types: Is TypeScript used correctly? Any unsafe type assertions?
- Naming: Are variables and functions named clearly?
- Comments: Is complex logic explained? Are there outdated comments?
## Inline code suggestions
For specific code improvements, use GitHub'\''s suggestion syntax via `gh api`.
This creates suggestions the author can commit with one click.
Single-line suggestion:
```bash
gh api repos/'"${GITHUB_REPO}"'/pulls/'"${PR_NUMBER}"'/reviews \
-X POST \
-f commit_id="$(gh pr view '"${PR_NUMBER}"' --json headRefOid -q .headRefOid)" \
-f event='\''COMMENT'\'' \
-f body='\'''\'' \
-F comments='\''[
{
"path": "src/example.ts",
"line": 42,
"body": "Consider simplifying:\n\n```suggestion\nconst result = items.filter(Boolean);\n```"
}
]'\''
```
Multi-line suggestion (replacing lines 40-45):
```bash
gh api repos/'"${GITHUB_REPO}"'/pulls/'"${PR_NUMBER}"'/reviews \
-X POST \
-f commit_id="$(gh pr view '"${PR_NUMBER}"' --json headRefOid -q .headRefOid)" \
-f event='\''COMMENT'\'' \
-f body='\'''\'' \
-F comments='\''[
{
"path": "src/example.ts",
"start_line": 40,
"line": 45,
"body": "This can be simplified:\n\n```suggestion\nconst simplified = doThing();\n```"
}
]'\''
```
Use inline suggestions for concrete improvements. Use regular comments for questions or broader feedback.
## Post your review
After your investigation, post a single helpful comment that helps the author and gives maintainers context.
Start with a warm thank you for their contribution. Be conversational, not robotic.
Include what'\''s relevant:
- In-depth explanation of what the PR does - Be comprehensive. A maintainer should be able to read this section and fully understand the author'\''s intent, why they made the changes, how they implemented it, and what files/systems are affected. Don'\''t just summarize - explain.
- Related issues/PRs you found that provide useful context (link to them)
- Your review findings (issues to address, suggestions, etc.)
- Clear next steps for the author
Include a '\''For Maintainers'\'' section with:
- Anything else useful to help the maintainer resolve this PR
- Related issues/PRs with context on why they'\''re relevant
- Open issues this PR might fix that weren'\''t linked in the description
- Your recommendation: merge as-is, needs changes, needs discussion, close, etc.
- SME table - who should review this and why
For the SME table:
| SME | Reason |
|-----|--------|
| @username | Primary contributor to affected files |
## Update labels
Add appropriate labels based on your analysis:
```bash
gh label list --json name,description --limit 100
gh pr edit '"${PR_NUMBER}"' --add-label '\''label1,label2'\''
```
When done, add the reviewed label:
```bash
gh pr edit '"${PR_NUMBER}"' --add-label '\''Bot Reviewed'\''
```
## Remember
- This is a one-time automated response - you will NOT see their reply or respond again. Never say things like '\''let me know if you have questions'\'', '\''I can help you with'\'', or '\''feel free to ask'\'' - you won'\''t be there to follow up. Frame any questions for the maintainers who will follow up.
- Be helpful and welcoming - Many contributors are new to the project
- Be specific - Point to exact lines and suggest fixes, don'\''t give vague feedback
- Think deeply - Don'\''t just surface-level review, understand the intent and evaluate the approach
- Use inline suggestions - Make it easy for authors to accept changes
- You'\''re a first-pass reviewer - A human maintainer will do final approval'
+108
View File
@@ -0,0 +1,108 @@
name: E2E Tests
on:
push:
branches:
- main
pull_request:
types: [opened, reopened, synchronize, ready_for_review]
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
matrix_prep:
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
steps:
- id: set-matrix
run: |
echo 'matrix=[{"runner":"ubuntu"},{"runner":"windows"},{"runner":"macos"}]' >> $GITHUB_OUTPUT
e2e:
needs: matrix_prep
strategy:
fail-fast: false
matrix:
include: ${{ fromJson(needs.matrix_prep.outputs.matrix) }}
runs-on: ${{ matrix.runner }}-latest
timeout-minutes: 20
permissions:
id-token: write
contents: read
steps:
- uses: actions/checkout@v4
- name: Setup Node.js environment
uses: actions/setup-node@v4
with:
node-version: 22
# Cache root dependencies - only reuse if package-lock.json exactly matches
- name: Cache root dependencies
uses: actions/cache@v4
id: root-cache
with:
path: node_modules
key: ${{ runner.os }}-npm-${{ hashFiles('package-lock.json') }}
# Cache webview-ui dependencies - only reuse if package-lock.json exactly matches
- name: Cache webview-ui dependencies
uses: actions/cache@v4
id: webview-cache
with:
path: webview-ui/node_modules
key: ${{ runner.os }}-npm-webview-${{ hashFiles('webview-ui/package-lock.json') }}
# Cache VS Code installation
- name: Cache VS Code
uses: actions/cache@v4
id: vscode-cache
with:
path: .vscode-test
key: vscode-${{ runner.os }}-stable-${{ hashFiles('.vscode-test.mjs', 'package.json') }}
restore-keys: |
vscode-${{ runner.os }}-stable-
# Cache Playwright browsers
- name: Cache Playwright browsers
uses: actions/cache@v4
id: playwright-cache
with:
path: |
~/.cache/ms-playwright
~/Library/Caches/ms-playwright
~/AppData/Local/ms-playwright
key: playwright-browsers-${{ runner.os }}-${{ hashFiles('package-lock.json') }}
restore-keys: |
playwright-browsers-${{ runner.os }}-
- name: Install root dependencies
if: steps.root-cache.outputs.cache-hit != 'true'
run: npm ci
- name: Install webview-ui dependencies
if: steps.webview-cache.outputs.cache-hit != 'true'
run: cd webview-ui && npm ci
- name: Install xvfb on Linux
if: matrix.runner == 'ubuntu'
run: sudo apt-get update && sudo apt-get install -y xvfb
# Run optimized E2E tests (eliminates redundant builds)
- name: Run E2E tests - Linux
if: matrix.runner == 'ubuntu'
run: xvfb-run -a npm run test:e2e:optimal
- name: Run E2E tests - Non-Linux
if: matrix.runner != 'ubuntu'
run: npm run test:e2e:optimal
- uses: actions/upload-artifact@v4
if: ${{ failure() }}
with:
name: playwright-recordings-${{ matrix.runner }}
path: |
test-results/playwright/
@@ -1,100 +0,0 @@
name: ext-jb-test-integration
on:
pull_request_target:
types: [opened, reopened]
issue_comment:
types: [created]
permissions:
contents: read
pull-requests: read
concurrency:
group: jetbrains-trigger-${{ github.event.pull_request.number || github.event.issue.number }}
cancel-in-progress: true
jobs:
trigger-integration-test:
name: Run Tests
runs-on: ubuntu-latest
# Auto-run only for trusted PR authors. Anyone else needs a maintainer
# to opt their PR in by commenting /test-jetbrains.
if: |
(github.event_name == 'pull_request_target' &&
contains(fromJSON('["MEMBER","OWNER","COLLABORATOR"]'), github.event.pull_request.author_association)) ||
(github.event_name == 'issue_comment' &&
github.event.issue.pull_request &&
contains(github.event.comment.body, '/test-jetbrains') &&
contains(fromJSON('["MEMBER","OWNER","COLLABORATOR"]'), github.event.comment.author_association))
steps:
- name: Generate GitHub App Token
id: app-token
uses: actions/create-github-app-token@v1
with:
app-id: ${{ vars.CLINE_JETBRAINS_APP_ID }}
private-key: ${{ secrets.CLINE_JETBRAINS_APP_KEY }}
owner: cline
repositories: intellij-plugin
- name: Get PR details (for issue_comment trigger)
id: pr-details
if: github.event_name == 'issue_comment'
env:
GH_TOKEN: ${{ github.token }}
run: |
PR_DATA=$(gh api repos/${{ github.repository }}/pulls/${{ github.event.issue.number }})
echo "head_ref=$(echo "$PR_DATA" | jq -r '.head.ref')" >> $GITHUB_OUTPUT
echo "head_sha=$(echo "$PR_DATA" | jq -r '.head.sha')" >> $GITHUB_OUTPUT
echo "title=$(echo "$PR_DATA" | jq -r '.title')" >> $GITHUB_OUTPUT
echo "html_url=$(echo "$PR_DATA" | jq -r '.html_url')" >> $GITHUB_OUTPUT
- name: Sanitize untrusted inputs
id: sanitize
env:
RAW_BRANCH_NAME: ${{ github.event_name == 'pull_request_target' && github.head_ref || steps.pr-details.outputs.head_ref }}
RAW_PR_TITLE: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.title || steps.pr-details.outputs.title }}
run: |
# Sanitize branch name for JSON
BRANCH_NAME_JSON=$(jq -n --arg b "$RAW_BRANCH_NAME" '$b')
echo "branch_name=$BRANCH_NAME_JSON" >> $GITHUB_OUTPUT
# Sanitize PR title for JSON
PR_TITLE_JSON=$(jq -n --arg t "$RAW_PR_TITLE" '$t')
echo "pr_title=$PR_TITLE_JSON" >> $GITHUB_OUTPUT
- name: Trigger IntelliJ Plugin Integration Test
env:
BRANCH_NAME: ${{ steps.sanitize.outputs.branch_name }}
PR_TITLE: ${{ steps.sanitize.outputs.pr_title }}
PR_NUMBER: ${{ github.event.pull_request.number || github.event.issue.number }}
PR_SHA: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.head.sha || steps.pr-details.outputs.head_sha }}
PR_URL: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.html_url || steps.pr-details.outputs.html_url }}
run: |
curl -X POST \
-H "Authorization: Bearer ${{ steps.app-token.outputs.token }}" \
-H "Accept: application/vnd.github.v3+json" \
-H "User-Agent: cline-pr-trigger" \
-H "Content-Type: application/json" \
https://api.github.com/repos/cline/intellij-plugin/dispatches \
-d @- <<EOF
{
"event_type": "cline-pr-check",
"client_payload": {
"pr_number": "$PR_NUMBER",
"branch_name": $BRANCH_NAME,
"action": "${{ github.event.action }}",
"sha": "$PR_SHA",
"pr_title": $PR_TITLE,
"pr_url": "$PR_URL"
}
}
EOF
- name: Log trigger details
env:
PR_NUMBER: ${{ github.event.pull_request.number || github.event.issue.number }}
PR_SHA: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.head.sha || steps.pr-details.outputs.head_sha }}
run: |
echo "Triggered IntelliJ Plugin integration test for:"
echo " PR #$PR_NUMBER"
echo " Trigger: ${{ github.event_name }}"
echo " Action: ${{ github.event.action }}"
echo " SHA: $PR_SHA"
@@ -1,104 +0,0 @@
name: ext-vscode-publish-nightly
on:
workflow_dispatch:
run-name: "Publish Nightly from ${{ github.ref_name }} @ ${{ github.sha }}"
# Prevent concurrent publish runs on the same branch. The nightly publish script
# generates the extension version from a seconds-resolution timestamp, so parallel
# runs on the same ref can collide on the same version and cause publish failures
# or inconsistent tagging. Runs on different branches proceed independently.
concurrency:
group: ext-vscode-publish-nightly-${{ github.ref }}
cancel-in-progress: false
permissions: {}
jobs:
test:
if: github.repository == 'cline/cline' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dpc/sdk-migration-simpler-login')
permissions:
contents: read
pull-requests: read
uses: ./.github/workflows/ext-vscode-test.yml
publish:
needs: test
permissions:
contents: write
name: Publish Cline (Nightly) Extension
if: github.repository == 'cline/cline' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dpc/sdk-migration-simpler-login')
runs-on: ubuntu-latest
environment: PublishNightly
defaults:
run:
working-directory: apps/vscode
steps:
- name: Checkout selected branch
uses: actions/checkout@v4
with:
ref: ${{ github.sha }}
lfs: true
persist-credentials: false
- name: Show build source
working-directory: ${{ github.workspace }}
run: |
echo "Building ref: $GITHUB_REF"
echo "Building sha: $GITHUB_SHA"
git --no-pager log -1 --oneline
- name: Setup Node.js
uses: actions/setup-node@v4
with:
# Keep publish environment aligned with test workflow/tooling lockfile expectations.
# Newer LTS (Node 24 / npm 11) can make `npm list` fail with ELSPROBLEMS during vsce packaging.
node-version: 22
- name: Install extension dependencies
working-directory: ${{ github.workspace }}
run: npm --prefix apps/vscode ci --include=optional
- name: Install webview-ui dependencies
working-directory: ${{ github.workspace }}
run: npm --prefix apps/vscode/webview-ui ci --include=optional
- name: Install Publishing Tools
run: npm install -g @vscode/vsce ovsx
- name: Publish Nightly Extension
env:
VSCE_PAT: ${{ secrets.VSCE_PAT }}
OVSX_PAT: ${{ secrets.OVSX_PAT }}
TELEMETRY_SERVICE_API_KEY: ${{ secrets.TELEMETRY_SERVICE_API_KEY }}
ERROR_SERVICE_API_KEY: ${{ secrets.ERROR_SERVICE_API_KEY }}
CLINE_ENVIRONMENT: production
# OpenTelemetry production defaults (can be overridden at runtime)
OTEL_TELEMETRY_ENABLED: ${{ secrets.OTEL_TELEMETRY_ENABLED }}
OTEL_LOGS_EXPORTER: otlp
OTEL_METRICS_EXPORTER: otlp
OTEL_EXPORTER_OTLP_PROTOCOL: ${{ secrets.OTEL_EXPORTER_OTLP_PROTOCOL }}
OTEL_EXPORTER_OTLP_ENDPOINT: ${{ secrets.OTEL_EXPORTER_OTLP_ENDPOINT }}
OTEL_EXPORTER_OTLP_HEADERS: ${{ secrets.OTEL_EXPORTER_OTLP_HEADERS }}
run: npm run publish:marketplace:nightly
- name: Tag published commit
working-directory: ${{ github.workspace }}
env:
GH_TOKEN: ${{ github.token }}
run: |
SAFE_REF=$(echo "$GITHUB_REF_NAME" | tr '/[:upper:]' '-[:lower:]' | tr -cd 'a-z0-9._-')
SHORT_SHA=$(git rev-parse --short=12 HEAD)
TIMESTAMP=$(date -u +"%Y%m%d%H%M%S")
TAG="nightly-${SAFE_REF}-${TIMESTAMP}-${SHORT_SHA}"
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git tag -a "$TAG" -m "Cline Nightly published from ${GITHUB_REF_NAME} at ${GITHUB_SHA}"
# Use an explicit HTTPS remote with GH_TOKEN because checkout was run with
# persist-credentials: false, so actions/checkout did not persist a git credential helper.
git push "https://x-access-token:${GH_TOKEN}@github.com/${GITHUB_REPOSITORY}.git" "refs/tags/${TAG}"
echo "Tagged published commit: $TAG"
@@ -1,229 +0,0 @@
name: ext-vscode-publish-stable
on:
workflow_dispatch:
inputs:
release-type:
description: "Choose release type (release or pre-release)"
required: true
default: "release"
type: choice
options:
- pre-release
- release
auto_create_tag_from_main:
description: "Auto-create and push the provided tag from the tested main commit (recommended)"
required: true
default: true
type: boolean
tag:
description: "Tag to publish (required in both modes, e.g., v3.1.2)"
required: true
type: string
permissions:
contents: write
packages: write
checks: write
pull-requests: write
jobs:
test:
uses: ./.github/workflows/ext-vscode-test.yml
publish:
needs: test
name: Publish Extension
runs-on: ubuntu-latest
environment: publish
defaults:
run:
working-directory: apps/vscode
steps:
- uses: actions/checkout@v4
with:
ref: main
fetch-depth: 0
fetch-tags: true
lfs: true
- name: Resolve Release Tag
id: resolve_tag
working-directory: ${{ github.workspace }}
env:
TAG: ${{ github.event.inputs.tag }}
AUTO_CREATE: ${{ github.event.inputs.auto_create_tag_from_main }}
run: |
TESTED_SHA="${{ github.sha }}"
WORKFLOW_REF="${{ github.ref }}"
if [[ -z "$TAG" ]]; then
echo "Error: tag input is required"
exit 1
fi
if [[ ! "$TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+([-.][0-9A-Za-z.]+)?$ ]]; then
echo "Error: tag must match vX.Y.Z (optionally with -suffix or .suffix)"
exit 1
fi
TAG_REF="refs/tags/$TAG"
git fetch origin main --tags
if [[ "$AUTO_CREATE" == "true" ]]; then
if [[ "$WORKFLOW_REF" != "refs/heads/main" ]]; then
echo "Error: auto-create mode requires dispatching from main (current ref: $WORKFLOW_REF)"
exit 1
fi
echo "Auto-create enabled. Using tested workflow SHA: $TESTED_SHA"
if ! git merge-base --is-ancestor "$TESTED_SHA" origin/main; then
echo "Error: tested SHA $TESTED_SHA is not on origin/main"
exit 1
fi
if git show-ref --verify --quiet "$TAG_REF"; then
TAG_SHA=$(git rev-list -n 1 "$TAG_REF^{commit}")
if [[ "$TAG_SHA" != "$TESTED_SHA" ]]; then
echo "Error: tag '$TAG' already exists at $TAG_SHA, not at tested SHA ($TESTED_SHA)"
exit 1
fi
echo "Tag '$TAG' already exists at tested SHA. Continuing."
else
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git tag "$TAG" "$TESTED_SHA"
git push origin "$TAG_REF"
echo "Created and pushed tag '$TAG' from tested SHA $TESTED_SHA."
fi
else
if ! git show-ref --verify --quiet "$TAG_REF"; then
echo "Error: tag '$TAG' does not exist in the repository"
exit 1
fi
echo "Using existing tag '$TAG'."
fi
git checkout --detach "$TAG_REF^{commit}"
echo "tag=$TAG" >> $GITHUB_OUTPUT
echo "resolved_sha=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 22
- name: Install extension dependencies
working-directory: ${{ github.workspace }}
run: npm --prefix apps/vscode install --include=optional
- name: Install webview-ui dependencies
working-directory: ${{ github.workspace }}
run: npm --prefix apps/vscode/webview-ui install --include=optional
- name: Install Publishing Tools
run: npm install -g @vscode/vsce ovsx
- name: Get Version
id: get_version
run: |
VERSION=$(node -p "require('./package.json').version")
echo "version=$VERSION" >> $GITHUB_OUTPUT
- name: Verify Tag Matches Package Version
run: |
TAG="${{ steps.resolve_tag.outputs.tag }}"
VERSION="v${{ steps.get_version.outputs.version }}"
if [[ "$TAG" != "$VERSION" ]]; then
echo "Error: tag '$TAG' does not match package version '$VERSION'"
exit 1
fi
echo "Tag and package version match: $TAG"
- name: Package and Publish Extension
env:
VSCE_PAT: ${{ secrets.VSCE_PAT }}
OVSX_PAT: ${{ secrets.OVSX_PAT }}
CLINE_ENVIRONMENT: production
TELEMETRY_SERVICE_API_KEY: ${{ secrets.TELEMETRY_SERVICE_API_KEY }}
ERROR_SERVICE_API_KEY: ${{ secrets.ERROR_SERVICE_API_KEY }}
# OpenTelemetry production defaults (can be overridden at runtime)
OTEL_TELEMETRY_ENABLED: ${{ secrets.OTEL_TELEMETRY_ENABLED }}
OTEL_LOGS_EXPORTER: otlp
OTEL_METRICS_EXPORTER: otlp
OTEL_EXPORTER_OTLP_PROTOCOL: ${{ secrets.OTEL_EXPORTER_OTLP_PROTOCOL }}
OTEL_EXPORTER_OTLP_ENDPOINT: ${{ secrets.OTEL_EXPORTER_OTLP_ENDPOINT }}
OTEL_EXPORTER_OTLP_HEADERS: ${{ secrets.OTEL_EXPORTER_OTLP_HEADERS }}
RELEASE_TYPE: ${{ github.event.inputs.release-type }}
run: |
# Swap README.marketplace.md into README.md so both the GitHub
# release artifact (vsce package below) and the marketplace
# publish (npm run publish:marketplace below, which swaps
# internally as an idempotent no-op) ship the same README.
node scripts/marketplace-readme.mjs swap-in
trap 'node scripts/marketplace-readme.mjs restore' EXIT
# Required to generate the .vsix
vsce package --allow-package-secrets sendgrid --out "cline-${{ steps.get_version.outputs.version }}.vsix"
if [ "$RELEASE_TYPE" = "pre-release" ]; then
npm run publish:marketplace:prerelease
echo "Successfully published pre-release version ${{ steps.get_version.outputs.version }} to VS Code Marketplace and Open VSX Registry"
else
npm run publish:marketplace
echo "Successfully published release version ${{ steps.get_version.outputs.version }} to VS Code Marketplace and Open VSX Registry"
fi
- name: Get Previous Tag
id: prev_tag
working-directory: ${{ github.workspace }}
run: |
CURRENT_TAG="${{ steps.resolve_tag.outputs.tag }}"
PREV_TAG=$(git describe --tags --abbrev=0 "$CURRENT_TAG^" 2>/dev/null || echo "")
echo "prev_tag=$PREV_TAG" >> $GITHUB_OUTPUT
- name: Get Changelog Entry
id: changelog
working-directory: ${{ github.workspace }}
run: |
# Get content between first ## [ and second ## [
CONTENT=$(awk '/^## \[/{if(found) exit; found=1; next} found{print}' CHANGELOG.md)
echo "content<<EOF" >> $GITHUB_OUTPUT
echo "$CONTENT" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
- name: Create GitHub Release
uses: softprops/action-gh-release@v1
with:
tag_name: ${{ steps.resolve_tag.outputs.tag }}
files: "apps/vscode/*.vsix"
body: |
${{ steps.changelog.outputs.content }}
**Full Changelog**: https://github.com/${{ github.repository }}/compare/${{ steps.prev_tag.outputs.prev_tag }}...${{ steps.resolve_tag.outputs.tag }}
prerelease: ${{ github.event.inputs.release-type == 'pre-release' }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Post release to Slack
uses: slackapi/slack-github-action@v3.0.1
with:
method: chat.postMessage
token: ${{ secrets.SLACK_RELEASE_BOT_TOKEN }}
payload: |
channel: "C0APVKGGZFC"
text: "Cline ${{ steps.resolve_tag.outputs.tag }}"
blocks:
- type: "section"
text:
type: "mrkdwn"
text: "*Cline ${{ steps.resolve_tag.outputs.tag }}*"
- type: "section"
text:
type: "mrkdwn"
text: ${{ toJSON(steps.changelog.outputs.content) }}
- type: "context"
elements:
- type: "mrkdwn"
text: "Full Changelog: https://github.com/${{ github.repository }}/compare/${{ steps.prev_tag.outputs.prev_tag }}...${{ steps.resolve_tag.outputs.tag }}"
-162
View File
@@ -1,162 +0,0 @@
name: ext-vscode-test-e2e
on:
push:
branches:
- main
pull_request:
types: [opened, reopened, synchronize, ready_for_review]
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
permissions:
contents: read
pull-requests: read
jobs:
detect-changes:
runs-on: ubuntu-latest
name: Detect Changes
outputs:
e2e: ${{ steps.force.outputs.run_all == 'true' || steps.filter.outputs.e2e == 'true' }}
steps:
- id: force
if: github.event_name == 'push' || github.event_name == 'workflow_dispatch'
run: echo "run_all=true" >> "$GITHUB_OUTPUT"
- uses: actions/checkout@v4
if: steps.force.outputs.run_all != 'true'
- uses: dorny/paths-filter@v3
if: steps.force.outputs.run_all != 'true'
id: filter
with:
filters: |
e2e:
- 'apps/vscode/src/**'
- 'apps/vscode/webview-ui/**'
- 'apps/vscode/proto/**'
- 'apps/vscode/tests/**'
- 'apps/vscode/scripts/**'
- 'apps/vscode/standalone/**'
- 'apps/vscode/assets/**'
- 'apps/vscode/walkthrough/**'
- 'apps/vscode/package.json'
- 'apps/vscode/package-lock.json'
- 'apps/vscode/buf.yaml'
- 'apps/vscode/tsconfig*.json'
- 'apps/vscode/biome.jsonc'
- 'apps/vscode/esbuild.mjs'
- 'apps/vscode/.mocharc.json'
- 'apps/vscode/.vscode-test.mjs'
- 'apps/vscode/.vscodeignore'
- 'apps/vscode/playwright*.ts'
- '.github/workflows/ext-vscode-test-e2e.yml'
matrix_prep:
needs: detect-changes
if: needs.detect-changes.outputs.e2e == 'true'
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
steps:
- id: set-matrix
run: |
echo 'matrix=[{"runner":"ubuntu"},{"runner":"windows"},{"runner":"macos"}]' >> $GITHUB_OUTPUT
e2e:
needs: [detect-changes, matrix_prep]
if: needs.detect-changes.outputs.e2e == 'true'
strategy:
fail-fast: false
matrix:
include: ${{ fromJson(needs.matrix_prep.outputs.matrix) }}
runs-on: ${{ matrix.runner }}-latest
timeout-minutes: 20
permissions:
id-token: write
contents: read
defaults:
run:
working-directory: apps/vscode
steps:
- uses: actions/checkout@v4
- name: Setup Node.js environment
uses: actions/setup-node@v4
with:
node-version: 22
# Cache root dependencies - only reuse if package-lock.json exactly matches
- name: Cache root dependencies
uses: actions/cache@v4
id: root-cache
with:
path: apps/vscode/node_modules
key: ${{ runner.os }}-npm-${{ hashFiles('apps/vscode/package-lock.json') }}
# Cache webview-ui dependencies - only reuse if package-lock.json exactly matches
- name: Cache webview-ui dependencies
uses: actions/cache@v4
id: webview-cache
with:
path: apps/vscode/webview-ui/node_modules
key: ${{ runner.os }}-npm-webview-${{ hashFiles('apps/vscode/webview-ui/package-lock.json') }}
# Cache VS Code installation
- name: Cache VS Code
uses: actions/cache@v4
id: vscode-cache
with:
path: apps/vscode/.vscode-test
key: vscode-${{ runner.os }}-stable-${{ hashFiles('apps/vscode/.vscode-test.mjs', 'apps/vscode/package.json') }}
restore-keys: |
vscode-${{ runner.os }}-stable-
# Cache Playwright browsers
- name: Cache Playwright browsers
uses: actions/cache@v4
id: playwright-cache
with:
path: |
~/.cache/ms-playwright
~/Library/Caches/ms-playwright
~/AppData/Local/ms-playwright
key: playwright-browsers-${{ runner.os }}-${{ hashFiles('apps/vscode/package-lock.json') }}
restore-keys: |
playwright-browsers-${{ runner.os }}-
- name: Install extension dependencies
working-directory: ${{ github.workspace }}
run: npm --prefix apps/vscode ci
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Install webview-ui dependencies
working-directory: ${{ github.workspace }}
run: npm --prefix apps/vscode/webview-ui ci
- name: Install vsce
run: npm install -g @vscode/vsce
- name: Install xvfb on Linux
if: matrix.runner == 'ubuntu'
run: sudo apt-get update && sudo apt-get install -y xvfb
# Run optimized E2E tests (eliminates redundant builds)
- name: Run E2E tests - Linux
if: matrix.runner == 'ubuntu'
run: xvfb-run -a npm run test:e2e:optimal
- name: Run E2E tests - Non-Linux
if: matrix.runner != 'ubuntu'
run: npm run test:e2e:optimal
- uses: actions/upload-artifact@v4
if: ${{ failure() }}
with:
name: playwright-recordings-${{ matrix.runner }}
path: |
test-results/playwright/
-365
View File
@@ -1,365 +0,0 @@
name: ext-vscode-test
on:
push:
branches:
- main
workflow_dispatch:
pull_request:
branches:
- main
workflow_call:
# Set default permissions for all jobs
permissions:
contents: read # Needed to check out code
pull-requests: read # Needed for changed-file detection on pull requests
jobs:
detect-changes:
runs-on: ubuntu-latest
name: Detect Changes
outputs:
vscode: ${{ steps.force.outputs.run_all == 'true' || steps.filter.outputs.vscode == 'true' }}
testing_platform: ${{ steps.force.outputs.run_all == 'true' || steps.filter.outputs.testing_platform == 'true' }}
steps:
- id: force
if: github.event_name == 'push' || github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_call'
run: echo "run_all=true" >> "$GITHUB_OUTPUT"
- uses: actions/checkout@v4
if: steps.force.outputs.run_all != 'true'
- uses: dorny/paths-filter@v3
if: steps.force.outputs.run_all != 'true'
id: filter
with:
filters: |
vscode:
- 'apps/vscode/src/**'
- 'apps/vscode/webview-ui/**'
- 'apps/vscode/proto/**'
- 'apps/vscode/tests/**'
- 'apps/vscode/scripts/**'
- 'apps/vscode/standalone/**'
- 'apps/vscode/assets/**'
- 'apps/vscode/walkthrough/**'
- 'apps/vscode/package.json'
- 'apps/vscode/package-lock.json'
- 'apps/vscode/buf.yaml'
- 'apps/vscode/tsconfig*.json'
- 'apps/vscode/biome.jsonc'
- 'apps/vscode/esbuild.mjs'
- 'apps/vscode/.mocharc.json'
- 'apps/vscode/.nycrc*.json'
- 'apps/vscode/.vscode-test.mjs'
- 'apps/vscode/test-setup.js'
- '.github/workflows/ext-vscode-test.yml'
testing_platform:
- 'apps/vscode/src/**'
- 'apps/vscode/proto/**'
- 'apps/vscode/standalone/**'
- 'apps/vscode/testing-platform/**'
- 'apps/vscode/tests/specs/**'
- 'apps/vscode/package.json'
- 'apps/vscode/package-lock.json'
- 'apps/vscode/buf.yaml'
- 'apps/vscode/tsconfig*.json'
- 'apps/vscode/esbuild.mjs'
- 'apps/vscode/.vscodeignore'
- 'apps/vscode/scripts/**'
- '.github/workflows/ext-vscode-test.yml'
quality-checks:
needs: detect-changes
if: needs.detect-changes.outputs.vscode == 'true' || needs.detect-changes.outputs.testing_platform == 'true'
runs-on: ubuntu-latest
name: Quality Checks
defaults:
run:
working-directory: apps/vscode
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js environment
uses: actions/setup-node@v4
with:
node-version: 22
cache: 'npm'
cache-dependency-path: |
apps/vscode/package-lock.json
apps/vscode/webview-ui/package-lock.json
- name: Install extension dependencies
working-directory: ${{ github.workspace }}
run: npm --prefix apps/vscode ci
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Install webview-ui dependencies
working-directory: ${{ github.workspace }}
run: npm --prefix apps/vscode/webview-ui ci
- name: Run Quality Checks (Parallel)
run: npm run ci:check-all
vscode-test:
needs: [detect-changes, quality-checks]
if: needs.detect-changes.outputs.vscode == 'true'
env:
VSCODE_TEST_VERSION: 1.103.0
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest]
runs-on: ${{ matrix.os }}
name: ${{ matrix.os == 'ubuntu-latest' && 'vscode test' || format('vscode test ({0})', matrix.os) }}
defaults:
run:
shell: bash
working-directory: apps/vscode
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js environment
uses: actions/setup-node@v4
with:
node-version: 22
cache: 'npm'
cache-dependency-path: |
apps/vscode/package-lock.json
apps/vscode/webview-ui/package-lock.json
- name: Install extension dependencies
working-directory: ${{ github.workspace }}
run: npm --prefix apps/vscode ci
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Install webview-ui dependencies
working-directory: ${{ github.workspace }}
run: npm --prefix apps/vscode/webview-ui ci
- name: Set up NPM on Windows
if: runner.os == 'Windows'
working-directory: ${{ github.workspace }}
run: |
npm config set script-shell "C:\\Program Files\\Git\\bin\\bash.exe"
- name: Cache VS Code test runtime
if: runner.os == 'Windows'
uses: actions/cache@v4
with:
path: .vscode-test
key: vscode-test-runtime-${{ runner.os }}-${{ env.VSCODE_TEST_VERSION }}
# Build the extension and tests (without redundant checks)
- name: Build Tests and Extension
id: build_step
run: npm run ci:build
- name: Unit Tests with coverage - Linux
id: unit_tests_linux
if: ${{ !cancelled() && steps.build_step.outcome == 'success' && runner.os == 'Linux' }}
run: |
npx nyc --nycrc-path .nycrc.unit.json --reporter=lcov npm run test:unit
- name: Unit Tests - Non-Linux
id: unit_tests_non_linux
if: ${{ !cancelled() && steps.build_step.outcome == 'success' && runner.os != 'Linux' }}
run: |
npm run test:unit
- name: Extension Integration Tests - Linux
id: integration_tests_linux
if: ${{ !cancelled() && steps.build_step.outcome == 'success' && runner.os == 'Linux' }}
run: xvfb-run -a npm run test:coverage
- name: Extension Integration Tests - Non-Linux
id: integration_tests_non_linux
if: ${{ !cancelled() && steps.build_step.outcome == 'success' && runner.os != 'Linux' }}
run: |
for attempt in 1 2 3; do
echo "Running extension integration tests (attempt ${attempt}/3)"
if npm run test:integration; then
exit 0
fi
if [ "$attempt" -eq 3 ]; then
echo "Extension integration tests failed after 3 attempts"
exit 1
fi
echo "Extension integration tests failed; retrying after short delay"
sleep 5
done
- name: Webview Tests with Coverage
id: webview_tests
if: ${{ !cancelled() && steps.build_step.outcome == 'success' }}
run: |
cd webview-ui
npm run test:coverage
- name: Save Coverage Reports
uses: actions/upload-artifact@v4
# Only upload artifacts on Linux - We only need coverage from one OS
if: runner.os == 'Linux'
with:
name: pr-coverage-reports
path: |
apps/vscode/coverage-unit/lcov.info
apps/vscode/webview-ui/coverage/lcov.info
test-platform-integration:
needs: [detect-changes, quality-checks]
if: needs.detect-changes.outputs.testing_platform == 'true'
runs-on: ubuntu-latest
defaults:
run:
working-directory: apps/vscode
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js environment
uses: actions/setup-node@v4
with:
node-version: 22
cache: 'npm'
cache-dependency-path: |
apps/vscode/package-lock.json
apps/vscode/webview-ui/package-lock.json
apps/vscode/testing-platform/package-lock.json
- name: Install extension dependencies
working-directory: ${{ github.workspace }}
run: npm --prefix apps/vscode ci
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Install webview-ui dependencies
working-directory: ${{ github.workspace }}
run: npm --prefix apps/vscode/webview-ui ci
- name: Download ripgrep binaries
run: npm run download-ripgrep
- name: Compile Standalone
run: npm run compile-standalone
- name: Install testing platform dependencies
working-directory: ${{ github.workspace }}
run: npm --prefix apps/vscode/testing-platform ci
- name: Running testing platform integration spec tests
timeout-minutes: 7
run: npm run test:tp-orchestrator -- tests/specs/ --count=1 --coverage
- name: Save Coverage Reports
uses: actions/upload-artifact@v4
with:
name: test-platform-integration-core-coverage
path: apps/vscode/coverage/**/lcov.info
# Keep the required "test" check as a tiny aggregate gate instead of the conditional
# VS Code matrix. GitHub treats conditionally skipped jobs as successful required
# checks, so the gate below preserves the old required check name while making sure
# whichever filtered test jobs were selected actually passed.
test:
needs: [detect-changes, quality-checks, vscode-test, test-platform-integration]
if: ${{ !cancelled() }}
runs-on: ubuntu-latest
name: test
steps:
- name: Verify selected test jobs
env:
DETECT_CHANGES_RESULT: ${{ needs.detect-changes.result }}
QUALITY_CHECKS_RESULT: ${{ needs.quality-checks.result }}
VSCODE_CHANGED: ${{ needs.detect-changes.outputs.vscode }}
TESTING_PLATFORM_CHANGED: ${{ needs.detect-changes.outputs.testing_platform }}
VSCODE_TEST_RESULT: ${{ needs.vscode-test.result }}
TEST_PLATFORM_RESULT: ${{ needs.test-platform-integration.result }}
run: |
if [ "$DETECT_CHANGES_RESULT" != "success" ]; then
echo "detect-changes did not succeed: $DETECT_CHANGES_RESULT"
exit 1
fi
if [ "$VSCODE_CHANGED" != "true" ] && [ "$TESTING_PLATFORM_CHANGED" != "true" ]; then
echo "No root test paths changed; skipping root test requirements."
exit 0
fi
if [ "$QUALITY_CHECKS_RESULT" != "success" ]; then
echo "quality-checks did not succeed: $QUALITY_CHECKS_RESULT"
exit 1
fi
if [ "$VSCODE_CHANGED" = "true" ] && [ "$VSCODE_TEST_RESULT" != "success" ]; then
echo "vscode-test did not succeed: $VSCODE_TEST_RESULT"
exit 1
fi
if [ "$TESTING_PLATFORM_CHANGED" = "true" ] && [ "$TEST_PLATFORM_RESULT" != "success" ]; then
echo "test-platform-integration did not succeed: $TEST_PLATFORM_RESULT"
exit 1
fi
echo "Selected root test jobs passed."
qlty:
needs: [detect-changes, quality-checks, vscode-test, test-platform-integration]
if: ${{ !cancelled() && needs.quality-checks.result == 'success' && (needs.vscode-test.result == 'success' || needs.vscode-test.result == 'skipped') && (needs.test-platform-integration.result == 'success' || needs.test-platform-integration.result == 'skipped') && (needs.detect-changes.outputs.vscode == 'true' || needs.detect-changes.outputs.testing_platform == 'true') }}
runs-on: ubuntu-latest
# Run on PRs to main, pushes to main, and manual dispatches
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Download unit tests coverage reports
if: needs.detect-changes.outputs.vscode == 'true'
uses: actions/download-artifact@v4
with:
name: pr-coverage-reports
path: apps/vscode
- name: Upload core unit tests coverage to Qlty
if: needs.detect-changes.outputs.vscode == 'true'
uses: qltysh/qlty-action/coverage@v2
with:
token: ${{ secrets.QLTY_COVERAGE_TOKEN }}
# we can merge multiple files if necessary
files: |
apps/vscode/coverage-unit/lcov.info
tag: unit:core
- name: Upload webview-ui unit tests coverage to Qlty
if: needs.detect-changes.outputs.vscode == 'true'
uses: qltysh/qlty-action/coverage@v2
with:
token: ${{ secrets.QLTY_COVERAGE_TOKEN }}
# we can merge multiple files if necessary
files: |
apps/vscode/webview-ui/coverage/lcov.info
tag: unit:webview-ui
add-prefix: webview-ui/
- name: Download test platform integration core coverage artifact
if: needs.detect-changes.outputs.testing_platform == 'true'
uses: actions/download-artifact@v4
continue-on-error: true
id: download-integration-coverage
with:
name: test-platform-integration-core-coverage
path: apps/vscode/integration-core-coverage-reports
- name: Upload core integration tests coverage to Qlty
if: needs.detect-changes.outputs.testing_platform == 'true' && steps.download-integration-coverage.outcome == 'success'
uses: qltysh/qlty-action/coverage@v2
with:
token: ${{ secrets.QLTY_COVERAGE_TOKEN }}
files: apps/vscode/integration-core-coverage-reports/**/lcov.info
tag: integration:core
@@ -0,0 +1,53 @@
name: Auto-label Issues
on:
issues:
types: [opened, edited]
jobs:
label:
runs-on: ubuntu-latest
permissions:
issues: write
steps:
- uses: actions/github-script@v7
with:
script: |
const body = context.payload.issue.body || '';
const labels = context.payload.issue.labels.map(l => l.name);
// Check if JetBrains Plugin is selected
if (body.match(/###\s*Plugin Type\s*\n+JetBrains Plugin/i)) {
if (!labels.includes('JetBrains')) {
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
labels: ['JetBrains']
});
}
}
// Check if VSCode Extension is selected
if (body.match(/###\s*Plugin Type\s*\n+VSCode Extension/i)) {
if (!labels.includes('VS Code')) {
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
labels: ['VS Code']
});
}
}
// Check if CLI is selected
if (body.match(/###\s*Plugin Type\s*\n+CLI/i)) {
if (!labels.includes('CLI')) {
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
labels: ['CLI']
});
}
}
+112
View File
@@ -0,0 +1,112 @@
name: Publish NPM Release
on:
workflow_dispatch:
inputs:
confirm_publish:
description: 'Type "publish" to confirm you want to publish to NPM'
required: true
type: string
permissions:
contents: read
checks: write # Required by test workflow
pull-requests: write # Required by test workflow
jobs:
test:
uses: ./.github/workflows/test.yml
publish-npm-release:
needs: test
name: Publish Cline CLI to NPM
if: github.repository == 'cline/cline' && github.ref == 'refs/heads/main' && github.event.inputs.confirm_publish == 'publish'
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20.x'
registry-url: 'https://registry.npmjs.org'
# Cache root dependencies
- name: Cache root dependencies
uses: actions/cache@v4
id: root-cache
with:
path: node_modules
key: ${{ runner.os }}-npm-${{ hashFiles('package-lock.json') }}
# Cache cli-ts dependencies
- name: Cache cli-ts dependencies
uses: actions/cache@v4
id: cli-ts-cache
with:
path: cli-ts/node_modules
key: ${{ runner.os }}-npm-cli-ts-${{ hashFiles('cli-ts/package-lock.json') }}
- name: Install root dependencies
if: steps.root-cache.outputs.cache-hit != 'true'
run: npm ci --include=optional
- name: Install cli-ts dependencies
if: steps.cli-ts-cache.outputs.cache-hit != 'true'
run: cd cli-ts && npm ci
- name: Generate Protos
run: npm run protos
- name: Read release version
id: version
run: |
# Read version from cli-ts/package.json
VERSION=$(node -p "require('./cli-ts/package.json').version")
echo "Release version: $VERSION"
echo "version=$VERSION" >> $GITHUB_OUTPUT
- name: Clean previous builds
run: rm -rf dist-standalone
- name: Build and package CLI
env:
TELEMETRY_SERVICE_API_KEY: ${{ secrets.TELEMETRY_SERVICE_API_KEY }}
ERROR_SERVICE_API_KEY: ${{ secrets.ERROR_SERVICE_API_KEY }}
CLINE_ENVIRONMENT: production
OTEL_TELEMETRY_ENABLED: "1"
OTEL_METRICS_EXPORTER: otlp
OTEL_LOGS_EXPORTER: otlp
OTEL_EXPORTER_OTLP_PROTOCOL: ${{ secrets.OTEL_EXPORTER_OTLP_PROTOCOL }}
OTEL_EXPORTER_OTLP_ENDPOINT: ${{ secrets.OTEL_EXPORTER_OTLP_ENDPOINT }}
OTEL_EXPORTER_OTLP_HEADERS: ${{ secrets.OTEL_EXPORTER_OTLP_HEADERS }}
POSTHOG_TELEMETRY_ENABLED: "true"
run: node scripts/package-npm.mjs
- name: Verify build output
run: |
echo "Checking dist-standalone directory..."
ls -la dist-standalone/
echo "Checking dist-standalone/dist..."
ls -la dist-standalone/dist/
echo "Checking package.json in dist-standalone..."
cat dist-standalone/package.json
- name: Publish to NPM with latest tag
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_RELEASE_TOKEN }}
run: |
echo "Publishing version ${{ steps.version.outputs.version }} to NPM with tag 'latest'..."
cd dist-standalone
npm publish --tag latest --access public
- name: Summary
run: |
echo "✅ Successfully published cline@${{ steps.version.outputs.version }} to NPM with tag 'latest'"
echo ""
echo "📦 Install with: npm install -g cline"
echo "🔗 NPM: https://www.npmjs.com/package/cline/v/${{ steps.version.outputs.version }}"
+152
View File
@@ -0,0 +1,152 @@
name: Publish NPM Nightly
on:
schedule:
- cron: '0 12 * * *' # 4 AM PST (UTC-8) = 12 UTC
workflow_dispatch:
permissions:
contents: read
checks: write # Required by test workflow
pull-requests: write # Required by test workflow
jobs:
test:
uses: ./.github/workflows/test.yml
publish-npm-nightly:
needs: test
name: Publish Cline CLI (Nightly) to NPM
if: github.repository == 'cline/cline' && github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Check for recent commits
id: check_commits
run: |
if [ $(git rev-list --count HEAD --since="24 hours ago") -eq 0 ]; then
echo "No commits in last 24 hours, skipping publish"
echo "skip=true" >> $GITHUB_OUTPUT
else
echo "Found recent commits, proceeding with publish"
echo "skip=false" >> $GITHUB_OUTPUT
fi
- name: Setup Node.js
if: steps.check_commits.outputs.skip != 'true'
uses: actions/setup-node@v4
with:
node-version: '20.x'
registry-url: 'https://registry.npmjs.org'
# Cache root dependencies
- name: Cache root dependencies
if: steps.check_commits.outputs.skip != 'true'
uses: actions/cache@v4
id: root-cache
with:
path: node_modules
key: ${{ runner.os }}-npm-${{ hashFiles('package-lock.json') }}
# Cache cli-ts dependencies
- name: Cache cli-ts dependencies
if: steps.check_commits.outputs.skip != 'true'
uses: actions/cache@v4
id: cli-ts-cache
with:
path: cli-ts/node_modules
key: ${{ runner.os }}-npm-cli-ts-${{ hashFiles('cli-ts/package-lock.json') }}
- name: Install root dependencies
if: steps.check_commits.outputs.skip != 'true' && steps.root-cache.outputs.cache-hit != 'true'
run: npm ci --include=optional
- name: Install cli-ts dependencies
if: steps.check_commits.outputs.skip != 'true' && steps.cli-ts-cache.outputs.cache-hit != 'true'
run: cd cli-ts && npm ci
- name: Generate Protos
if: steps.check_commits.outputs.skip != 'true'
run: npm run protos
- name: Generate nightly version with timestamp
if: steps.check_commits.outputs.skip != 'true'
id: version
run: |
# Read base version from cli-ts/package.json (e.g., "2.0.0")
BASE_VERSION=$(node -p "require('./cli-ts/package.json').version")
# Generate timestamp (Unix epoch seconds)
TIMESTAMP=$(date +%s)
# Create unique nightly version: 2.0.0-nightly.1736365200
VERSION="${BASE_VERSION}-nightly.${TIMESTAMP}"
echo "Base version: $BASE_VERSION"
echo "Generated nightly version: $VERSION"
echo "version=$VERSION" >> $GITHUB_OUTPUT
- name: Update cli-ts/package.json with nightly version
if: steps.check_commits.outputs.skip != 'true'
run: |
# Update version with timestamp-based nightly version
node -e "
const fs = require('fs');
const pkg = JSON.parse(fs.readFileSync('cli-ts/package.json', 'utf8'));
pkg.version = '${{ steps.version.outputs.version }}';
fs.writeFileSync('cli-ts/package.json', JSON.stringify(pkg, null, '\t'));
"
echo "Using version ${{ steps.version.outputs.version }} for build"
cat cli-ts/package.json | grep '"version"'
- name: Clean previous builds
if: steps.check_commits.outputs.skip != 'true'
run: rm -rf dist-standalone
- name: Build and package CLI
if: steps.check_commits.outputs.skip != 'true'
env:
TELEMETRY_SERVICE_API_KEY: ${{ secrets.TELEMETRY_SERVICE_API_KEY }}
ERROR_SERVICE_API_KEY: ${{ secrets.ERROR_SERVICE_API_KEY }}
CLINE_ENVIRONMENT: production
OTEL_TELEMETRY_ENABLED: "1"
OTEL_METRICS_EXPORTER: otlp
OTEL_LOGS_EXPORTER: otlp
OTEL_EXPORTER_OTLP_PROTOCOL: ${{ secrets.OTEL_EXPORTER_OTLP_PROTOCOL }}
OTEL_EXPORTER_OTLP_ENDPOINT: ${{ secrets.OTEL_EXPORTER_OTLP_ENDPOINT }}
OTEL_EXPORTER_OTLP_HEADERS: ${{ secrets.OTEL_EXPORTER_OTLP_HEADERS }}
POSTHOG_TELEMETRY_ENABLED: "true"
run: node scripts/package-npm.mjs
- name: Verify build output
if: steps.check_commits.outputs.skip != 'true'
run: |
echo "Checking dist-standalone directory..."
ls -la dist-standalone/
echo "Checking dist-standalone/dist..."
ls -la dist-standalone/dist/
echo "Checking package.json in dist-standalone..."
cat dist-standalone/package.json
- name: Publish to NPM with nightly tag
if: steps.check_commits.outputs.skip != 'true'
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_RELEASE_TOKEN }}
run: |
echo "Publishing version ${{ steps.version.outputs.version }} to NPM with tag 'nightly'..."
cd dist-standalone
npm publish --tag nightly --access public
- name: Summary
if: steps.check_commits.outputs.skip != 'true'
run: |
echo "✅ Successfully published cline@${{ steps.version.outputs.version }} to NPM with tag 'nightly'"
echo ""
echo "📦 Install with: npm install -g cline@nightly"
echo "🔗 NPM: https://www.npmjs.com/package/cline/v/${{ steps.version.outputs.version }}"
+82
View File
@@ -0,0 +1,82 @@
name: "Publish Nightly Release"
on:
schedule:
- cron: '0 12 * * *' # 4 AM PST (UTC-8) = 12 UTC
workflow_dispatch:
permissions:
contents: write
packages: write
checks: write
pull-requests: write
jobs:
test:
uses: ./.github/workflows/test.yml
publish:
needs: test
name: Publish Cline (Nightly) Extension
if: github.repository == 'cline/cline'
runs-on: ubuntu-latest
environment: PublishNightly
steps:
- uses: actions/checkout@v4
- name: Check for recent commits
run: |
if [ $(git rev-list --count HEAD --since="24 hours ago") -eq 0 ]; then
echo "No commits in last 24 hours, exiting"
exit 0
fi
echo "Found recent commits, proceeding with build"
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "lts/*"
# Cache root dependencies - only reuse if package-lock.json exactly matches
- name: Cache root dependencies
uses: actions/cache@v4
id: root-cache
with:
path: node_modules
key: ${{ runner.os }}-npm-${{ hashFiles('package-lock.json') }}
# Cache webview-ui dependencies - only reuse if package-lock.json exactly matches
- name: Cache webview-ui dependencies
uses: actions/cache@v4
id: webview-cache
with:
path: webview-ui/node_modules
key: ${{ runner.os }}-npm-webview-${{ hashFiles('webview-ui/package-lock.json') }}
- name: Install root dependencies
if: steps.root-cache.outputs.cache-hit != 'true'
run: npm ci --include=optional
- name: Install webview-ui dependencies
if: steps.webview-cache.outputs.cache-hit != 'true'
run: cd webview-ui && npm ci --include=optional
- name: Install Publishing Tools
run: npm install -g @vscode/vsce ovsx
- name: Publish Extension as Pre-release
env:
VSCE_PAT: ${{ secrets.VSCE_PAT }}
OVSX_PAT: ${{ secrets.OVSX_PAT }}
TELEMETRY_SERVICE_API_KEY: ${{ secrets.TELEMETRY_SERVICE_API_KEY }}
ERROR_SERVICE_API_KEY: ${{ secrets.ERROR_SERVICE_API_KEY }}
CLINE_ENVIRONMENT: production
# OpenTelemetry production defaults (can be overridden at runtime)
OTEL_TELEMETRY_ENABLED: ${{ secrets.OTEL_TELEMETRY_ENABLED }}
OTEL_LOGS_EXPORTER: otlp
OTEL_METRICS_EXPORTER: otlp
OTEL_EXPORTER_OTLP_PROTOCOL: ${{ secrets.OTEL_EXPORTER_OTLP_PROTOCOL }}
OTEL_EXPORTER_OTLP_ENDPOINT: ${{ secrets.OTEL_EXPORTER_OTLP_ENDPOINT }}
OTEL_EXPORTER_OTLP_HEADERS: ${{ secrets.OTEL_EXPORTER_OTLP_HEADERS }}
run: npm run publish:marketplace:nightly
+148
View File
@@ -0,0 +1,148 @@
name: "Publish Release"
on:
workflow_dispatch:
inputs:
release-type:
description: "Choose release type (release or pre-release)"
required: true
default: "release"
type: choice
options:
- pre-release
- release
tag:
description: "Enter existing tag to publish (e.g., v3.1.2)"
required: true
type: string
permissions:
contents: write
packages: write
checks: write
pull-requests: write
jobs:
test:
uses: ./.github/workflows/test.yml
publish:
needs: test
name: Publish Extension
runs-on: ubuntu-latest
environment: publish
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.inputs.tag }}
fetch-depth: 0
fetch-tags: true
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "lts/*"
# Cache root dependencies - only reuse if package-lock.json exactly matches
- name: Cache root dependencies
uses: actions/cache@v4
id: root-cache
with:
path: node_modules
key: ${{ runner.os }}-npm-${{ hashFiles('package-lock.json') }}
# Cache webview-ui dependencies - only reuse if package-lock.json exactly matches
- name: Cache webview-ui dependencies
uses: actions/cache@v4
id: webview-cache
with:
path: webview-ui/node_modules
key: ${{ runner.os }}-npm-webview-${{ hashFiles('webview-ui/package-lock.json') }}
- name: Install root dependencies
if: steps.root-cache.outputs.cache-hit != 'true'
run: npm install --include=optional
- name: Install webview-ui dependencies
if: steps.webview-cache.outputs.cache-hit != 'true'
run: cd webview-ui && npm install --include=optional
- name: Install Publishing Tools
run: npm install -g @vscode/vsce ovsx
- name: Get Version
id: get_version
run: |
VERSION=$(node -p "require('./package.json').version")
echo "version=$VERSION" >> $GITHUB_OUTPUT
- name: Validate Tag
id: validate_tag
run: |
TAG="${{ github.event.inputs.tag }}"
echo "tag=$TAG" >> $GITHUB_OUTPUT
echo "Using existing tag: $TAG"
# Verify the tag exists
if ! git rev-parse "$TAG" >/dev/null 2>&1; then
echo "Error: Tag '$TAG' does not exist in the repository"
exit 1
fi
echo "Tag '$TAG' validated successfully"
- name: Package and Publish Extension
env:
VSCE_PAT: ${{ secrets.VSCE_PAT }}
OVSX_PAT: ${{ secrets.OVSX_PAT }}
CLINE_ENVIRONMENT: production
TELEMETRY_SERVICE_API_KEY: ${{ secrets.TELEMETRY_SERVICE_API_KEY }}
ERROR_SERVICE_API_KEY: ${{ secrets.ERROR_SERVICE_API_KEY }}
# OpenTelemetry production defaults (can be overridden at runtime)
OTEL_TELEMETRY_ENABLED: ${{ secrets.OTEL_TELEMETRY_ENABLED }}
OTEL_LOGS_EXPORTER: otlp
OTEL_METRICS_EXPORTER: otlp
OTEL_EXPORTER_OTLP_PROTOCOL: ${{ secrets.OTEL_EXPORTER_OTLP_PROTOCOL }}
OTEL_EXPORTER_OTLP_ENDPOINT: ${{ secrets.OTEL_EXPORTER_OTLP_ENDPOINT }}
OTEL_EXPORTER_OTLP_HEADERS: ${{ secrets.OTEL_EXPORTER_OTLP_HEADERS }}
run: |
# Required to generate the .vsix
vsce package --allow-package-secrets sendgrid --out "cline-${{ steps.get_version.outputs.version }}.vsix"
if [ "${{ github.event.inputs.release-type }}" = "pre-release" ]; then
npm run publish:marketplace:prerelease
echo "Successfully published pre-release version ${{ steps.get_version.outputs.version }} to VS Code Marketplace and Open VSX Registry"
else
npm run publish:marketplace
echo "Successfully published release version ${{ steps.get_version.outputs.version }} to VS Code Marketplace and Open VSX Registry"
fi
- name: Get Previous Tag
id: prev_tag
run: |
CURRENT_TAG="${{ steps.validate_tag.outputs.tag }}"
PREV_TAG=$(git describe --tags --abbrev=0 "$CURRENT_TAG^" 2>/dev/null || echo "")
echo "prev_tag=$PREV_TAG" >> $GITHUB_OUTPUT
- name: Get Changelog Entry
id: changelog
run: |
# Get content between first ## [ and second ## [
CONTENT=$(awk '/^## \[/{if(found) exit; found=1; next} found{print}' CHANGELOG.md)
echo "content<<EOF" >> $GITHUB_OUTPUT
echo "$CONTENT" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
- name: Create GitHub Release
uses: softprops/action-gh-release@v1
with:
tag_name: ${{ steps.validate_tag.outputs.tag }}
files: "*.vsix"
body: |
${{ steps.changelog.outputs.content }}
**Full Changelog**: https://github.com/${{ github.repository }}/compare/${{ steps.prev_tag.outputs.prev_tag }}...${{ steps.validate_tag.outputs.tag }}
prerelease: ${{ github.event.inputs.release-type == 'pre-release' }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-65
View File
@@ -1,65 +0,0 @@
name: repo-label-issues
on:
issues:
types: [opened, edited]
jobs:
label:
runs-on: ubuntu-latest
permissions:
issues: write
steps:
- uses: actions/github-script@v7
with:
script: |
const body = context.payload.issue.body || '';
const labels = context.payload.issue.labels.map(l => l.name);
// Check if JetBrains Plugin is selected
if (body.match(/###\s*Plugin Type\s*\n+JetBrains Plugin/i)) {
if (!labels.includes('JetBrains')) {
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
labels: ['JetBrains']
});
}
}
// Check if VSCode Extension is selected
if (body.match(/###\s*Plugin Type\s*\n+VSCode Extension/i)) {
if (!labels.includes('VS Code')) {
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
labels: ['VS Code']
});
}
}
// Check if CLI is selected
if (body.match(/###\s*Plugin Type\s*\n+CLI/i)) {
if (!labels.includes('CLI')) {
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
labels: ['CLI']
});
}
}
// Check if beta version checkbox is checked
if (body.includes('- [X] I am using a beta version of Cline') || body.includes('- [x] I am using a beta version of Cline')) {
if (!labels.includes('beta')) {
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
labels: ['beta']
});
}
}
-25
View File
@@ -1,25 +0,0 @@
# This workflow will only label and/or close 30 issues at a time in order to avoid exceeding a rate limit.
# More info: https://docs.github.com/en/actions/use-cases-and-examples/project-management/closing-inactive-issues
name: repo-stale-issues
on:
schedule:
- cron: "30 1 * * *"
jobs:
close-issues:
runs-on: ubuntu-latest
permissions:
issues: write
pull-requests: write
steps:
- uses: actions/stale@v9
with:
days-before-issue-stale: 60
days-before-issue-close: 14
stale-issue-label: "stale"
stale-issue-message: "This issue is stale because it has been open for 60 days with no activity."
close-issue-message: "This issue was closed because it has been inactive for 14 days since being marked as stale."
days-before-pr-stale: -1
days-before-pr-close: -1
exempt-issue-labels: "pinned,security"
repo-token: ${{ secrets.GITHUB_TOKEN }}
-282
View File
@@ -1,282 +0,0 @@
name: sdk-publish
on:
workflow_dispatch:
inputs:
channel:
description: "Publish channel"
required: true
type: choice
options:
- nightly
- latest
default: nightly
force_publish:
description: "Force publish even if there are no commits in the last 24 hours"
required: false
type: boolean
default: false
confirm_publish:
description: 'Required when channel=latest. Type "publish" to confirm release publish.'
required: false
type: string
schedule:
# Run nightly at 2:00 AM UTC
- cron: "0 2 * * *"
defaults:
run:
working-directory: .
jobs:
test:
permissions:
contents: read
uses: ./.github/workflows/sdk-test.yml
publish-sdk:
needs: test
name: Publish SDK Packages
permissions:
contents: write
id-token: write
if: |
github.repository == 'cline/cline' &&
github.ref == 'refs/heads/main' &&
(
github.event_name != 'workflow_dispatch' ||
inputs.channel != 'latest' ||
(
inputs.confirm_publish == 'publish' &&
!endsWith(github.actor, '[bot]')
)
)
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Determine publish channel
id: channel
env:
EVENT_NAME: ${{ github.event_name }}
INPUT_CHANNEL: ${{ inputs.channel }}
run: |
# Default to nightly for scheduled runs
if [ "$EVENT_NAME" = "schedule" ]; then
echo "channel=nightly" >> $GITHUB_OUTPUT
else
echo "channel=$INPUT_CHANNEL" >> $GITHUB_OUTPUT
fi
- name: Check for recent commits
id: check_commits
env:
CHANNEL: ${{ steps.channel.outputs.channel }}
FORCE_PUBLISH: ${{ inputs.force_publish }}
run: |
# Always publish for latest (production) releases
if [ "$CHANNEL" = "latest" ]; then
echo "Production release requested, proceeding with publish"
echo "skip=false" >> $GITHUB_OUTPUT
exit 0
fi
if [ "$FORCE_PUBLISH" = "true" ]; then
echo "force_publish enabled, proceeding with publish"
echo "skip=false" >> $GITHUB_OUTPUT
exit 0
fi
if [ "$(git rev-list --count HEAD --since="24 hours ago")" -eq 0 ]; then
echo "No commits in last 24 hours, skipping publish"
echo "skip=true" >> $GITHUB_OUTPUT
else
echo "Found recent commits, proceeding with publish"
echo "skip=false" >> $GITHUB_OUTPUT
fi
- name: Verify trusted publishing context
if: steps.check_commits.outputs.skip != 'true'
run: |
if [ -z "${ACTIONS_ID_TOKEN_REQUEST_TOKEN:-}" ] || [ -z "${ACTIONS_ID_TOKEN_REQUEST_URL:-}" ]; then
echo "GitHub OIDC request environment is unavailable. Ensure this job has id-token: write for npm trusted publishing."
exit 1
fi
echo "GitHub OIDC request environment is available for npm trusted publishing."
- name: Setup Bun
if: steps.check_commits.outputs.skip != 'true'
uses: oven-sh/setup-bun@v2
with:
bun-version: "1.3.13"
- name: Setup Node.js
if: steps.check_commits.outputs.skip != 'true'
uses: actions/setup-node@v4
with:
node-version: "24.x"
registry-url: "https://registry.npmjs.org"
- name: Verify publish tooling
if: steps.check_commits.outputs.skip != 'true'
run: |
NPM_VERSION=$(npm --version)
echo "npm ${NPM_VERSION}"
IFS=. read -r major minor patch <<EOF
${NPM_VERSION}
EOF
if [ "$major" -lt 11 ] || { [ "$major" -eq 11 ] && [ "$minor" -lt 5 ]; } || { [ "$major" -eq 11 ] && [ "$minor" -eq 5 ] && [ "$patch" -lt 1 ]; }; then
echo "npm 11.5.1 or newer is required for trusted publishing"
exit 1
fi
- name: Install dependencies
if: steps.check_commits.outputs.skip != 'true'
run: bun install
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Build SDK
if: steps.check_commits.outputs.skip != 'true'
run: bun run build:sdk
- name: Generate shared version
if: steps.check_commits.outputs.skip != 'true'
id: version
env:
CHANNEL: ${{ steps.channel.outputs.channel }}
run: |
BASE_VERSION=$(node -p "require('./sdk/packages/llms/package.json').version")
if [ "$CHANNEL" = "nightly" ]; then
TIMESTAMP=$(date +%s)
VERSION="${BASE_VERSION}-nightly.${TIMESTAMP}"
else
VERSION="$BASE_VERSION"
fi
echo "Base version: $BASE_VERSION"
echo "Channel: $CHANNEL"
echo "Publish version: $VERSION"
echo "version=$VERSION" >> $GITHUB_OUTPUT
- name: Update all package versions and lockfile
if: steps.check_commits.outputs.skip != 'true'
env:
VERSION: ${{ steps.version.outputs.version }}
run: bun sdk/scripts/version.ts "$VERSION"
- name: Verify publishability
if: steps.check_commits.outputs.skip != 'true'
run: bun sdk/scripts/check-publish.ts
- name: Prepare package tarball directory
if: steps.check_commits.outputs.skip != 'true'
run: mkdir -p "$RUNNER_TEMP/sdk-npm-packs"
# Pack with Bun so workspace/catalog protocols are resolved in the tarball,
# then publish that tarball with npm so npm trusted publishing can use GitHub OIDC.
# Publish sequentially in dependency order: shared → llms → agents → core → sdk
- name: Publish @cline/shared
if: steps.check_commits.outputs.skip != 'true'
env:
NPM_CONFIG_PROVENANCE: "true"
CHANNEL: ${{ steps.channel.outputs.channel }}
VERSION: ${{ steps.version.outputs.version }}
run: |
echo "Publishing @cline/shared@${VERSION} with tag '${CHANNEL}'..."
cd sdk/packages/shared
TARBALL=$(bun pm pack --destination "$RUNNER_TEMP/sdk-npm-packs" --quiet)
npm publish "$RUNNER_TEMP/sdk-npm-packs/$(basename "$TARBALL")" --tag "$CHANNEL" --access public
- name: Publish @cline/llms
if: steps.check_commits.outputs.skip != 'true'
env:
NPM_CONFIG_PROVENANCE: "true"
CHANNEL: ${{ steps.channel.outputs.channel }}
VERSION: ${{ steps.version.outputs.version }}
run: |
echo "Publishing @cline/llms@${VERSION} with tag '${CHANNEL}'..."
cd sdk/packages/llms
TARBALL=$(bun pm pack --destination "$RUNNER_TEMP/sdk-npm-packs" --quiet)
npm publish "$RUNNER_TEMP/sdk-npm-packs/$(basename "$TARBALL")" --tag "$CHANNEL" --access public
- name: Publish @cline/agents
if: steps.check_commits.outputs.skip != 'true'
env:
NPM_CONFIG_PROVENANCE: "true"
CHANNEL: ${{ steps.channel.outputs.channel }}
VERSION: ${{ steps.version.outputs.version }}
run: |
echo "Publishing @cline/agents@${VERSION} with tag '${CHANNEL}'..."
cd sdk/packages/agents
TARBALL=$(bun pm pack --destination "$RUNNER_TEMP/sdk-npm-packs" --quiet)
npm publish "$RUNNER_TEMP/sdk-npm-packs/$(basename "$TARBALL")" --tag "$CHANNEL" --access public
- name: Publish @cline/core
if: steps.check_commits.outputs.skip != 'true'
env:
NPM_CONFIG_PROVENANCE: "true"
CHANNEL: ${{ steps.channel.outputs.channel }}
VERSION: ${{ steps.version.outputs.version }}
run: |
echo "Publishing @cline/core@${VERSION} with tag '${CHANNEL}'..."
cd sdk/packages/core
TARBALL=$(bun pm pack --destination "$RUNNER_TEMP/sdk-npm-packs" --quiet)
npm publish "$RUNNER_TEMP/sdk-npm-packs/$(basename "$TARBALL")" --tag "$CHANNEL" --access public
- name: Publish @cline/sdk
if: steps.check_commits.outputs.skip != 'true'
env:
NPM_CONFIG_PROVENANCE: "true"
CHANNEL: ${{ steps.channel.outputs.channel }}
VERSION: ${{ steps.version.outputs.version }}
run: |
echo "Publishing @cline/sdk@${VERSION} with tag '${CHANNEL}'..."
cd sdk/packages/sdk
TARBALL=$(bun pm pack --destination "$RUNNER_TEMP/sdk-npm-packs" --quiet)
npm publish "$RUNNER_TEMP/sdk-npm-packs/$(basename "$TARBALL")" --tag "$CHANNEL" --access public
- name: Create package tags for production publish
if: steps.check_commits.outputs.skip != 'true' && steps.channel.outputs.channel == 'latest'
env:
VERSION: ${{ steps.version.outputs.version }}
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
for PKG in shared llms agents core sdk; do
TAG="sdk/${PKG}/v${VERSION}"
if git rev-parse -q --verify "refs/tags/${TAG}" >/dev/null; then
echo "Tag already exists locally: ${TAG}"
else
git tag -a "${TAG}" -m "@cline/${PKG}@${VERSION}"
echo "Created tag: ${TAG}"
fi
# Ensure remote has the tag; this is idempotent if tag already exists remotely.
git push origin "refs/tags/${TAG}"
done
- name: Summary
if: steps.check_commits.outputs.skip != 'true'
env:
VERSION: ${{ steps.version.outputs.version }}
CHANNEL: ${{ steps.channel.outputs.channel }}
run: |
echo "Published SDK packages with tag '${CHANNEL}':"
echo " - @cline/shared@${VERSION}"
echo " - @cline/llms@${VERSION}"
echo " - @cline/agents@${VERSION}"
echo " - @cline/core@${VERSION}"
echo " - @cline/sdk@${VERSION}"
if [ "$CHANNEL" = "latest" ]; then
echo "Created git tags:"
echo " - sdk/shared/v${VERSION}"
echo " - sdk/llms/v${VERSION}"
echo " - sdk/agents/v${VERSION}"
echo " - sdk/core/v${VERSION}"
echo " - sdk/sdk/v${VERSION}"
fi
-112
View File
@@ -1,112 +0,0 @@
name: sdk-test
on:
push:
branches:
- main
paths:
- "sdk/**"
- ".github/workflows/sdk-test.yml"
workflow_dispatch:
pull_request:
branches:
- main
paths:
- "sdk/**"
- ".github/workflows/sdk-test.yml"
workflow_call:
permissions:
contents: read
defaults:
run:
working-directory: .
jobs:
quality-checks:
runs-on: ubuntu-latest
name: Quality Checks
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: "1.3.13"
- name: Install dependencies
run: bun install
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Typecheck
run: |
bun run build:sdk
bun run -F @cline/cli build
bun run types
- name: Lint & Format
run: bun run lint
test:
needs: quality-checks
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
node-version: "24.x"
- os: windows-latest
node-version: "24.x"
runs-on: ${{ matrix.os }}
name: Test (${{ matrix.os }}, Node ${{ matrix.node-version }})
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: "1.3.13"
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- name: Install dependencies
run: bun install
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Build SDK
id: build_sdk_step
run: bun run build:sdk
- name: Build CLI
id: build_cli_step
if: ${{ !cancelled() && steps.build_sdk_step.outcome == 'success' }}
run: bun -F @cline/cli build
- name: Run Tests
if: ${{ !cancelled() && steps.build_sdk_step.outcome == 'success' && steps.build_cli_step.outcome == 'success' && matrix.os != 'windows-latest' }}
run: bun run test
- name: Run SDK Tests (Windows)
if: ${{ !cancelled() && steps.build_sdk_step.outcome == 'success' && steps.build_cli_step.outcome == 'success' && matrix.os == 'windows-latest' }}
run: bun -F './sdk/packages/**' test
- name: Smoke test SQLite under Node
if: ${{ !cancelled() && steps.build_sdk_step.outcome == 'success' && matrix.os != 'windows-latest' }}
timeout-minutes: 10
run: bun sdk/scripts/ci-node-smoke.ts
- name: Run TUI e2e tests
if: ${{ !cancelled() && steps.build_sdk_step.outcome == 'success' && steps.build_cli_step.outcome == 'success' && matrix.os == 'ubuntu-latest' && matrix.node-version == '24.x' }}
run: bun -F @cline/cli test:e2e:cli:tui
- name: Verify packages are publishable
if: ${{ !cancelled() && steps.build_sdk_step.outcome == 'success' && steps.build_cli_step.outcome == 'success' && matrix.os == 'ubuntu-latest' && matrix.node-version == '24.x' }}
run: bun sdk/scripts/check-publish.ts
+25
View File
@@ -0,0 +1,25 @@
# This workflow will only label and/or close 30 issues at a time in order to avoid exceeding a rate limit.
# More info: https://docs.github.com/en/actions/use-cases-and-examples/project-management/closing-inactive-issues
name: Close inactive issues
on:
schedule:
- cron: "30 1 * * *"
jobs:
close-issues:
runs-on: ubuntu-latest
permissions:
issues: write
pull-requests: write
steps:
- uses: actions/stale@v9
with:
days-before-issue-stale: 60
days-before-issue-close: 14
stale-issue-label: "stale"
stale-issue-message: "This issue is stale because it has been open for 60 days with no activity."
close-issue-message: "This issue was closed because it has been inactive for 14 days since being marked as stale."
days-before-pr-stale: -1
days-before-pr-close: -1
exempt-issue-labels: "pinned,security"
repo-token: ${{ secrets.GITHUB_TOKEN }}
+32
View File
@@ -0,0 +1,32 @@
name: Test Stale Issues Workflow
on:
workflow_dispatch:
inputs:
days-before-stale:
description: "Days before an issue becomes stale"
required: true
default: "1"
days-before-close:
description: "Days before a stale issue is closed"
required: true
default: "1"
jobs:
test-stale:
runs-on: ubuntu-latest
permissions:
issues: write
pull-requests: write
steps:
- uses: actions/stale@28ca103
with:
days-before-issue-stale: ${{ github.event.inputs.days-before-stale }}
days-before-issue-close: ${{ github.event.inputs.days-before-close }}
stale-issue-label: "stale"
stale-issue-message: "This issue is stale because it has been open for ${{ github.event.inputs.days-before-stale }} days with no activity."
close-issue-message: "This issue was closed because it has been inactive for ${{ github.event.inputs.days-before-close }} days since being marked as stale."
days-before-pr-stale: -1
days-before-pr-close: -1
exempt-issue-labels: "pinned,security"
repo-token: ${{ secrets.GITHUB_TOKEN }}
debug-only: true
+262
View File
@@ -0,0 +1,262 @@
name: Tests
on:
push:
branches:
- main
workflow_dispatch:
pull_request:
branches:
- main
workflow_call:
# Set default permissions for all jobs
permissions:
contents: read # Needed to check out code
checks: write # Needed to report test results
pull-requests: write # Needed to add comments/annotations to PRs
jobs:
quality-checks:
runs-on: ubuntu-latest
name: Quality Checks
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js environment
uses: actions/setup-node@v4
with:
node-version: 22
- name: Cache root dependencies
uses: actions/cache@v4
id: root-cache
with:
path: node_modules
key: ${{ runner.os }}-npm-${{ hashFiles('package-lock.json') }}
- name: Cache webview-ui dependencies
uses: actions/cache@v4
id: webview-cache
with:
path: webview-ui/node_modules
key: ${{ runner.os }}-npm-webview-${{ hashFiles('webview-ui/package-lock.json') }}
- name: Install root dependencies
if: steps.root-cache.outputs.cache-hit != 'true'
run: npm ci
- name: Install webview-ui dependencies
if: steps.webview-cache.outputs.cache-hit != 'true'
run: cd webview-ui && npm ci
- name: Run Quality Checks (Parallel)
run: npm run ci:check-all
test:
needs: quality-checks
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest]
runs-on: ${{ matrix.os }}
name: ${{ matrix.os == 'ubuntu-latest' && 'test' || format('test ({0})', matrix.os) }}
defaults:
run:
shell: bash
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js environment
uses: actions/setup-node@v4
with:
node-version: 22
- name: Cache root dependencies
uses: actions/cache@v4
id: root-cache
with:
path: node_modules
key: ${{ runner.os }}-npm-${{ hashFiles('package-lock.json') }}
- name: Cache webview-ui dependencies
uses: actions/cache@v4
id: webview-cache
with:
path: webview-ui/node_modules
key: ${{ runner.os }}-npm-webview-${{ hashFiles('webview-ui/package-lock.json') }}
- name: Install root dependencies
if: steps.root-cache.outputs.cache-hit != 'true'
run: npm ci
- name: Install webview-ui dependencies
if: steps.webview-cache.outputs.cache-hit != 'true'
run: cd webview-ui && npm ci
- name: Set up NPM on Windows
if: runner.os == 'Windows'
run: |
npm config set script-shell "C:\\Program Files\\Git\\bin\\bash.exe"
# Build the extension and tests (without redundant checks)
- name: Build Tests and Extension
id: build_step
run: npm run ci:build
- name: Unit Tests with coverage - Linux
id: unit_tests_linux
if: ${{ !cancelled() && steps.build_step.outcome == 'success' && runner.os == 'Linux' }}
run: |
npx nyc --nycrc-path .nycrc.unit.json --reporter=lcov npm run test:unit
- name: Unit Tests - Non-Linux
id: unit_tests_non_linux
if: ${{ !cancelled() && steps.build_step.outcome == 'success' && runner.os != 'Linux' }}
run: |
npm run test:unit
- name: Extension Integration Tests - Linux
id: integration_tests_linux
if: ${{ !cancelled() && steps.build_step.outcome == 'success' && runner.os == 'Linux' }}
run: xvfb-run -a npm run test:coverage
- name: Extension Integration Tests - Non-Linux
id: integration_tests_non_linux
if: ${{ !cancelled() && steps.build_step.outcome == 'success' && runner.os != 'Linux' }}
run: npm run test:integration
- name: Webview Tests with Coverage
id: webview_tests
if: ${{ !cancelled() && steps.build_step.outcome == 'success' }}
run: |
cd webview-ui
npm run test:coverage
- name: Save Coverage Reports
uses: actions/upload-artifact@v4
# Only upload artifacts on Linux - We only need coverage from one OS
if: runner.os == 'Linux'
with:
name: pr-coverage-reports
path: |
coverage-unit/lcov.info
webview-ui/coverage/lcov.info
test-platform-integration:
needs: quality-checks
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js environment
uses: actions/setup-node@v4
with:
node-version: 22
- name: Cache root dependencies
uses: actions/cache@v4
id: root-cache
with:
path: node_modules
key: ${{ runner.os }}-npm-${{ hashFiles('package-lock.json') }}
- name: Cache webview-ui dependencies
uses: actions/cache@v4
id: webview-cache
with:
path: webview-ui/node_modules
key: ${{ runner.os }}-npm-webview-${{ hashFiles('webview-ui/package-lock.json') }}
# Cache testing-platform dependencies
- name: Cache testing-platform dependencies
uses: actions/cache@v4
id: testing-platform-cache
with:
path: testing-platform/node_modules
key: ${{ runner.os }}-npm-testing-platform-${{ hashFiles('testing-platform/package-lock.json') }}
- name: Install root dependencies
if: steps.root-cache.outputs.cache-hit != 'true'
run: npm ci
- name: Install webview-ui dependencies
if: steps.webview-cache.outputs.cache-hit != 'true'
run: cd webview-ui && npm ci
- name: Download ripgrep binaries
run: npm run download-ripgrep
- name: Compile Standalone
run: npm run compile-standalone
- name: Install testing platform dependencies
if: steps.testing-platform-cache.outputs.cache-hit != 'true'
run: cd testing-platform && npm ci
- name: Running testing platform integration spec tests
continue-on-error: true
timeout-minutes: 7
# Temporarily wrapping the test command to always return a neutral exit code.
# This prevents the job from showing as failed and avoids distracting developers
# until the integration tests are ready to be enforced.
run: |
npm run test:tp-orchestrator -- tests/specs/ --count=1 --coverage
- name: Save Coverage Reports
uses: actions/upload-artifact@v4
with:
name: test-platform-integration-core-coverage
path: coverage/**/lcov.info
qlty:
needs: [test, test-platform-integration]
runs-on: ubuntu-latest
# Run on PRs to main, pushes to main, and manual dispatches
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Download unit tests coverage reports
uses: actions/download-artifact@v4
with:
name: pr-coverage-reports
path: .
- name: Upload core unit tests coverage to Qlty
uses: qltysh/qlty-action/coverage@v2
with:
token: ${{ secrets.QLTY_COVERAGE_TOKEN }}
# we can merge multiple files if necessary
files: |
coverage-unit/lcov.info
tag: unit:core
- name: Upload webview-ui unit tests coverage to Qlty
uses: qltysh/qlty-action/coverage@v2
with:
token: ${{ secrets.QLTY_COVERAGE_TOKEN }}
# we can merge multiple files if necessary
files: |
webview-ui/coverage/lcov.info
tag: unit:webview-ui
add-prefix: webview-ui/
- name: Download test platform integration core coverage artifact
uses: actions/download-artifact@v4
continue-on-error: true
id: download-integration-coverage
with:
name: test-platform-integration-core-coverage
path: integration-core-coverage-reports
- name: Upload core integration tests coverage to Qlty
if: steps.download-integration-coverage.outcome == 'success'
uses: qltysh/qlty-action/coverage@v2
with:
token: ${{ secrets.QLTY_COVERAGE_TOKEN }}
files: integration-core-coverage-reports/**/lcov.info
tag: integration:core
@@ -0,0 +1,97 @@
name: Trigger Jetbrains Plugin <-> Cline Tests
on:
pull_request_target:
types: [opened, reopened]
issue_comment:
types: [created]
permissions:
contents: read
pull-requests: read
concurrency:
group: jetbrains-trigger-${{ github.event.pull_request.number || github.event.issue.number }}
cancel-in-progress: true
jobs:
trigger-integration-test:
name: Run Tests
runs-on: ubuntu-latest
# Run on PR open/reopen, or when someone comments /test-jetbrains on a PR
if: |
github.event_name == 'pull_request_target' ||
(github.event_name == 'issue_comment' &&
github.event.issue.pull_request &&
contains(github.event.comment.body, '/test-jetbrains'))
steps:
- name: Generate GitHub App Token
id: app-token
uses: actions/create-github-app-token@v1
with:
app-id: 1998650
private-key: ${{ secrets.CLINE_JETBRAINS_WORKFLOW_KEY }}
owner: cline
repositories: intellij-plugin
- name: Get PR details (for issue_comment trigger)
id: pr-details
if: github.event_name == 'issue_comment'
env:
GH_TOKEN: ${{ github.token }}
run: |
PR_DATA=$(gh api repos/${{ github.repository }}/pulls/${{ github.event.issue.number }})
echo "head_ref=$(echo "$PR_DATA" | jq -r '.head.ref')" >> $GITHUB_OUTPUT
echo "head_sha=$(echo "$PR_DATA" | jq -r '.head.sha')" >> $GITHUB_OUTPUT
echo "title=$(echo "$PR_DATA" | jq -r '.title')" >> $GITHUB_OUTPUT
echo "html_url=$(echo "$PR_DATA" | jq -r '.html_url')" >> $GITHUB_OUTPUT
- name: Sanitize untrusted inputs
id: sanitize
env:
RAW_BRANCH_NAME: ${{ github.event_name == 'pull_request_target' && github.head_ref || steps.pr-details.outputs.head_ref }}
RAW_PR_TITLE: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.title || steps.pr-details.outputs.title }}
run: |
# Sanitize branch name for JSON
BRANCH_NAME_JSON=$(jq -n --arg b "$RAW_BRANCH_NAME" '$b')
echo "branch_name=$BRANCH_NAME_JSON" >> $GITHUB_OUTPUT
# Sanitize PR title for JSON
PR_TITLE_JSON=$(jq -n --arg t "$RAW_PR_TITLE" '$t')
echo "pr_title=$PR_TITLE_JSON" >> $GITHUB_OUTPUT
- name: Trigger IntelliJ Plugin Integration Test
env:
BRANCH_NAME: ${{ steps.sanitize.outputs.branch_name }}
PR_TITLE: ${{ steps.sanitize.outputs.pr_title }}
PR_NUMBER: ${{ github.event.pull_request.number || github.event.issue.number }}
PR_SHA: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.head.sha || steps.pr-details.outputs.head_sha }}
PR_URL: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.html_url || steps.pr-details.outputs.html_url }}
run: |
curl -X POST \
-H "Authorization: Bearer ${{ steps.app-token.outputs.token }}" \
-H "Accept: application/vnd.github.v3+json" \
-H "User-Agent: cline-pr-trigger" \
-H "Content-Type: application/json" \
https://api.github.com/repos/cline/intellij-plugin/dispatches \
-d @- <<EOF
{
"event_type": "cline-pr-check",
"client_payload": {
"pr_number": "$PR_NUMBER",
"branch_name": $BRANCH_NAME,
"action": "${{ github.event.action }}",
"sha": "$PR_SHA",
"pr_title": $PR_TITLE,
"pr_url": "$PR_URL"
}
}
EOF
- name: Log trigger details
env:
PR_NUMBER: ${{ github.event.pull_request.number || github.event.issue.number }}
PR_SHA: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.head.sha || steps.pr-details.outputs.head_sha }}
run: |
echo "Triggered IntelliJ Plugin integration test for:"
echo " PR #$PR_NUMBER"
echo " Trigger: ${{ github.event_name }}"
echo " Action: ${{ github.event.action }}"
echo " SHA: $PR_SHA"
+5 -39
View File
@@ -17,8 +17,8 @@ pnpm-lock.yaml
.actrc
CLAUDE.local.md
apps/vscode/webview-ui/src/**/*.js
apps/vscode/webview-ui/src/**/*.js.map
webview-ui/src/**/*.js
webview-ui/src/**/*.js.map
# Ignore coverage directories and files
coverage
@@ -35,10 +35,9 @@ coverage-unit
.worktrees
## Generated files ##
apps/vscode/src/generated/
apps/vscode/src/shared/proto/
apps/vscode/webview-ui/src/services/grpc-client.ts
*.tsbuildinfo
src/generated/
src/shared/proto/
webview-ui/src/services/grpc-client.ts
# E2E Tests
test-results
@@ -48,36 +47,3 @@ test-results
.secrets
*.tsbuildinfo
# Smoke test results (generated)
evals/smoke-tests/results/
.tui-test
secrets.json
tui-traces
tests/**/cache
# Backup created by scripts/marketplace-readme.mjs while publishing.
# Should never be committed: only exists if a publish aborts mid-swap.
.README.github.bak
# Tauri generated code
apps/*/src-tauri/gen
apps/*/src-tauri/bin
apps/examples/*/src-tauri/gen
apps/examples/*/src-tauri/bin
# Tauri UI test snapshots
apps/*/src/tests/.tui-test
apps/*/src/tests/tui-traces
apps/vscode/webview-ui/src/**/*.js
apps/vscode/webview-ui/src/**/*.js.map
# SDK Session files / User data
.cline/data
.cline/tmp
*.db
*.db-shm
*.db-wal
.cline/**/managed.json
.cline/**/bundle.json
-4
View File
@@ -1,4 +0,0 @@
title = "Cline SDK secret scanning"
[extend]
useDefault = true
-3
View File
@@ -1,3 +0,0 @@
[submodule "evals/cline-bench"]
path = evals/cline-bench
url = https://github.com/cline/cline-bench.git
-52
View File
@@ -1,52 +0,0 @@
{
"strictness": 2,
"triggerOnUpdates": true,
"statusCheck": true,
"rules": [
{
"id": "sdk-tool-handler-telemetry",
"rule": "Any new tool handler added to packages/agents/src or packages/core/src that performs a user-visible action (writes files, executes commands, modifies state, calls external APIs) must include a call to captureToolUsage() from packages/core/src/services/telemetry/core-events.ts, or emit a task.tool_used event via telemetry.capture(). Pure read-only helpers and getters are exempt. When in doubt, prefer instrumentation.",
"scope": [
"sdk/packages/agents/src/**",
"sdk/packages/core/src/**"
],
"severity": "high"
},
{
"id": "sdk-session-lifecycle-telemetry",
"rule": "New session start, end, or state-transition code paths in packages/core/src must call the appropriate typed helper from packages/core/src/services/telemetry/core-events.ts (captureTaskCreated, captureTaskCompleted, captureConversationTurnEvent, captureTokenUsage, etc.). Do not inline raw telemetry.capture() calls for session lifecycle events — always use the typed helper, which guarantees a consistent payload shape.",
"scope": [
"sdk/packages/core/src/cline-core/**",
"sdk/packages/core/src/runtime/**"
],
"severity": "high"
},
{
"id": "sdk-no-raw-event-strings",
"rule": "All telemetry event name strings must be sourced from CORE_TELEMETRY_EVENTS in packages/core/src/services/telemetry/core-events.ts. If a PR introduces a string literal in a telemetry.capture(), telemetry.captureRequired(), or recordCounter()/recordHistogram()/recordGauge() call that does not reference CORE_TELEMETRY_EVENTS, flag it. New events must be added to CORE_TELEMETRY_EVENTS first, with a typed capture helper created alongside them.",
"scope": [
"sdk/packages/core/src/**",
"sdk/packages/agents/src/**",
"apps/cli/src/**",
"apps/vscode/src/**"
],
"severity": "medium"
},
{
"id": "sdk-auth-telemetry-completeness",
"rule": "Any new OAuth or authentication provider added under packages/core/src/auth must emit all four lifecycle events using the typed helpers from core-events.ts: captureAuthStarted (at flow entry), captureAuthSucceeded + identifyAccount (on token success), captureAuthFailed (on error), and captureAuthLoggedOut (on token invalidation or explicit logout). Flag PRs that introduce a new auth flow file without all four. Cross-reference packages/core/src/auth/cline.ts and packages/core/src/auth/codex.ts as canonical examples.",
"scope": [
"sdk/packages/core/src/auth/**"
],
"severity": "high"
},
{
"id": "sdk-telemetry-doc-update",
"rule": "Any PR that adds new event constants to CORE_TELEMETRY_EVENTS in packages/core/src/services/telemetry/core-events.ts, adds new capture* helper functions, or changes the payload shape of an existing event must update the Event Catalog section in DOC.md. Flag PRs that modify core-events.ts without a corresponding change to DOC.md.",
"scope": [
"sdk/packages/core/src/services/telemetry/core-events.ts"
],
"severity": "medium"
}
]
}
-32
View File
@@ -1,32 +0,0 @@
{
"files": [
{
"path": "sdk/packages/core/src/services/telemetry/core-events.ts",
"description": "Single source of truth for all telemetry event names (CORE_TELEMETRY_EVENTS) and their typed capture helper functions. Every PR touching telemetry must be evaluated against this catalog. New events must be defined here first."
},
{
"path": "sdk/packages/shared/src/services/telemetry.ts",
"description": "ITelemetryService interface definition. Defines the contract all telemetry implementations must satisfy (capture, captureRequired, recordCounter, recordHistogram, recordGauge, flush, dispose)."
},
{
"path": "sdk/packages/core/src/services/telemetry/TelemetryService.ts",
"description": "Reference implementation of ITelemetryService used by all hosts. Multi-adapter fan-out service that forwards events to OpenTelemetry."
},
{
"path": "sdk/packages/core/src/services/telemetry/OpenTelemetryProvider.ts",
"description": "OpenTelemetry-backed provider that wires logs/metrics/traces exporters. Contains createConfiguredTelemetryService and createConfiguredTelemetryHandle, the canonical factories every host should use."
},
{
"path": "DOC.md",
"description": "Public API and event documentation. The Event Catalog and 'Activation funnel' sections must be kept in sync with core-events.ts. Host integration rules (CLI dir ordering, hub daemon metadata forwarding) are documented here."
},
{
"path": "sdk/ARCHITECTURE.md",
"description": "Architecture reference. Telemetry design decisions, completion semantics (submit_and_exit anchoring), and hub-daemon telemetry forwarding are documented here. Use as ground truth for design intent."
},
{
"path": "sdk/AGENTS.md",
"description": "Package boundary rules. Telemetry runtime services live in @cline/core; @cline/agents must not own stateful telemetry. Use to evaluate whether a telemetry change is being made in the correct package."
}
]
}
-142
View File
@@ -1,142 +0,0 @@
# SDK Telemetry Standards
These rules supplement `config.json`. The structured rules describe **what** to enforce; this
document explains **why**, so Greptile has the context to avoid false positives.
## Telemetry Stack
The SDK uses OpenTelemetry (OTEL) as its sole telemetry transport. Events flow through:
```
core-events.ts (event catalog + typed helpers)
ITelemetryService (sdk/packages/shared) ← interface contract
TelemetryService (sdk/packages/core) ← multi-adapter fan-out
OpenTelemetryAdapter → OpenTelemetryProvider ← OTLP transport
OTLP endpoint (collector or vendor)
```
The SDK does **not** depend on the original `cline/cline` repo for telemetry. The two have
parallel-but-independent stacks; this `.greptile/` config covers only the SDK.
## The Single Source of Truth
`sdk/packages/core/src/services/telemetry/core-events.ts` is the single source of truth for all
event names. It exports:
- `CORE_TELEMETRY_EVENTS` — a frozen const object grouped by family
(`CLIENT`, `SESSION`, `USER`, `TASK`, `HOOKS`, `WORKSPACE`)
- A typed `capture*()` helper for every event family
(`captureExtensionActivated`, `captureTaskCreated`, `captureToolUsage`, etc.)
**Never use raw string literals for event names at call sites.** A new event always means:
1. Add the constant to `CORE_TELEMETRY_EVENTS`
2. Add a typed `capture*()` helper alongside it (with a typed `properties` parameter)
3. Update the Event Catalog section in `DOC.md`
4. Add a unit test in `core-events.test.ts` asserting the event is dropped when telemetry is opted out
## The Activation Funnel
The canonical funnel that downstream analytics depends on:
```
user.extension_activated
→ workspace.initialized
→ workspace.path_resolved (gated on multi-root)
→ task.created
→ task.conversation_turn (one per turn, source: "user" | "assistant")
→ task.completed (source: "submit_and_exit" | "shutdown")
```
Emission ownership:
- `user.extension_activated`: emitted **once per host process** by host-specific helpers
(`captureCliExtensionActivated` for the CLI, `captureExtensionActivated` for VS Code).
- `workspace.initialized` / `workspace.init_error`: emitted by a per-process de-duplicated
emitter in `prepareLocalRuntimeBootstrap`. Hosts must NOT re-emit these.
- `workspace.path_resolved`: emitted from default tool executors **only when**
`WorkspaceManager` exposes more than one root.
- `task.*`: emitted by core session lifecycle code in `sdk/packages/core/src/cline-core/` and
`sdk/packages/core/src/runtime/`. Hosts must not duplicate this emission.
## `task.completed` Semantics
`task.completed` marks the moment the **assistant declared the task done**, not the moment
the SDK session record was finalized. The local runtime emits it when it observes a successful
`submit_and_exit` tool call (the SDK analog of original Cline's `attempt_completion`). For
non-interactive runs that finish without invoking the explicit completion tool,
`shutdownSession` emits it as a fallback with `source: "shutdown"`.
Each session is guaranteed at most one `task.completed` emission. The `source` field
(`"submit_and_exit" | "shutdown"`) is required for analytics attribution.
## CLI Directory-Ordering Rule
The CLI accepts `--config <dir>`. The CLI **must** apply `setClineDir(...)` and
`setHomeDir(...)` from `@cline/shared/storage` **before** calling
`captureCliExtensionActivated()`. Otherwise the telemetry singleton's persisted distinct-id
and any other on-disk telemetry state lands under `~/.cline` instead of the user's chosen
config dir.
The canonical pattern is in `apps/cli/src/main.ts` (PR #357):
```ts
if (configDir) setClineDir(configDir);
setHomeDir(homedir());
captureCliExtensionActivated(); // <-- after dir overrides
```
## Hub Daemon Metadata Forwarding
Hosts that spawn a detached `@cline/core/hub/daemon-entry` process must forward telemetry
metadata into the daemon argv so the daemon can reconstruct an equivalent
`ITelemetryService`. The expected payload is base64-encoded JSON with snake_case keys:
```
{ extension_version, cline_type, platform, platform_version, os_type, os_version, is_remote_workspace }
```
The reference implementation is `apps/vscode/src/hub-daemon.ts` (PR #357). Without this
forwarding, hub-backed sessions silently drop their lifecycle telemetry.
## Auth Lifecycle Completeness
Every authentication provider in `sdk/packages/core/src/auth/` must emit all four auth lifecycle
events using the typed helpers:
| Phase | Helper | Where it fires |
|---|---|---|
| Flow entry | `captureAuthStarted(provider)` | Top of the OAuth flow function |
| Token success | `captureAuthSucceeded(provider)` + `identifyAccount(...)` | After successful token exchange |
| Token error | `captureAuthFailed(provider, errorMessage)` | In the catch block |
| Token invalidation | `captureAuthLoggedOut(provider, reason)` | On invalid_grant or explicit logout |
Cross-reference `sdk/packages/core/src/auth/cline.ts` and `sdk/packages/core/src/auth/codex.ts` as
canonical examples of all four phases.
## Single Telemetry Service Per Host
On VS Code, the telemetry handle is built **once** in `activate()`
(`apps/vscode/src/telemetry.ts`) and the same instance is passed into the sidebar, panel
command, and daemon spawn payload. Do not let individual controllers construct their own
`ITelemetryService` — that fragments distinct-id state, opt-out tracking, and flush ownership.
The CLI follows the same pattern via the `getCliTelemetryService()` singleton in
`apps/cli/src/utils/telemetry.ts`, which is memoized by the activation gate in
`telemetry.activation-gate.ts`.
## Common False-Positive Adjustments
If Greptile flags one of the following, the rule is **not** violated:
- A telemetry call that is wrapped in a host-specific helper (e.g.
`captureCliExtensionActivated` wrapping `captureExtensionActivated`) — the inner helper
is the typed call.
- `enterprise.*` events emitted from `apps/cli/src/utils/enterprise.ts` — these are
enterprise-side events not yet in `CORE_TELEMETRY_EVENTS`; they are tracked separately.
- A new test file that uses raw event name strings inside `expect(...)` assertions — tests
may reference event names as strings to assert what was emitted.
-9
View File
@@ -1,10 +1 @@
if ! command -v gitleaks >/dev/null 2>&1; then
echo "gitleaks is required for the pre-commit secret scan."
echo "Install it with: brew install gitleaks"
echo "Other install options: https://github.com/gitleaks/gitleaks#installing"
exit 1
fi
gitleaks git --pre-commit --redact --staged --verbose || exit 1
lint-staged
-9
View File
@@ -1,9 +0,0 @@
{
"shortcuts": [
{
"label": "Build & Link CLI",
"command": "bun -F @cline/cli build && bun -F @cline/cli link",
"icon": "play"
}
]
}
+15
View File
@@ -0,0 +1,15 @@
{
"extension": [
"ts"
],
"spec": [
"src/**/__tests__/*.ts"
],
"require": [
"ts-node/register",
"source-map-support/register",
"./src/test/requires.ts"
],
"recursive": true,
"exit": true
}
+1 -1
View File
@@ -1 +1 @@
22
lts/*
-3
View File
@@ -1,3 +0,0 @@
bun 1.3.13
node 22
@@ -1,6 +1,5 @@
import { defineConfig } from "@vscode/test-cli"
import path from "path"
const vscodeTestVersion = process.env.VSCODE_TEST_VERSION ?? "stable"
export default defineConfig({
files: "{out/**/*.test.js,src/**/*.test.js,!src/test/e2e/**/*.test.js,!out/src/test/e2e/**/*.test.js}",
@@ -13,7 +12,7 @@ export default defineConfig({
require: ["./test-setup.js"],
},
workspaceFolder: "test-workspace",
version: vscodeTestVersion,
version: "stable",
extensionDevelopmentPath: path.resolve("./"),
launchArgs: ["--disable-extensions"],
})
+1 -2
View File
@@ -5,7 +5,6 @@
"connor4312.esbuild-problem-matchers",
"ms-vscode.extension-test-runner",
"bradlc.vscode-tailwindcss",
"biomejs.biome",
"oven.bun-vscode"
"biomejs.biome"
]
}
+31 -145
View File
@@ -10,23 +10,22 @@
"type": "extensionHost",
"request": "launch",
"args": [
"--extensionDevelopmentPath=${workspaceFolder}/apps/vscode",
"--extensionDevelopmentPath=${workspaceFolder}",
"--disable-workspace-trust",
"--disable-extension",
"saoudrizwan.claude-dev", // Avoid conflicts with installed Cline
"--disable-extension",
"saoudrizwan.cline-nightly", // Avoid conflicts with installed Cline Nightly
"${workspaceFolder}/apps/vscode",
"--disable-extensions"
"${workspaceFolder}"
],
"outFiles": [
"${workspaceFolder}/apps/vscode/dist/**/*.js"
"${workspaceFolder}/dist/**/*.js"
],
"preLaunchTask": "${defaultBuildTask}",
"envFile": "${workspaceFolder}/apps/vscode/.env",
"envFile": "${workspaceFolder}/.env",
"env": {
"IS_DEV": "true",
"DEV_WORKSPACE_FOLDER": "${workspaceFolder}/apps/vscode",
"DEV_WORKSPACE_FOLDER": "${workspaceFolder}",
"CLINE_ENVIRONMENT": "production"
}
},
@@ -35,22 +34,22 @@
"type": "extensionHost",
"request": "launch",
"args": [
"--extensionDevelopmentPath=${workspaceFolder}/apps/vscode",
"--extensionDevelopmentPath=${workspaceFolder}",
"--disable-workspace-trust",
"--disable-extension",
"saoudrizwan.claude-dev", // Avoid conflicts with installed Cline
"--disable-extension",
"saoudrizwan.cline-nightly", // Avoid conflicts with installed Cline Nightly
"${workspaceFolder}/apps/vscode"
"${workspaceFolder}"
],
"outFiles": [
"${workspaceFolder}/apps/vscode/dist/**/*.js"
"${workspaceFolder}/dist/**/*.js"
],
"preLaunchTask": "${defaultBuildTask}",
"envFile": "${workspaceFolder}/apps/vscode/.env",
"envFile": "${workspaceFolder}/.env",
"env": {
"IS_DEV": "true",
"DEV_WORKSPACE_FOLDER": "${workspaceFolder}/apps/vscode",
"DEV_WORKSPACE_FOLDER": "${workspaceFolder}",
"CLINE_ENVIRONMENT": "staging"
}
},
@@ -59,22 +58,22 @@
"type": "extensionHost",
"request": "launch",
"args": [
"--extensionDevelopmentPath=${workspaceFolder}/apps/vscode",
"--extensionDevelopmentPath=${workspaceFolder}",
"--disable-workspace-trust",
"--disable-extension",
"saoudrizwan.claude-dev", // Avoid conflicts with installed Cline
"--disable-extension",
"saoudrizwan.cline-nightly", // Avoid conflicts with installed Cline Nightly
"${workspaceFolder}/apps/vscode"
"${workspaceFolder}"
],
"outFiles": [
"${workspaceFolder}/apps/vscode/dist/**/*.js"
"${workspaceFolder}/dist/**/*.js"
],
"preLaunchTask": "${defaultBuildTask}",
"envFile": "${workspaceFolder}/apps/vscode/.env",
"envFile": "${workspaceFolder}/.env",
"env": {
"IS_DEV": "true",
"DEV_WORKSPACE_FOLDER": "${workspaceFolder}/apps/vscode",
"DEV_WORKSPACE_FOLDER": "${workspaceFolder}",
"CLINE_ENVIRONMENT": "local"
}
},
@@ -84,27 +83,27 @@
"request": "launch",
"runtimeExecutable": "${execPath}",
"args": [
"--user-data-dir=${workspaceFolder}/apps/vscode/dist/tmp/user",
"--user-data-dir=${workspaceFolder}/dist/tmp/user",
"--profile-temp",
"--sync=off",
"--disable-extension",
"saoudrizwan.claude-dev", // Avoid conflicts with installed Cline
"--disable-extension",
"saoudrizwan.cline-nightly", // Avoid conflicts with installed Cline Nightly
"--extensionDevelopmentPath=${workspaceFolder}/apps/vscode",
"${workspaceFolder}/apps/vscode"
"--extensionDevelopmentPath=${workspaceFolder}",
"${workspaceFolder}"
],
"outFiles": [
"${workspaceFolder}/apps/vscode/dist/**/*.js"
"${workspaceFolder}/dist/**/*.js"
],
"preLaunchTask": "clean-tmp-user",
"internalConsoleOptions": "openOnSessionStart",
"postDebugTask": "stop",
"envFile": "${workspaceFolder}/apps/vscode/.env",
"envFile": "${workspaceFolder}/.env",
"env": {
"IS_DEV": "true",
"TEMP_PROFILE": "true",
"DEV_WORKSPACE_FOLDER": "${workspaceFolder}/apps/vscode",
"DEV_WORKSPACE_FOLDER": "${workspaceFolder}",
"CLINE_ENVIRONMENT": "production"
}
},
@@ -117,13 +116,13 @@
],
"sourceMaps": true,
"resolveSourceMapLocations": [
"${workspaceFolder}/apps/vscode/**",
"${workspaceFolder}/**",
"!**/node_modules/**"
],
"cwd": "${workspaceFolder}/apps/vscode",
"cwd": "${workspaceFolder}",
"outFiles": [
"${workspaceFolder}/apps/vscode/dist/**/*.js",
"${workspaceFolder}/apps/vscode/dist-standalone/**/*.js"
"${workspaceFolder}/dist/**/*.js",
"${workspaceFolder}/dist-standalone/**/*.js"
],
"preLaunchTask": "compile-standalone",
"runtimeExecutable": "npx",
@@ -131,11 +130,11 @@
"tsx"
],
"program": "scripts/test-standalone-core-api-server.ts",
"envFile": "${workspaceFolder}/apps/vscode/.env",
"envFile": "${workspaceFolder}/.env",
"env": {
"PROTOBUS_PORT": "26040",
"HOSTBRIDGE_PORT": "26041",
"WORKSPACE_DIR": "${workspaceFolder}/apps/vscode",
"WORKSPACE_DIR": "${workspaceFolder}",
"E2E_TEST": "true",
"CLINE_ENVIRONMENT": "local"
},
@@ -151,10 +150,10 @@
],
"sourceMaps": true,
"resolveSourceMapLocations": [
"${workspaceFolder}/apps/vscode/**",
"${workspaceFolder}/**",
"!**/node_modules/**"
],
"cwd": "${workspaceFolder}/apps/vscode",
"cwd": "${workspaceFolder}",
"runtimeExecutable": "npx",
"runtimeArgs": [
"mocha"
@@ -169,7 +168,7 @@
"--exit",
"${file}"
],
"envFile": "${workspaceFolder}/apps/vscode/.env",
"envFile": "${workspaceFolder}/.env",
"env": {
"TS_NODE_PROJECT": "./tsconfig.unit-test.json",
"NODE_ENV": "test",
@@ -188,7 +187,7 @@
"run",
"storybook"
],
"cwd": "${workspaceFolder}/apps/vscode/webview-ui",
"cwd": "${workspaceFolder}/webview-ui",
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen",
"serverReadyAction": {
@@ -199,119 +198,6 @@
"env": {
"IS_DEV": "true"
}
},
{
"name": "Launch Bun CLI (Prompt)",
"type": "bun",
"request": "launch",
"cwd": "${workspaceFolder}/sdk/apps/cli",
"runtime": "bun",
"runtimeArgs": [
"--conditions=development"
],
"program": "${workspaceFolder}/sdk/apps/cli/src/index.ts",
"args": [
"${input:cliPrompt}"
],
"env": {
"CLINE_BUILD_ENV": "development"
},
"presentation": {
"hidden": true
}
},
{
"name": "Launch RPC Server",
"type": "bun",
"request": "launch",
"cwd": "${workspaceFolder}/sdk/apps/cli",
"runtime": "bun",
"runtimeArgs": [
"--conditions=development"
],
"program": "${workspaceFolder}/sdk/apps/cli/src/index.ts",
"args": [
"rpc",
"start"
],
"env": {
"CLINE_BUILD_ENV": "development",
"CLINE_DEBUG_PORT_BASE": "9230"
},
"presentation": {
"hidden": true
}
},
{
"name": "Attach RPC Runtime (9230)",
"type": "bun",
"request": "attach",
"url": "ws://127.0.0.1:9230",
"localRoot": "${workspaceFolder}/sdk",
"remoteRoot": "${workspaceFolder}/sdk",
"presentation": {
"hidden": true
}
},
{
"name": "Attach Hook Worker (9231)",
"type": "bun",
"request": "attach",
"url": "ws://127.0.0.1:9231",
"localRoot": "${workspaceFolder}/sdk",
"remoteRoot": "${workspaceFolder}/sdk",
"presentation": {
"hidden": true
}
},
{
"name": "Attach Plugin Sandbox (9232)",
"type": "bun",
"request": "attach",
"url": "ws://127.0.0.1:9232",
"localRoot": "${workspaceFolder}/sdk",
"remoteRoot": "${workspaceFolder}/sdk",
"presentation": {
"hidden": true
}
},
{
"name": "Attach Connector Child (9233)",
"type": "bun",
"request": "attach",
"url": "ws://127.0.0.1:9233",
"localRoot": "${workspaceFolder}/sdk",
"remoteRoot": "${workspaceFolder}/sdk",
"presentation": {
"hidden": true
}
}
],
"compounds": [
{
"name": "Launch RPC Server Debugger",
"configurations": [
"Launch RPC Server",
"Attach RPC Runtime (9230)"
]
},
{
"name": "Launch CLI Debugger",
"configurations": [
"Launch Bun CLI (Prompt)",
"Attach RPC Runtime (9230)",
"Attach Hook Worker (9231)",
"Attach Plugin Sandbox (9232)",
"Attach Connector Child (9233)"
]
}
],
"inputs": [
{
"id": "cliPrompt",
"type": "promptString",
"description": "Prompt to send to the CLI",
"default": "hey"
}
]
}
+1 -2
View File
@@ -1,6 +1,5 @@
// Place your settings in this file to overwrite default and user settings.
{
"files.insertFinalNewline": true,
"files.exclude": {
"out": false, // set this to true to hide the "out" folder with the compiled JS files
"dist": false // set this to true to hide the "dist" folder with the compiled JS files
@@ -17,7 +16,7 @@
// Protobuf settings
"protoc": {
"options": [
"--proto_path=apps/vscode/proto"
"--proto_path=proto"
]
},
// Enable Lint and format using Biome
+22 -50
View File
@@ -5,28 +5,24 @@
"tasks": [
{
"label": "compile-standalone",
"type": "shell",
"command": "npm run compile-standalone",
"type": "npm",
"script": "compile-standalone",
"group": "build",
"problemMatcher": [],
"presentation": {
"reveal": "always"
},
"options": {
"cwd": "${workspaceFolder}/apps/vscode"
}
},
{
"label": "npm: protos",
"type": "shell",
"command": "npm run protos",
"type": "npm",
"script": "protos",
"problemMatcher": [],
"isBackground": false,
"presentation": {
"reveal": "always"
},
"options": {
"cwd": "${workspaceFolder}/apps/vscode",
"env": {
"IS_DEV": "true"
}
@@ -64,8 +60,8 @@
"group": "build"
},
{
"type": "shell",
"command": "npm run build:webview",
"type": "npm",
"script": "build:webview",
"group": "build",
"problemMatcher": [],
"isBackground": true,
@@ -78,15 +74,14 @@
"reveal": "always"
},
"options": {
"cwd": "${workspaceFolder}/apps/vscode",
"env": {
"IS_DEV": "true"
}
}
},
{
"type": "shell",
"command": "npm run build:webview:test",
"type": "npm",
"script": "build:webview:test",
"group": "build",
"problemMatcher": [],
"isBackground": true,
@@ -99,7 +94,6 @@
"reveal": "always"
},
"options": {
"cwd": "${workspaceFolder}/apps/vscode",
"env": {
"IS_DEV": "true",
"IS_TEST": "true"
@@ -107,8 +101,8 @@
}
},
{
"type": "shell",
"command": "npm run dev:webview",
"type": "npm",
"script": "dev:webview",
"group": "build",
"problemMatcher": [
{
@@ -137,15 +131,14 @@
"reveal": "always"
},
"options": {
"cwd": "${workspaceFolder}/apps/vscode",
"env": {
"IS_DEV": "true"
}
}
},
{
"type": "shell",
"command": "npm run watch:esbuild",
"type": "npm",
"script": "watch:esbuild",
"group": "build",
"problemMatcher": {
"pattern": [
@@ -176,15 +169,14 @@
"reveal": "always"
},
"options": {
"cwd": "${workspaceFolder}/apps/vscode",
"env": {
"IS_DEV": "true"
}
}
},
{
"type": "shell",
"command": "npm run watch:esbuild:test",
"type": "npm",
"script": "watch:esbuild:test",
"group": "build",
"problemMatcher": {
"pattern": [
@@ -215,7 +207,6 @@
"reveal": "always"
},
"options": {
"cwd": "${workspaceFolder}/apps/vscode",
"env": {
"IS_DEV": "true",
"IS_TEST": "true"
@@ -223,8 +214,8 @@
}
},
{
"type": "shell",
"command": "npm run watch:tsc",
"type": "npm",
"script": "watch:tsc",
"group": "build",
"problemMatcher": "$tsc-watch",
"isBackground": true,
@@ -235,15 +226,11 @@
"presentation": {
"group": "watch",
"reveal": "always"
},
"options": {
"cwd": "${workspaceFolder}/apps/vscode"
}
},
{
"type": "shell",
"command": "npm run watch-tests",
"label": "npm: watch-tests",
"type": "npm",
"script": "watch-tests",
"problemMatcher": "$tsc-watch",
"isBackground": true,
"dependsOn": [
@@ -253,10 +240,7 @@
"reveal": "always",
"group": "watchers"
},
"group": "build",
"options": {
"cwd": "${workspaceFolder}/apps/vscode"
}
"group": "build"
},
{
"label": "tasks: watch-tests",
@@ -278,11 +262,11 @@
"dependsOn": [
"watch"
],
"command": "rm -rf ${workspaceFolder}/apps/vscode/dist/tmp/user && mkdir -p ${workspaceFolder}/apps/vscode/dist/tmp/user"
"command": "rm -rf ${workspaceFolder}/dist/tmp/user && mkdir -p ${workspaceFolder}/dist/tmp/user"
},
{
"type": "shell",
"command": "npm run storybook",
"type": "npm",
"script": "storybook",
"group": "build",
"problemMatcher": [],
"isBackground": false,
@@ -295,22 +279,10 @@
"reveal": "always"
},
"options": {
"cwd": "${workspaceFolder}/apps/vscode",
"env": {
"IS_DEV": "true"
}
}
},
{
"label": "build-sdk",
"type": "shell",
"command": "bun run build:sdk",
"problemMatcher": [
"$tsc"
],
"options": {
"cwd": "${workspaceFolder}"
}
}
],
"inputs": [
+4 -12
View File
@@ -2,10 +2,6 @@
.vscode/**
.vscode-test/**
.worktrees/**
# Agent tooling, never shipped in the VSIX
.agents/**
.claude/**
.codex/**
CLAUDE.local.md
out/
dist-standalone/
@@ -26,14 +22,8 @@ eslint-rules/**
.husky/**
.env
# sdk (separate monorepo with its own build/release pipeline)
sdk/**
# Source-of-truth for the marketplace README (the .vsix only ever sees the
# README.md that scripts/marketplace-readme.mjs swaps into place). The backup
# only exists if a publish aborts mid-swap; neither should ship in the .vsix.
README.marketplace.md
.README.github.bak
# cli
cli/**
# Custom
**/demo.gif
@@ -45,9 +35,11 @@ README.marketplace.md
eslint-rules/
old_docs/
evals/
.changie.yaml
.codespellrc
.mocharc.json
buf.yaml
.changeset/
.clinerules/
# 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)
+2 -542
View File
@@ -1,554 +1,14 @@
# Changelog
## [3.87.0]
### Added
- Add MiniMax M3 model support.
### Fixed
- Update VS Code extension dependencies to resolve security issues in `@xmldom/xmldom`, `basic-ftp`, `axios`, `undici`, and other direct/transitive packages.
## [3.86.2]
### Fixed
- Fix `@` file mentions and workspace file search on VS Code 1.122+ by resolving the new bundled `@vscode/ripgrep-universal` per-platform binary layout before falling back to legacy ripgrep paths.
## [3.86.1]
### Fixed
- Fix `@` file mentions failing to find files in some environments (notably VS Code Remote SSH, and after certain VS Code updates) by keeping the file-search fallback alive when the workspace index or bundled ripgrep binary is unavailable.
## [3.86.0]
### Added
- Add Claude Opus 4.8 provider support, including 1M-context variants where available.
- Add Moonshot Kimi K2.6 model support.
### Fixed
- Show prompt-cache support for Qwen 3.7 Max in the Cline provider.
- Fix the VS Code nightly publish workflow startup permissions.
### Changed
- Move the VS Code extension project into `apps/vscode`.
## [3.85.0]
### Added
- Add GPT-5.5 support to SAP AI Core.
- Add DeepSeek V4 Flash and Pro models.
- Add Gemini 3.5 Flash to Gemini and Vertex providers.
- Add `/lg-task` URI webhook integration for LG dashboard flows.
### Fixed
- Fix Vertex AI global endpoint handling for Claude models.
- Route Poolside Laguna models through next-gen prompts and native tool calling.
### Changed
- Update `diff` and `protobufjs` dependencies.
## [3.84.0]
### Added
- Add SAP AI Core support for additional hosted models
### Fixed
- Disable the MCP "Restart Server" button when a server is toggled off.
### Changed
- Remove the Cline Kanban launch modal and bundled demo media from the VS Code extension startup flow.
## [3.83.0]
### Fixed
- Show a clear "Searching..." state in the @-mention file picker
- Improve @-mention file search performance
- Allow `write_to_file` to create or overwrite files with empty content.
- Fix validation failures for MCP servers that require an object.
- Enable OpenRouter prompt cache control for Qwen models.
- Update Axios and SAP Connectivity dependencies
### Changed
- Use the VS Code-specific `README.marketplace.md` when packaging and publishing the VS Code extension
- Add telemetry to @-mention search to help diagnose local, remote, and multi-root workspace search behavior.
## [3.82.0]
### Added
- Restore VS Code foreground terminal support and settings.
- Add latest OpenAI, SAP AI Core, and Z AI models.
### Fixed
- Fix hook template JSON escaping.
- Improve ripgrep file search error handling.
### Changed
- Remove hardcoded model lists from docs.
## [3.81.0]
### Added
- Add GPT-5.5 model support for OpenAI Codex subscription users.
### Fixed
- Remove hardcoded "Whats New" fallback items in webview; only remote-configured welcome banners are shown.
### Changed
- Improve cline-core memory diagnostics used by the extension runtime:
- enable near-heap-limit heap snapshots
- add periodic memory usage logging
- log discovered heap snapshots on abnormal exits for easier OOM debugging
## [3.80.0]
### Added
- Wire up remote `globalSkills` from enterprise remote config with full UI, toggle support, and system prompt integration — enterprise-managed skills now appear under a dedicated "Enterprise Skills" section and support `alwaysEnabled` enforcement
- Onboarding flow now uses dynamically fetched recommended models instead of a hardcoded list, with a fallback to the welcome view on failure
- Add dedicated "Quota Exceeded" error message in the chat error UI when Cline account spend caps are hit
### Fixed
- Fix OOM crashes during long conversations by setting `--max-old-space-size=8192` for the cline-core Node.js process (was defaulting to ~2 GB)
- Show detailed error information in the chat error row instead of a generic caught error message
- Update `axios` to 1.15.0 across all packages
### Changed
- Remove foreground terminal mode — all task command execution now defaults to background mode, removing the VS Code integrated terminal dependency and related settings UI
- Remove old hardcoded announcement banners
## [3.79.0]
### Added
- Add Claude Opus 4.7 model support
- Add Azure Blob Storage as a storage provider
- Add `globalSkills` to remote config
- Inline value reuse in user-level remote-config discovery
### Fixed
- Fix cache reflection for Cline and Vercel API handlers
- Fix stuck `command_output` ask when terminal command ends unexpectedly
- Add `use_subagents` to system prompt for GLM, Hermes, and XS models
- Fix action injection security risk
### Changed
- Remove deprecated evals tool
## [3.78.0]
### Added
- Add a dedicated "Spend Limit Reached" error UI when spend caps are hit
- Docs updates
### Fixed
- Show actual `read_file` line ranges in chat UI
## [3.77.0]
### Added
- Add "Lazy Teammate Mode" experimental toggle
- `read_file` tool now supports chunked reading for targeted file access
### Fixed
- Exclude `new_task` tool from system prompt in yolo/headless mode
- Fix Kanban demo video formatting
### Changed
- Polish `Notification` hook functionality
## [3.76.0]
### Added
- Add Cline Kanban launch modal in webview; CLI now launches Kanban by default with a migration view
- Add toggle to disable feature tips in chat
- Add repeated tool call loop detection to prevent infinite loops wasting tokens
### Fixed
- Fix CLI Kanban spawn on Windows by enabling shell mode for `npx.cmd`
## [3.75.0]
### Added
- Latency improvements for remote workspaces
### Fixed
- Stabilize flaky hooks tests
### Changed
- Remove example hooks in favor of reading the docs
## [3.74.0]
### Added
- Implement dynamic free model detection for Cline API
- Add file read deduplication cache to prevent repeated reads
- Add feature tips tooltip during thinking state
### Fixed
- Replace error message when not logged in to Cline
- Align ClineRulesToggleModal padding with ServersToggleModal
- Skip WebP for GLM and Devstral models running through llama.cpp
- Respect user-configured context window in LiteLLM getModel()
- Honor explicit model IDs outside static catalog in W&B provider
- Add missing Fireworks serverless models and pricing
## [3.73.0]
### Added
- Added W&B Inference by CoreWeave as a new API provider with 17 models
- Improved parallel tool calling support for OpenRouter and Cline providers
### Fixed
- Claude Code Provider: handle rate limit events, empty content arrays, error results, and unknown content types without crashing
- Tool handlers (`read_file`, `list_files`, `list_code_definition_names`, `search_files`) now return graceful errors instead of crashing
## [3.72.0]
### Added
- Added Anthropic Opus 4.6 fast mode variants
### Fixed
- Resolved native tool placeholder interpolation in prompts
- Gemini: capped Flash output tokens to 8192 across providers
- Fixed Windows unit test path normalization
- Fixed flaky hooks tests on Windows
- Bedrock: handle thinking and redacted_thinking blocks correctly in message conversion and streaming
- Prevent crash when `list_files` or `list_code_definition_names` receives a file path
### Changed
- Updated Jupyter Notebook GIFs
- Markdown image loading now requires user consent
- Added `.github/copilot-instructions.md` for coding agents
- Hooks: reintroduced feature toggle
## [3.71.0]
### Added
- Added GPT-5.4 models for ChatGPT subscription users
- Hooks: Added a `Notification` hook for attention and completion boundaries
### Fixed
- Handle streamable HTTP MCP reconnects more reliably after disconnects
## [3.70.0]
### Added
- New Cline API docs: Getting Started, Auth, Chat Completions, Models, Errors, and SDK Examples
- Hook payloads now include `model.provider` and `model.slug`
- Token/cost updates now happen immediately as usage chunks arrive, not after tool execution
### Fixed
- Improve subagent context compaction logic
- Subagent stream retry delay increased to reduce noise from transient failures
- State serialization errors are now caught and logged instead of crashing
- Removed incorrect `max_tokens` from OpenRouter requests
### Changed
- Windows test cleanup now retries on locked files and applies per-test timeouts
- Updated hooks docs
## [3.69.0]
### Added
- Add `User-Agent` header to requests sent to the Cline backend
- Add default auto-tag workflow for publish release flow
- Show Cline SDK docs on the Cline page
### Fixed
- Retry nested git restore and prevent silent `.git_disabled` leftovers in checkpoints
- Prevent Chinese filename escaping in diff view
- Trigger auto-compaction on OpenRouter context overflow errors
- Restore GPT-OSS native file editing on OpenAI-compatible models
### Changed
- Update Cline SDK docs
- Improve hooks support for Windows PowerShell
## [3.68.0]
### Added
- Add dynamic Cline provider model fetching from Cline endpoint
- Add additional Markdown formatting in CLI
- Add focus indicator on action buttons in extension
### Fixed
- Clear all OCA secrets on auth refresh failure to prevent re-auth loops
- Resolve "Could not find the file context" error in Explain Changes
- Use `JSON_SCHEMA` for `yaml.load` to prevent unsafe deserialization
- Fetch model info from API in CLI headless auth for Cline and Vercel providers
- Generate commit message from staged changes only when staging exists
- Update stale `maxTokens` values for Claude 3.7+ models across Anthropic, Bedrock, Vertex, and SAP AI Core
- Use `model.info.maxTokens` for OpenRouter instead of hardcoded `8192`
### Changed
- Increase timeout for a flaky test to reduce short-term test instability
## [3.67.1]
### Added
- Added Cline SDK API interface for programmatic access to Cline features and tools, enabling integration into custom applications.
- Added Codex 5.3 model support
### Fixed
- Fix OpenAI Codex by setting `store` to `false`
- Use `isLocatedInPath()` instead of string matching for path containment checks
## [3.67.0]
### Added
- Add support for skills and optional modelId in subagent configuration
- Add AgentConfigLoader for file-based agent configs
- Add Responses API support for OpenAI native provider
- Preconnect websocket to reduce response latency
- Fetch featured models from backend with local fallback
- Add /q command to quit CLI
- Add MCP enterprise configuration details
- Pull Cline's recommended models from internal endpoint
- Add dynamic flag to adjust banner cache duration
### Fixed
- Fix reasoning delta crash on usage-only stream chunks
- Fix OpenAI tool ID transformation restricted to native provider only
- Fix auth check for ACP mode
- Fix CLI yolo mode to not persist yolo setting to disk
- Fix inline focus-chain slider within its feature row
- Fix Gemini 3.1 Pro compatibility
- Fix Cline auth with ACP flag
### Changed
- Move PR skill to .agents/skills
- SambaNova provider: update models list
- Remove changeset-converter GitHub Action and npm run changeset
## [3.66.0]
### Added
- Gemini-3.1 Pro Preview
## [3.65.0]
### Added
- Add /skills slash command to CLI for viewing and managing installed skills
### Fixed
- Fix aggressive context compaction caused by accidental clicks on the context window progress bar silently setting a very low auto-condense threshold
- Fix infinite retry loop when write_to_file fails with missing content parameter.
- Fixed default claude model
## [3.64.0]
### Added
- Added sonnet 4.6
## [3.63.0]
### Added
- added zai GLM 5 Free promo
### Fixed
- Restore reasoning trace visibility in chat and improve the thinking row UX so reasoning is visible, then collapsible after completion.
## [3.62.0]
### Fixed
- Banners now display immediately when opening the extension instead of requiring user interaction first
- Resolved 17 security vulnerabilities including high-severity DoS issues in dependencies (body-parser, axios, qs, tar, and others)
## [3.61.0]
- UI/UX fixes with minimax model family
## [3.60.0]
- Fixes for Minimax model family
## [3.59.0]
- Added Minimax 2.5 Free Promo
- Fixed Response chaining for OpenAI's Responses API
## [3.58.0]
### Added
- Subagent: replace legacy subagents with the native `use_subagents` tool
- Bundle `endpoints.json` support so packaged distributions can ship required endpoints out-of-the-box
- Amazon Bedrock: support parallel tool calling
- New "double-check completion" experimental feature to verify work before marking tasks complete
- CLI: new task controls/flags including custom `--thinking` token budget and `--max-consecutive-mistakes` for yolo runs
- Remote config: new UI/options (including connection/test buttons) and support for syncing deletion of remotely configured MCP servers
- Vertex / Claude Code: add 1M context model options for Claude Opus 4.6
- ZAI/GLM: add GLM-5
### Fixed
- CLI: handle stdin redirection correctly in CI/headless environments
- CLI: preserve OAuth callback paths during auth redirects
- VS Code Web: generate auth callback URLs via `vscode.env.asExternalUri` (OAuth callback reliability)
- Terminal: surface command exit codes in results and improve long-running `execute_command` timeout behavior
- UI: add loading indicator and fix `api_req_started` rendering
- Task streaming: prevent duplicate streamed text rows after completion
- API: preserve selected Vercel model when model metadata is missing
- Telemetry: route PostHog networking through proxy-aware shared fetch and ensure telemetry flushes on shutdown
- CI: increase Windows E2E test timeout to reduce flakiness
### Changed
- Settings/model UX: move "reasoning effort" into model configuration and expose it in settings
- CLI provider selection: limit provider list to those remotely configured
- UI: consolidate ViewHeader component/styling across views
- Tools: add auto-approval support for `attempt_completion` commands
- Remotely configured MCP server schema now supports custom headers
## [3.57.1]
### Fixed
- Fixed Opus 4.6 for bedrock provider
## [3.57.0]
### Added
- Cline CLI 2.0 now available. Install with `npm install -g cline`
- Anthopic Opus 4.6
- Minimax-2.1 and Kimi-k2.5 now available for free for a limited time promo
- Codex-5.3 through ChatGPT subscription
### Fixed
- Fix read file tool to support reading large files
- Fix decimal input crash in OpenAI Compatible price fields (#8129)
- Fix build complete handlers when updating the api config
- Fixed missing provider from list
- Fixed Favorite Icon / Star from getting clipped in the task history view
### Changed
- Make skills always enabled and remove feature toggle setting
## [3.56.0]
### Added
- **CLI authentication:** Added Vercel AI Gateway and Cline API key provider support for headless CI/automation workflows
- **New model:** Added Kimi-K2.5 model to Moonshot provider (262K context, image support, prompt caching)
- **Prompt variant:** Added Trinity Large prompt variant for improved tool-calling support
- **OpenTelemetry:** Added support for custom headers on metrics and logs endpoints
- **Social links:** Added community icons (X, Discord, GitHub, Reddit, LinkedIn) to the What's New modal
### Fixed
- **LiteLLM:** Fixed thinking configuration not appearing for reasoning-capable models
- **OpenTelemetry:** Fixed endpoint path handling (no longer incorrectly appends `/v1/logs` or `/v1/metrics`) and ensured logs are sent regardless of VSCode telemetry settings
- **CLI auth:** Fixed `cline auth` displaying incorrect provider information after configuration
### Changed
- **Hooks:** Hook scripts now run from the workspace repository root instead of filesystem root
- **Default settings:** Enabled multi-root workspaces, parallel tool calling, and skills by default; disabled strict plan mode by default
- **Settings UI:** Refreshed feature settings section with collapsible design
## [3.55.0]
- Add new model: Arcee Trinity Large Preview
- Add new model: Moonshot Kimi K2.5
- Add MCP prompts support - prompts from connected MCP servers now appear in slash command autocomplete as `/mcp:<server>:<prompt>`
## [3.54.0]
### Added
- Native tool calls support for Ollama provider
- Sonnet 4.5 is now the default Amazon Bedrock model id
### Fixed
- Prevent infinite retry loops when replace_in_file fails repeatedly. The system now detects repeated failures and provides better guidance to break out of retry cycles.
- Skip diff error UI handling during streaming to prevent flickering. Error handling is deferred until streaming completes.
- Strip notebook cell outputs when extracting text content from Jupyter notebooks, significantly reducing context size sent to the LLM.
- Throttle diff view updates during streaming to reduce UI flickering and improve performance.
### Changed
- Removed Mistral's Devstral-2512 free from the free models list
- Removed deprecated zai-glm-4.6 model from Cerebras provider
## [3.53.1]
### Fixed
- Bug in responses API
- Bug in responses API
## [3.53.0]
### Fixed
- Removed grok model from free tier
- Removed grok model from free tier
## [3.52.0]
+1
View File
@@ -1,2 +1,3 @@
@.clinerules/general.md
@.clinerules/network.md
@.clinerules/cli.md
+33 -14
View File
@@ -42,26 +42,41 @@ We also welcome contributions to our [documentation](https://github.com/cline/cl
```bash
code cline
```
3. Install [bun](https://bun.com)
4. Install the necessary dependencies for the extension and webview-gui:
3. Install the necessary dependencies for the extension and webview-gui:
```bash
cd apps/vscode && npm run install:all && cd ../..
cd sdk && bun run build && cd ..
npm run install:all
```
5. Generate Protocol Buffer files (required before first build):
6. Launch by pressing `F5` (or `Run`->`Start Debugging`) to open a new VSCode window with the extension loaded. (You may need to install the [esbuild problem matchers extension](https://marketplace.visualstudio.com/items?itemName=connor4312.esbuild-problem-matchers) if you run into issues building the project.)
4. Generate Protocol Buffer files (required before first build):
```bash
npm run protos
```
5. Launch by pressing `F5` (or `Run`->`Start Debugging`) to open a new VSCode window with the extension loaded. (You may need to install the [esbuild problem matchers extension](https://marketplace.visualstudio.com/items?itemName=connor4312.esbuild-problem-matchers) if you run into issues building the project.)
### Creating a Pull Request
1. Commit your changes.
1. Before creating a PR, generate a changeset entry:
```bash
npm run changeset
```
This will prompt you for:
- Type of change (major, minor, patch)
- `major` → breaking changes (1.0.0 → 2.0.0)
- `minor` → new features (1.0.0 → 1.1.0)
- `patch` → bug fixes (1.0.0 → 1.0.1)
- Description of your changes
2. Push your branch and create a PR on GitHub. Our CI will:
2. Commit your changes and the generated `.changeset` file
3. Push your branch and create a PR on GitHub. Our CI will:
- Run tests and checks
3. Testing
- Run `cd apps/vscode && npm run test` to run tests locally.
- Changesetbot will create a comment showing the version impact
- When merged to main, changesetbot will create a Version Packages PR
- When the Version Packages PR is merged, a new release will be published
4. Testing
- Run `npm run test` to run tests locally.
- Before submitting PR, run `npm run format:fix` to format your code
### Extension
@@ -73,7 +88,6 @@ We also welcome contributions to our [documentation](https://github.com/cline/cl
- If you dismissed the prompts, you can install them manually from the Extensions panel
2. **Local Development**
- cd into the vscode extension, `cd apps/vscode`
- Run `npm run install:all` to install dependencies
- Run `npm run protos` to generate Protocol Buffer files (required before first build)
- Run `npm run test` to run tests locally
@@ -178,10 +192,15 @@ Anyone can contribute code to Cline, but we ask that you follow these guidelines
- Temporary workspaces with test fixtures
- Video recording for failed tests
4. **Versioning & Changelog Notes**
4. **Version Management with Changesets**
- Contributors do not need to create changelog-entry files as part of PRs.
- Maintainers handle release versioning and changelog curation during the release process.
- 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**
+1 -1
View File
@@ -186,7 +186,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2026 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.

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