Compare commits

..

699 Commits

Author SHA1 Message Date
Saoud Rizwan 9a5e1751b2 chore(cli): release v3.0.44 2026-07-16 18:38:54 -07:00
Saoud Rizwan 131e25e1a1 chore(sdk): release v0.0.64 2026-07-16 18:14:58 -07:00
Saoud Rizwan a7ff007af9 chore(cli): release v3.0.43 2026-07-16 17:52:46 -07:00
Bee ef27f45080 fix: max output token handling (#12031)
* fix: max output token handling

* shared

* max reasoning budgetTokens

* fix unit test

* fix: address review feedback on max output token handling

- OpenRouter effort branch sends only reasoning.effort (OpenRouter rejects
  effort combined with reasoning.max_tokens)
- OpenAI Responses forwards explicit caller maxTokens for API-key usage;
  ChatGPT OAuth and synthesized gateway defaults are still omitted
- Gateway lifts the synthesized default output cap above explicit Anthropic
  reasoning budgets so max_tokens > thinking.budget_tokens holds

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix: address second-round review feedback on max output token handling

- Replace the gateway-only requestedMaxTokens field with a defaultedMaxTokens
  flag set when the gateway synthesizes a cap, so explicit maxTokens from
  direct provider callers is forwarded by default (greptile P1)
- Check the parsed hostname instead of a URL substring when detecting the
  ChatGPT OAuth backend (CodeQL)
- Drop the empty else-if branch in toAiSdkMessages in favor of an explicit
  emptiedByDroppedReasoning condition (greptile P2; biome rejects the
  suggested bare continue)
- Dedupe isPositiveFiniteNumber by exporting it from gateway.ts (greptile P2)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* refactor: extract isPositiveFiniteNumber into providers/utils.ts

Move the shared helper to its own module as suggested in review instead
of exporting it from gateway.ts.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* chore: remove unrelated VS Code changes

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-07-16 17:50:22 -07:00
Dominic Cooney e3c6d51072 fix: recognize frontmatter with a leading UTF-8 BOM (#12277)
* fix(vscode): recognize SKILL.md frontmatter with a leading UTF-8 BOM

SKILL.md files saved with a UTF-8 BOM (e.g. by Windows Notepad's 'UTF-8 with BOM' encoding) were silently skipped and not recognized as skills, because gray-matter/regex-style frontmatter parsers require '---' at byte offset 0 and never accounted for the leading BOM byte sequence Node's utf-8 decoder does not strip.

Fixes the shared parseYamlFrontmatter() helper (used by skills, rules, workflows, and remote skill entries in the VS Code extension) and every duplicated ad-hoc frontmatter regex across the SDK/CLI/hub/desktop-app/example-plugin code paths to strip a leading BOM before matching.

Adds regression tests exercising the exact reported scenario (BOM-prefixed SKILL.md silently missing name/description) in frontmatter.test.ts, skills.test.ts, skill-frontmatter-toggle.test.ts, user-instruction-config-loader.test.ts, and configured-agent-config.test.ts.

Fixes https://github.com/cline/cline/issues/12151

* refactor(shared): centralize UTF-8 BOM stripping

* refactor(shared): add UTF-8 file readers

* docs: guide UTF-8 configuration reads
2026-07-17 09:37:20 +09:00
Saoud Rizwan 48bac25548 chore(sdk): regenerate lockfile for v0.0.63 2026-07-16 17:13:44 -07:00
Saoud Rizwan 37f5f104f3 chore(sdk): release v0.0.63 2026-07-16 16:47:40 -07:00
Saoud Rizwan 3577b52404 feat(core): emit mistake-limit telemetry from the session runtime (#12355)
Moves the task.mistake_limit_reached capture (#12354) from the VS Code
SdkController wrapper into @cline/core so every host (CLI, VS Code,
hub daemon) emits it via its session telemetry service.

The MistakeTracker gains an onLimitTelemetry hook fired exactly once
per limit hit, before the limit decision is resolved — including when
no onConsecutiveMistakeLimitReached callback is configured (the
default-stop path, which the extension-side capture missed). The
orchestrator wires the hook to captureMistakeLimitReached using its
reserved telemetry field, reading sessionId/modelId/providerId at fire
time so mid-session connection updates are reflected.

The now-redundant extension wrapper and TelemetryService method are
removed to avoid double-counting in VS Code.
2026-07-16 16:39:50 -07:00
Max 1843bc8ed0 fix(vscode): persist selected account organization (#12345)
* fix(vscode): persist selected account organization

* fix(vscode): ignore stale organization responses

---------

Co-authored-by: Max Paulus 🥪 <max@cline.bot>
Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-07-16 16:08:07 -07:00
Max fead00ec57 fix(vscode): avoid duplicate OpenAI provider settings (#12346)
Co-authored-by: Max Paulus 🥪 <max@cline.bot>
Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-07-16 16:07:48 -07:00
Max 238107d21c fix(vscode): preview auto-approved apply patches (#12349)
Co-authored-by: Max Paulus 🥪 <max@cline.bot>
Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-07-16 16:05:24 -07:00
Saoud Rizwan 2063a661bd feat(vscode): capture telemetry when consecutive mistake limit is reached (#12354) 2026-07-16 15:58:21 -07:00
Dominic Cooney ec02d5862e Fix debug harness: run under node, drop ws dependency. (#12319)
The harness rotted after the npm-to-bun migration: the 'ws' package it imported is no longer in the dependency tree, and Playwright's _electron.launch() times out under bun (the debugee Electron starts but Playwright never finishes attaching; the same launch attaches in under a second under node). Use the runtime's built-in WebSocket for the CDP client and document that the harness must be run with node.
2026-07-17 07:43:32 +09:00
Mikołaj Kondratek 8452084842 fix: auto-discover OS trust anchors in the CLI wrapper (#11498)
* fix: auto-discover OS trust anchors in the CLI wrapper

The 3.x CLI ships as a Bun-compiled binary. Bun does not read the OS
trust store unless NODE_USE_SYSTEM_CA is set, and even with the flag its
Windows enumeration covers only the `Root` store, not `CA`/Intermediate
(verified empirically across the CLINE-2353 Windows repro rounds). So a
corporate MITM root is not trusted out of the box and inference fails
with "unable to get local issuer certificate". The pre-3.0 (Node) CLI
had no app-level CA handling either; users only succeeded by setting
NODE_EXTRA_CA_CERTS manually. The reporter's ask: have it just work
without the env var.

This follows the CLINE-2353 SDK fetch-threading change. That made the
inference client honor a host-provided proxy/CA-aware fetch, but on the
CLI Bun's global fetch is already proxy-aware and a fetch function
cannot cross the hub-daemon process boundary, so the CLI's missing piece
is trust material, not the fetch. Env vars do inherit across spawns.

The npm `bin/cline` wrapper runs on Node (not Bun), so it can read the
full OS store via tls.getCACertificates("system") (Node >= 22, no flag
required) — including the Windows `CA` store Bun skips — and hand the
certs to the Bun child via NODE_EXTRA_CA_CERTS, which both runtimes
honor. This mirrors the JetBrains plugin's configureCertificates(),
replacing "harvest from the IDE trust store" with "harvest from the OS".

The merge logic lives in a dependency-free, injectable-module CommonJS
helper (bin/ca-certs.cjs) so it is unit-testable and ships verbatim in
the generated wrapper package (publish copies bin/ wholesale). A
user-set NODE_EXTRA_CA_CERTS is merged ahead of the system certs; a
self-reference to the managed bundle is detected to avoid re-appending
every launch; when no system certs are available the user's setting is
left untouched. Writes are atomic (temp + rename) and owner-only.

Adds ca-certs.test.ts (13 cases) covering harvest filtering, user-bundle
PEM/DER/missing handling, newline-separated merge, managed-path
self-reference, and the no-system-certs no-op.

* fix: harden CLI auto-CA harvesting (review follow-ups)

Follow-ups from the CLINE-2353 review of the CLI auto-CA wrapper.

- H1: a legacy NODE_EXTRA_CA_CERTS set to an OS-path-delimited list
  ("a.pem;b.pem", the CLINE-2324 footgun Node never split) was stat'd as
  one file, failed, and silently dropped the user's certs. readUserCerts
  now tries the whole value as one file first, then splits on the OS path
  delimiter and reads each existing PEM, merging them all.
- M1: skip the rewrite when the managed bundle is already current, instead
  of re-harvesting and rewriting on every launch (mirrors the JetBrains
  hash-and-skip). configureNodeExtraCaCerts now returns a typed outcome
  (unchanged | written | write-failed-reused | write-failed |
  no-system-certs) with cert counts.
- M2: tolerate rename-over-existing failures (Windows EPERM/EBUSY when a
  concurrent child holds the file open) by removing the target and
  retrying, then falling back to a previously-written bundle. Combined
  with M1 the steady state no longer rewrites at all.
- M3: the wrapper prints a one-line diagnostic under CLINE_DEBUG=1
  (cert counts + managed path, or a warning when no OS certs were found
  or the write failed). Runs once per startup.
- M4: corrected the now-stale CLI guidance in shared/net.ts (the CLI no
  longer requires users to set NODE_EXTRA_CA_CERTS manually).
- L1: documented the auto-trust behavior, the managed ~/.cline bundle,
  the merge-not-replace override semantics, and CLINE_DEBUG in the CLI
  README.
- L4: trimmed the helper's file header; DI is still injectable for tests.

ca-certs.test.ts grows to 20 cases: adds readUserCerts (single path,
delimited split, missing-segment skip, managed-bundle exclusion, empty),
the unchanged/second-run skip, and a write-failure outcome via an
fs that throws.

* fix: address CLI auto-CA review issues (temp cleanup, cert count, test)

- writeBundle now hoists the temp path so the outer catch removes a
  partially-written temp file (e.g. ENOSPC / ACL failure mid-write).
  Previously only the inner double-rename failure cleaned up, so repeated
  disk-full/permission failures left a stale .tmp per launch in ~/.cline.
  The inner Windows-rename fallback now lets its failure fall through to
  the single cleanup path instead of duplicating rmSync.
- userCertCount now counts individual certificates (via countCerts, which
  tallies BEGIN CERTIFICATE markers) rather than the number of PEM files,
  so a user bundle with N intermediates reports N and is comparable to
  systemCertCount. countCerts is exported for testing.
- Adds tests for the write-failed-reused branch (stale bundle reused when
  the rewrite fails but the old file is still readable) and for countCerts
  (one file holding two certs reports 2).

* fix: warn when the CLI wrapper's Node cannot read the OS trust store

tls.getCACertificates("system") needs Node >= 22.15; on older hosts the
auto-CA harvest silently did nothing, which is indistinguishable from a
broken corporate proxy. Distinguish the missing-API case as its own
outcome (api-unavailable) and print a non-debug warning when the user
has no NODE_EXTRA_CA_CERTS of their own. Found in round-5 Windows
validation (wrapper under Node 22.1.0).

* fix: copy only certificate blocks into the managed CA bundle

Combined cert+key PEMs (nginx/haproxy-style server.pem) passed the
old contains-a-certificate check, so a user NODE_EXTRA_CA_CERTS
pointing at one duplicated the private key into the managed bundle,
where it outlives rotation of the original and gets no permission
tightening on Windows. Extract complete BEGIN/END CERTIFICATE blocks
instead; files with none are treated as not PEM, and certificates-only
files pass through byte-identical so the unchanged-skip stays stable.
Raised in PR review.

* fix: show the old-Node trust warning once per Node version

The api-unavailable warning printed on every CLI invocation, turning
an actionable nudge into stderr noise for users pinned to an old Node.
Stamp the warning per Node version under the cline dir: it shows once,
re-arms when the Node version changes, and a bookkeeping failure never
suppresses the diagnostic. Raised in PR review.
2026-07-16 10:14:15 -07:00
Dominic Cooney a41129a5db fix(vscode): restore 'Proceed While Running' for foreground terminal commands (#12320)
* First cut of 'proceed while running' for foreground tasks.

* Address review: flush partial line on detach; cap log before write; freeze partial output at detach.

* fix(vscode): cap detached command log replay
2026-07-15 22:46:37 -07:00
Saoud Rizwan 1ea34be611 chore(cli): release v3.0.42 2026-07-15 20:03:31 -07:00
Saoud Rizwan e72bc3cd14 chore(sdk): release v0.0.62 2026-07-15 19:46:58 -07:00
Tomás Barreiro 9c907af826 Send the Feature Flag Event when rolling out (#12325)
* Send the Feature Flag Event when rolling out

* Update apps/vscode-rollout/scripts/smoke-loader.mjs

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-07-15 19:39:37 -07:00
Saoud Rizwan e8d3d82522 fix(core): omit telemetry from hub tool contexts (#12326) 2026-07-15 19:33:12 -07:00
Max 7f9d2e96d9 fix(ollama): restore native API routing so context window and timeout settings work (#12286)
The 4.0.0 SDK migration routed Ollama through the generic OpenAI-compatible
vendor (/v1/chat/completions), which cannot express Ollama's options.num_ctx.
Every model loaded at Ollama's 4096-token server default, truncating Cline's
prompt and breaking most features (CLINE-2603, CLINE-2566, CLINE-2572).

- Add a native Ollama vendor backed by ai-sdk-ollama (wraps the official
  ollama client); num_ctx derives from the resolved gateway model's
  contextWindow at the adapter boundary, defaulting to 32768
- Persist the Model Context Window setting in providers.json via the
  pre-existing provider-neutral contextWindow field (legacy
  ollamaApiOptionsCtxNum state key kept as read fallback / write mirror),
  and surface it as the selected model's contextWindow so the chat
  indicator, compaction budgets, and num_ctx all agree
- Project ProviderConfig.maxInputTokens (where ProviderSettings.contextWindow
  lands) onto the selected gateway model in both gateway builders so
  CLI/Core hosts honor the configured value too
- Stop falling back to the bundled Ollama-Cloud catalog when /api/tags is
  empty; local-model-source providers keep the user's committed model
  instead of silently selecting a cloud model (nemotron)
- Wire Request Timeout (ms) with the legacy semantics (response must start
  within requestTimeoutMs || 30000; streaming never cut off mid-generation)
- Settings UI: gate the context-window field until provider config loads,
  skip unchanged writes, drop the custom prompt checkbox

Fixes CLINE-2603, CLINE-2566, CLINE-2572

Co-authored-by: Max Paulus 🥪 <max@cline.bot>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-07-15 17:55:29 -07:00
Saoud Rizwan d618f8073a fix(vscode-rollout): align bundle versions and harden combined publish workflows (#12321)
* fix(vscode-rollout): align bundle versions in the stable AB workflow

Found by Max in local testing: the union manifest's version (what the
Marketplace and auto-update see) is the stitch input, but each bundle's
About tab and telemetry extension_version read that bundle's OWN
package.json — so the stable combined VSIX reported three different
versions (dispatch input / main's 4.0.0 / legacy's 4.0.8) depending on
where you looked. The nightly channel doesn't have this problem
(nightlify.mjs stamps one version into everything); this gives the stable
channel the identity-preserving equivalent: scripts/set-version.mjs stamps
the dispatch version into each checkout after install, before its build.

Also fixes a latent ab-package bug while restructuring the steps: the
next-bundle build never ran build:sdk, so the @cline/* workspace deps had
no dist and esbuild would fail on a fresh CI checkout (the workflow has
never run end-to-end — the publish environment gate blocked pre-merge
dispatches). Split install/build:sdk/align/build into separate steps,
mirroring the nightly workflow.

* fix(vscode-rollout): assert bundle sub-manifest versions in identity guardrails

Greptile round on #12321: the stable guardrail didn't assert version at
all. Went one further than the suggestion — both workflows' guardrails now
also assert each bundle sub-manifest's version (and name, for nightly)
matches the expected version, which is the check that actually regression-
guards the set-version.mjs/nightlify.mjs stamping (About tab + telemetry
extension_version read the sub-manifests, not the union). Expected version
routed through env rather than interpolated into the script body. Adds the
conventional paired test for set-version.mjs.

* fix(vscode-rollout): don't fail the nightly run when the tag push is rejected

First real combined publish (run 29454994164) published to both registries
successfully but the run went red at the last step: the default
GITHUB_TOKEN cannot create a ref whose commit modifies workflow files, and
HEAD was the #12253 squash merge which rewrote this very workflow. There
is no workflows permission grantable to the token, so this recurs any
night HEAD touched .github/workflows. The tag is bookkeeping — mark the
step continue-on-error so a successful publish isn't reported as a
failure. (Today's missing tag was pushed manually.)
2026-07-15 17:39:44 -07:00
Saoud Rizwan eb21ba583c fix(ci): restrict nightly publishing to main (#12322) 2026-07-15 15:35:09 -07:00
Saoud Rizwan f29c25395c feat(vscode-rollout): A/B loader and packaging for staged SDK extension rollout (#12253)
* feat(vscode-rollout): A/B loader and packaging for staged SDK extension rollout

Ship one marketplace VSIX containing a tiny loader plus two complete
extension bundles: next/ (SDK-based apps/vscode from main) and legacy/
(the legacy-extension branch). The loader picks one bundle per window
from a PostHog-flag-driven, sticky, one-way cohort assignment, activates
it with a Proxy-scoped ExtensionContext so each bundle resolves its
resources from its own subdirectory, and falls back to legacy (with
partial-registration cleanup and version pinning) if the next bundle
crashes during activation.

Includes the union-manifest generator with per-cohort when-clause
gating, the VSIX stitcher, a node-level loader smoke test, and the
ext-vscode-ab-package workflow that builds both refs and packages
(optionally publishes) the combined VSIX.

* fix(vscode-rollout): address rollout review feedback

* feat(vscode-rollout): versioned kill-switch, user-setting override, launch-cadence telemetry

Review follow-ups from #12253:

- Kill-switch is now scoped by version instead of boolean: the PostHog flag's
  payload carries {"maxKilledVersion": "x.y.z"} and the loader demotes only
  combined VSIXes <= that version, so killing a broken release never blocks
  the release that fixes it. Arming with no payload still demotes everything,
  and the old boolean memento format is normalized on read.

- cline.rollout.bundleOverride user setting (auto | next | legacy) as a
  manual escape hatch editable straight from settings.json: beats flags and
  the kill-switch in both directions, applies on window reload, reported as
  'override' on the activation event. Injected into the union manifest by
  gen-manifest so neither bundle has to know about it.

- parseRolloutFlags hardens flag typing: only a literal boolean true promotes
  (multivariate variants, numbers, junk fail safe), kill payloads are parsed
  defensively from /decide's JSON-string encoding.

- Activation events now carry ms_since_last_activation so the real window-
  reload cadence bounds how fast the rollout percentage gets dialed up.

- Walkthrough manifest invariant relaxed from byte-equality to structural
  equality (ids/media/completionEvents): the branches already diverge on one
  MCP step description, and since walkthrough markdown at the VSIX root comes
  from next regardless, hard-failing on copy tweaks bricked the release
  pipeline while protecting nothing. Copy divergence now warns and ships
  next's text.

* feat(vscode-rollout): identity-aware namespace, authoritative activation telemetry, nightly indicator

- Derive the setting section and sdkBundle context key from the packaged
  manifest name (cline.* for stable claude-dev, cline-nightly.* for the
  nightly identity, whose packaging rewrites the whole ID namespace);
  gen-manifest derives the same prefix for gates and the injected
  bundleOverride setting.
- Call the activated bundle's reportRolloutActivation export (merged on
  both branches) with attempted/actual/fallback — the authoritative
  extension.rollout.bundle_activated event, attributed via the bundle's
  variant-built telemetry. On crash fallback the LEGACY bundle reports it.
- Rename the loader's direct PostHog event to
  extension.rollout.loader_decision: it collided byte-for-byte with the
  bundles' event name under a different schema. It keeps the loader-side
  metadata (override, launch cadence, loader_version, extension_name) and
  gains double_failure for the both-bundles-dead case.
- Fix duplicate activation events on crash fallback: the recursive legacy
  activation no longer emits a second, contradictory fallback:false event.
- Nightly-only status bar indicator (Cline: Next / Cline: Legacy) so
  dogfooders can see which bundle a window is running.
- Union diverged engines to the newer requirement instead of hard-failing:
  main's VS Code engine (^1.101.0) has legitimately moved ahead of
  legacy-extension's (^1.84.0), which bricked every combined build.
- Smoke scenarios for all of the above.

* feat(vscode-rollout): publish the nightly as the combined A/B VSIX

Convert ext-vscode-publish-nightly.yml (cron + dispatch) from the
standalone SDK build to the combined loader + next + legacy package,
published as saoudrizwan.cline-nightly at <major>.<minor>.<unix-seconds>:

- scripts/nightlify.mjs reproduces publish-nightly.mjs's identity mutation
  (claude-dev -> cline-nightly, "cline. -> "cline-nightly., displayName,
  activity bar title) with the version as an explicit argument so ONE
  version reaches both bundle manifests and the union manifest. Runs after
  dependency install and before each bundle build.
- Both bundle builds get CLINE_ROLLOUT_VARIANT (next/legacy) in the nightly
  AND stable workflows — without it the merged rollout telemetry
  (extension_variant common prop + the authoritative bundle_activated
  capture) silently no-ops.
- dry-run dispatch input builds and uploads the installable .vsix without
  publishing or tagging; publish/tag steps are additionally gated to main,
  so the PR branch can be dispatched for pre-merge verification.
- Identity guardrails before packaging: nightly workflow asserts
  cline-nightly, the stable ab-package workflow asserts claude-dev.
- The nightly tag now records the legacy bundle sha in its message.
- README: nightly channel section (identity mapping, the two telemetry
  events and their owners, dry-run verification), and a note that the
  PostHog flags govern nightly only until the stable combined VSIX ships.

The single-bundle publish-nightly.mjs path remains for manual
feature-branch pre-release publishes; CI no longer invokes it.

* chore(vscode-rollout): harden nightly workflow gating

- Restore a job-level branch allowlist on the publish job (main + the
  rehearsal branch). Advisory defense-in-depth: the enforced gate is the
  PublishNightly environment's deployment-branch policy in repo settings,
  which must list the same branches; a dispatched branch runs its own copy
  of this file.
- Route the legacy-ref dispatch input through env instead of interpolating
  it into the run script body (script-injection hygiene; dispatch already
  requires write access).

* add otel vars to rollout build (#12316)

- Extension will not emit otel metrics to otel without these vars, so
adding those into the slow-rollout build workflow

Co-authored-by: Max Paulus 🥪 <max@cline.bot>

* fix(vscode-rollout): pass OTel env to the nightly legacy bundle build

Legacy's esbuild inlines OTEL_* at build time and its standalone publish
workflow passes them, so the combined nightly's legacy bundle was being
built with the OTel logs/metrics pipeline dead. Companion to #12316,
which fixes the same gap in ext-vscode-ab-package.yml (both bundles
there).

* feat(vscode-rollout): make the rollout two-way, remove the kill-switch

The one-way cohort + versioned kill-switch existed to avoid demoting users
whose SDK-bundle tasks aren't listed by legacy and whose rotated creds may
need a re-login. Decision: those are acceptable, temporary UX costs on an
emergency-only path — not worth a second flag and permanent mechanism
complexity (payload parsing, version scoping, killed-up-to cache format).

Now there is ONE knob: each background refresh caches exactly what
ext-sdk-bundle-rollout says for the next window. Dialing the percentage
down demotes; 0% pulls everyone back to legacy on their next reload.
Fail-safe direction preserved: only a literal boolean true promotes —
variant strings / numbers / a deleted flag all resolve to legacy; malformed
/decide responses leave the cache untouched. Local crash pinning (next
threw -> pin this version to legacy on this machine) is unchanged and
independent of the flag.

Removes KILLSWITCH_FLAG/KILLSWITCH_STATE_KEY/isVersionKilled/
normalizeKilledUpTo/compareVersions/nextCachedBundle; parseRolloutFlags
becomes parseRolloutAssignment returning the bundle to cache. Smoke
scenarios replaced with two-way promote/demote coverage.

---------

Co-authored-by: Max <maxpaulus43@gmail.com>
Co-authored-by: Max Paulus 🥪 <max@cline.bot>
2026-07-15 15:07:45 -07:00
Max 84c9b587a6 refactor(vscode): resolve model metadata host-side (#12130)
Co-authored-by: Max Paulus 🥪 <max@cline.bot>
2026-07-15 13:37:02 -07:00
Saoud Rizwan 6dca234d8e chore(cli): release v3.0.41 2026-07-15 11:02:17 -07:00
Renee Huang 9217eacbbd fix: update broken ACP editor integrations redirect to CLI reference (#12312)
* fix: update broken ACP editor integrations redirect to point to CLI reference

* feat: add ACP Editor Integrations page under CLI section

- Create cli/acp-editor-integrations.mdx with ACP overview, supported editors, quick start, and usage guide
- Add page to CLI navigation group in docs.json
- Restore redirect from /cline-cli/acp-editor-integrations to /cli/acp-editor-integrations (page now exists)

* Revert "feat: add ACP Editor Integrations page under CLI section"

This reverts commit 2728b9c2ad.
2026-07-15 10:57:59 -07:00
Saoud Rizwan adbb42a99c chore(sdk): release v0.0.61 2026-07-15 10:29:47 -07:00
Saoud Rizwan 50d1578a7e feat(ui): add shared Cline theme package (#12285)
* feat(desktop): refresh navigation and visual foundation

* feat(desktop): align settings with Cline Hub

* feat(desktop): improve chat markdown rendering

* fix(desktop): resolve review feedback blockers

* fix(desktop): refine inline code sizing

* fix(desktop): preserve workspace choices during startup

* feat(ui): add shared Cline theme package

* fix(desktop): restore sidebar session sorting

* fix(desktop): harden session startup state

* fix(desktop): tighten settings navigation state

* fix(desktop): tighten markdown link handling

* fix(ui): harden theme contract validation

* test(desktop): cover late workspace restoration
2026-07-15 00:04:08 -07:00
Saoud Rizwan 5ef3b81369 feat(desktop): improve chat markdown rendering (#12276)
* feat(desktop): refresh navigation and visual foundation

* feat(desktop): align settings with Cline Hub

* feat(desktop): improve chat markdown rendering

* fix(desktop): resolve review feedback blockers

* fix(desktop): refine inline code sizing

* fix(desktop): preserve workspace choices during startup

* fix(desktop): restore sidebar session sorting

* fix(desktop): harden session startup state

* fix(desktop): tighten settings navigation state

* fix(desktop): tighten markdown link handling

* test(desktop): cover late workspace restoration
2026-07-14 23:49:22 -07:00
Saoud Rizwan ec3a57771d fix(cli): block compaction during active turns (#12296) 2026-07-14 23:23:09 -07:00
Saoud Rizwan a695dab23a feat(desktop): align settings with Cline Hub (#12275)
* feat(desktop): refresh navigation and visual foundation

* feat(desktop): align settings with Cline Hub

* test(desktop): support webview component tests

* fix(desktop): resolve review feedback blockers

* fix(desktop): preserve workspace choices during startup

* fix(desktop): restore sidebar session sorting

* fix(desktop): harden session startup state

* fix(desktop): tighten settings navigation state

* test(desktop): cover late workspace restoration

* chore: preserve upstream merge contents
2026-07-14 23:18:48 -07:00
Saoud Rizwan 77af52661c fix(telemetry): attach organization context to cached-credential identity (#12288)
* fix(telemetry): attach organization context to cached-credential identity

CLI cached credentials only stored the account id, so telemetry identity
resolved from them (headless runs via #11581, the hub daemon via #12177)
carried user_id but no organization_id - making CLI/hub usage invisible
to organization-scoped dashboards even where per-user attribution works.

- AuthSettingsSchema gains optional organizationId/organizationName/
  memberId
- loadClineAccountSnapshot persists the active organization into the
  cached cline provider settings after fetching /me (cleared when the
  user is on their personal account), so the context survives across
  processes without a network call
- the CLI runtime identify and the hub daemon identity refresh read the
  persisted fields and pass them to identifyAccount; the daemon re-keys
  its refresh on account+organization so an org switch re-identifies a
  long-lived daemon

* fix(telemetry): strip stray NUL byte, drop needless reshaping of daemon identity resolve
2026-07-14 23:10:27 -07:00
Saoud Rizwan 0f4acccd08 feat(desktop): refresh navigation and visual foundation (#12268)
* feat(desktop): refresh navigation and visual foundation

* test(desktop): support webview component tests

* fix(desktop): resolve review feedback blockers

* fix(desktop): preserve workspace choices during startup

* fix(desktop): restore sidebar session sorting

* fix(desktop): harden session startup state

* test(desktop): cover late workspace restoration
2026-07-14 23:02:06 -07:00
Bee f8c73cd8cc feat(core): persist and refresh workspace git info (#12295) 2026-07-15 07:30:16 +02:00
Max 55a31a0d8a feat(vscode): add rollout telemetry to SDK extension (#12292)
* feat(vscode): add shared rollout telemetry contract

* feat(vscode-sdk): propagate rollout metadata

---------

Co-authored-by: Max Paulus 🥪 <max@cline.bot>
Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-07-14 22:29:52 -07:00
Bee 04438c0d54 feat: shows compaction progress status in UI (#12137)
* feat: shows compaction progress status in UI

* fixes p2

* fix: complete compaction lifecycle delivery

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-07-15 13:09:10 +08:00
Bee f053ec48e4 refactor(llms): owns provider-specific header policy (#12187)
* refactor(llms): owns provider-specific header policy

* header
2026-07-15 03:23:23 +02:00
Bee 0df406723c refactor(core): normalize read file request path aliases (#12287)
* fix(core): normalize read file request path aliases

Accept `file_path` and `filePath` in read file requests and normalize them to the canonical `path` field. Apply alias handling to direct, array, and nested inputs to prevent model-generated variants from failing validation.

Clarify path descriptions by removing redundant wording.

* update test
2026-07-15 08:46:00 +08:00
Dominic Cooney 12703bf407 Improve VS Code terminal reliability: OSC 633 parser, exit codes, timeout handling (#11972) 2026-07-14 16:34:36 -07:00
Bee 4a97b46f5f refactor(core): simplifies context compaction trigger (#12217)
* refactor(core): simplifies context compaction trigger

Simplifies automatic context compaction so it always triggers when input usage reaches 80% of the model’s effective maximum input-token limit.

* add bound

* feedback apply

* complete

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-07-15 02:11:38 +08:00
Saoud Rizwan 36fc3327ac fix(cli): highlight API key fallback hint (#12283) 2026-07-14 10:51:05 -07:00
Max 2b48dc411f make old tasks incompatible with new cline extension (#12127)
Preserve pretty legacy task display after resume

Co-authored-by: Max Paulus 🥪 <max@cline.bot>
Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-07-14 10:00:23 -07:00
Tomás Barreiro da6fe718d0 Rename sessionStartedAt to sessionStartedAtMs (#12279) 2026-07-14 16:08:17 +02:00
Tomás Barreiro 3515333e23 Review against camelCase telemetry (#12280) 2026-07-14 15:59:19 +02:00
Saoud Rizwan bd9ac5872b ci(sdk): create GitHub release and post to Slack on latest SDK publish (#12223)
* ci(sdk): create GitHub release and post to Slack on latest SDK publish

* ci(sdk): use random heredoc delimiter for changelog output
2026-07-14 01:31:45 -07:00
Saoud Rizwan fb15324ad2 fix(cli): prevent use-after-free when setting terminal title during TUI teardown (#12229)
* fix(cli): prevent use-after-free when setting terminal title during TUI teardown

* fix(cli): re-check renderer destruction before title reset in teardown microtask

* test(cli): cover terminal title teardown lifecycle
2026-07-14 01:31:01 -07:00
Saoud Rizwan 7a27c04ffa fix(core): stop reporting benign git states as workspace init errors (#12189)
* fix(core): stop reporting benign git states as workspace init errors [ENG-2244]

A freshly initialized repo with no commits makes 'git rev-parse HEAD'
fail, which generateWorkspaceInfoWithDiagnostics recorded as a workspace
init error and surfaced as workspace.init_error telemetry on every
session bootstrap. Filter out git failures that reflect normal
repository states; genuine failures (missing directory, real git
breakage) are still reported.

* fix(core): drop 'bad revision' from benign git error filter

Review feedback: 'fatal: bad revision HEAD' can also indicate a corrupt
.git/HEAD (checkIsRepo still succeeds), which is a genuinely broken
workspace that should keep reporting. The remaining patterns cover the
empty-repo message variants.
2026-07-14 01:30:33 -07:00
Saoud Rizwan c37b252f65 fix(vscode): restore multi-root mention resolution and validate stored task cwd (#12190)
* fix(vscode): restore multi-root mention resolution and validate stored task cwd [ENG-2245][ENG-2244]

The SDK adapter's ensureWorkspaceManager() was a stub returning
undefined, which silently disabled multi-root file mention resolution:
parseMentions only searched the primary cwd, so @-mentions of files in
secondary workspace roots failed with not_found. Build a real
WorkspaceRootManager from the host's workspace folders (cached until
the folder set changes) via a new WorkspaceRootManager.fromPaths().

Also validate that a resumed task's stored cwdOnTaskInitialization
still exists before using it — stale paths (deleted/moved dirs) fed
git-based workspace init and produced init-error telemetry.

* fix(vscode): use JSON.stringify for workspace manager cache key

Review feedback: a delimiter-joined key is ambiguous for paths
containing the delimiter (and the previous separator was an embedded
NUL byte). JSON.stringify is unambiguous and order-preserving.

* test(vscode): cover stored task cwd validation
2026-07-14 01:30:21 -07:00
Saoud Rizwan 2872138900 feat: suggest model IDs from OpenAI-compatible endpoints in extension and CLI (#12231)
* fix(vscode): use the requested provider's stored credentials when listing OpenAI-compatible models

The OpenAI-compatible settings pane already fetches GET <baseUrl>/models to
suggest model IDs, but the host handler always read the built-in "openai"
provider's stored settings. Custom OpenAI-compatible providers only expose a
masked API key to the webview, so their model-list requests went out
unauthenticated and the suggestion dropdown stayed empty.

Add provider_id to OpenAiModelsRequest and read that provider's stored API
key and custom headers in refreshOpenAiModels. Old clients omit the field,
which defaults to "openai" and preserves the previous behavior.

* feat(cli): suggest model ids from OpenAI-compatible endpoints in the model picker

The CLI showed a bare free-text input for openai-compatible providers and
never asked the endpoint what it serves. Fetch GET <baseUrl>/models with the
provider's stored API key/headers when opening the picker; when the endpoint
answers, show the standard fuzzy list (which keeps the "Create custom model
ID" row for manual entry). Any failure or empty answer falls back to the
existing free-text input.

* fix: resolve OpenAI-compatible model discovery config
2026-07-14 01:26:48 -07:00
Saoud Rizwan dc4620c529 fix(vscode): add to system prompt about plan/act modes and nudge about mode switches (#12227)
* feat(shared): move plan/act mode prompt instructions into the shared prompt builder

The CLI's #12057 fixes (mode-tag explanation, plan-mode contract,
mode-switch notice tracker) were CLI-only wiring, so the VSCode extension
never told the model what the <user_input mode> attribute means and plan
mode kept mutating files (CLINE-2576, CLINE-2607, CLINE-2579). Promote
the pieces every host needs into @cline/shared:

- buildClineSystemPrompt now appends MODE_TAG_INSTRUCTIONS for every mode
  and PLAN_MODE_INSTRUCTIONS for plan sessions, composed into the rules
  slot in the exact order the CLI historically built by hand, so CLI
  output is byte-identical after the refactor.
- The plan-mode contract gains an explicit run_commands paragraph:
  the tool intentionally stays available in plan mode (essential for
  read-only investigation) but is inspection-only there -- no file
  mutations, no state-changing commands. The mitigation for plan-mode
  mutations is prompting plus mode-switch notices, not tool removal.
- createModeSwitchNoticeTracker moves from apps/cli/runtime/interactive
  to @cline/shared next to formatModeSwitchNotice; the CLI re-exports it
  so its import surface and tests stay unchanged.
- deriveTitleFromPrompt gets a regression test pinning that titles never
  pick up mode-notice text.

* fix(vscode): teach the model about plan/act modes and surface mode switches

Port the CLI's #12057/#12058 plan-mode fixes to the extension:

- The session factory drops its local PLAN_MODE_INSTRUCTIONS copy; the
  shared prompt builder now emits both the mode-tag explanation and the
  plan-mode contract (including the read-only run_commands rule), so the
  extension's system prompt finally explains the <user_input mode>
  wrapper its own messages have carried all along.
- Manual Plan/Act toggles record a mode-switch notice in
  SdkModeCoordinator (shared round-trip-cancelling tracker, scoped to
  the rebuilt session so it never leaks across tasks), recorded only
  after the session replacement actually commits. The model-initiated
  switch_to_act_mode path passes source: "tool" and records nothing,
  matching the CLI: its tool result and continuation prompt already
  announce the switch.
- SdkSessionLifecycle.fireAndForgetSend -- the single funnel for
  outbound turn sends -- consumes the notice and prepends
  formatModeSwitchNotice() to the next message, exactly like the CLI's
  run-interactive stamping.
- Display boundaries never render the raw tag: the queued-prompt echo
  in the message translator now goes through formatDisplayUserInput,
  and isSyntheticUserPrompt strips notices before matching so a stamped
  continuation prompt cannot shift edit/regenerate ordinals.
2026-07-14 01:26:20 -07:00
Saoud Rizwan 2ac5c85e69 fix(vscode): restore editor diff view for SDK edit tools (#12219)
* feat(sdk): expose edit-executor internals for host diff previews

Extract computePatchChanges() from createApplyPatchExecutor so hosts can
compute a patch's per-file proposed content without writing to disk
(behavior-identical refactor; the executor now calls the helper), and
widen the @cline/core root exports with createEditorExecutor,
createApplyPatchExecutor, computePatchChanges, PatchActionType and the
related types. Needed by the VS Code adapter to restore the editor diff
view for SDK edit tools.

* fix(vscode): restore editor diff view for SDK edit tools

Adds SdkDiffEditCoordinator, which owns per-toolCallId diff sessions over
the legacy DiffViewProvider abstraction (HostProvider factory, so the
external/JetBrains gRPC DiffService path keeps working):

- the diff editor opens populated before the approval ask renders (the
  SDK surfaces tool input only after the model stream completes, so the
  approval callback is the only pre-execution point with full input)
- an overridden editor executor saves through the diff document:
  user edits in the editable right pane and post-save auto-formatting
  flow back to the model via formatResponse.fileEditWithUserChanges,
  plus 'new problems' diagnostics
- Reject/abort reverts (new files: file + created dirs removed)
- auto-approved edits open the diff during execution with the legacy
  3.5s diagnostics settle; Background Edit keeps the headless disk path
- apply_patch gets a preview-only diff of its first changed file; on
  approve the preview is reverted and the untouched SDK executor applies
  the whole patch
- any diff-pipeline failure reverts and falls back to the SDK disk
  executor, preserving canonical error strings

Fixes #11934 (CLINE-2580).

* refactor(vscode): make edit diff preview a read-only virtual-document diff

Reworks the diff view restoration after EDH testing showed the editable
real-document design breaking on same-file multi-edits (tab reuse opened
the actual file instead of a diff; sibling saves closed other sessions'
tabs; right-pane edits misbehaved).

New design per review:
- EditPreview abstraction (mirrors CommentReviewController pattern):
  VscodeEditPreview renders vscode.diff with BOTH sides as virtual
  cline-diff documents (unique fragment per preview, so same-file edits
  get distinct tabs and close is an exact tab match, never the real
  file); ExternalEditPreview uses the existing openMultiFileDiff/
  closeAllDiffs host-bridge RPCs. New createEditPreview factory on
  HostProvider.
- The preview never touches disk: executors close the preview and
  delegate to the SDK's default disk executors, whose results and error
  strings reach the model unchanged. Reject/abort just closes a tab.
- Dropped by design decision: editing in the diff view, user-edit
  feedback to the model, and diagnostics passback (the SDK already
  prompts the model to check).
- Auto-approved edits show a brief preview that lingers ~1.5s after the
  write; an abort cuts the linger short without failing the applied edit.
- A newer same-file preview supersedes an older pending one (approvals
  resolve sequentially), eliminating cross-session interference.
- Legacy DiffViewProvider stack returns to untouched dead code.

* fix(vscode): state that denied edits did not modify the file

Repro: ask Cline to edit a file, then answer the approval with feedback
instead of Approve/Reject. The denial reached the model as just
{"error":"make them bigger"} — nothing said the edit was NOT applied —
so the model treated the feedback as iteration on an applied change and
built its next old_text against content that never landed on disk. From
then on old_text no longer matched the real file and the diff preview
silently stopped appearing (and the eventual executor run would fail the
same way).

Denial reasons now come from buildToolApprovalDenialReason(): edit tools
get 'The user denied this edit. The file was NOT modified and still
contains its original content.' (legacy parity), and all tools get user
feedback wrapped in <feedback> tags instead of the bare prompt as the
whole reason. isKnownToolApprovalDenial also matches the new edit-denial
marker so translator suppression keeps working.

* feat(vscode): simulated streaming animation for edit previews

Brings back the legacy 'yellow sweep' feel on the virtual diff preview.
The SDK only surfaces complete tool input, so this is a deliberate
simulation of the legacy streaming look (which legacy also showed when
it already had the full content in memory).

The sweep covers the whole file like legacy did, with diff-aware pacing:

- Park at the top: whole document under the faded-yellow overlay, cursor
  highlight on line 0, viewport pinned to the top, ~400ms hold so the
  animation unambiguously starts from the top.
- Zip through unchanged spans in small fast steps (~8 lines per 16ms
  frame, capped per span) so they read as continuous motion.
- Slow down through each change: one line per 45ms frame with a ~350ms
  minimum dwell per hunk so even a one-line change visibly pauses.
- Changed runs come from a real line diff (diffLines), so multi-hunk
  edits slow at EACH hunk and the gaps between hunks zip; pure deletions
  pause at the deletion point.
- Zip frames chase the cursor (InCenter) for continuous scroll; typing
  frames scroll only when leaving the viewport (no per-frame judder).
- After the sweep reaches the bottom: short beat, then settle centered
  on the first changed line for review.

Mechanics: edit previews move from base64-query cline-diff URIs to a new
mutable cline-edit-preview content provider (content set programmatically,
re-rendered via onDidChange) so the virtual right side can update in
place. DecorationController is reused as-is. The approval ask renders
while the animation plays (legacy simultaneity); close() cancels
mid-animation; files >3000 lines render the final diff immediately.
External hosts keep the static openMultiFileDiff preview.

* chore(vscode): remove test artifact comment from memory-monitor

* fix(vscode): address review nits — skip diff computation for large files, close partially-opened previews

- buildEditPreviewAnimation (which runs a full line diff) now runs after
  the MAX_ANIMATED_LINES guard; oversized files use a cheap prefix scan
  just to aim the viewport.
- If preview.open() throws after partially opening, the tab is closed
  directly — the session was never registered, so discardPreview could
  not have reached it.

* fix(vscode): keep tsconfig valid JSON for test setup

* fix(vscode): bound diff preview animation
2026-07-14 01:25:36 -07:00
Tomás Barreiro ab68fd7f34 Store startedAt in auth metadata when starting a Cline session (#12270)
* Store startedAt in auth metadata when starting a Cline session

* Inject the sessionStartedAt when creating the auth credentials

* Remove injecting sessionStartedAt when it's not stored already

* Address review

* fix merge inconsistencies
2026-07-14 03:50:00 +02:00
Saoud Rizwan b4ed8a226e chore(cli): release v3.0.40 2026-07-13 12:25:49 -07:00
Saoud Rizwan cbf40961db fix(hub): make markdown code component assignable to streamdown Components
The custom MarkdownCode node type used a narrow { metastring?: string }
shape that is not assignable from the hast Element passed by
react-markdown/streamdown, so a clean rebuild (fresh dependency resolve,
as done by the release version.ts) fails the `satisfies Components`
check. Widen node.properties to Record<string, unknown> and validate the
metastring value at read time.
2026-07-13 12:02:49 -07:00
Saoud Rizwan 2d05ba52da chore(sdk): release v0.0.60 2026-07-13 11:25:21 -07:00
Saoud Rizwan 5d3778b5cf feat(cli): manual API key escape hatch for Cline OAuth providers (#12254)
* feat(cli): manual API key escape hatch for Cline OAuth providers

Add a way to configure the cline / cline-pass providers with a dashboard
API key from the /settings provider flow, for users where OAuth login
isn't working:

- "Enter API key manually" option in the already-configured dialog
- K keybinding in the OAuth login dialog to switch to key entry
- Saving clears stored OAuth tokens (on both the shared cline storage
  entry and any direct cline-pass entry) since the auth handler prefers
  auth.accessToken over apiKey — a stale token would otherwise keep
  winning over the manual key
- isProviderConfigured now counts a persisted API key for OAuth
  providers so escape-hatch users aren't forced back into OAuth on
  every provider switch

* fix(cli): move API key fallback to OAuth dialog
2026-07-13 11:14:11 -07:00
Saoud Rizwan 8d0eb54a1f feat(telemetry): track auth refresh outcomes to measure the hard-logout fix (#12256)
* fix(sdk): stop misclassifying transient refresh failures as invalid_grant

getValidClineCredentials returned null for BOTH a rejected refresh token and
any transient error (network down, timeout, 5xx) that happened to land after
the access token expired. Callers treat null as 'session dead' — the
extension wipes providers.json over it, logging out every Cline process on
the machine, which is what CLI users then hit as 'Unauthorized: please
re-authenticate'. A laptop waking from sleep past the ~1h token expiry with
a background job (balance/banners/remote-config) refreshing before the
network is up was enough to trigger it — no refresh-token rotation involved.

Now: null means the refresh token was REJECTED (re-auth required); transient
failures throw so callers keep stored credentials and retry later. The
extension's refreshAccessToken catch and the CLI's error surface already
handle the throw correctly with no changes.

* fix(sdk): write providers.json atomically

providers.json was written with a bare writeFileSync while being read
concurrently by every other Cline process (CLI, extension, hub). A reader
catching a partial write parses garbage, which read() silently treats as
EMPTY settings — indistinguishable from being logged out — and any
subsequent save from that process persists the empty state, erasing every
configured provider.

Stage to a pid-unique temp file and rename into place; rename is atomic on
POSIX and replaces on Windows, so readers only ever see a complete file.

* feat(telemetry): track auth refresh outcomes to measure the hard-logout fix

Adds the observability needed to verify in production that the
transient-vs-invalid_grant fix is working, and to diagnose any logouts that
remain:

- user.auth_refresh_soft_failure — fires when a refresh fails for a reason
  that does NOT invalidate the session (network error, timeout, 5xx) and
  stored credentials were kept. Instances with tokenExpired=true were hard
  logouts before the fix, so this is the 'prevented logout' counter. Emitted
  from the SDK (CLI path) and from the extension's refresh/restore catches
  under the same event name so dashboards aggregate both clients.
- user.auth_logged_out now carries the HTTP status and errorCode that caused
  it, and the extension emits it (with a distinct reason) at every site that
  clears providers.json: refresh_rejected, restore_refresh_rejected, and
  handleDeauth's LogoutReason (user_initiated / cross_window_sync / …), which
  was previously accepted and ignored. Extension-triggered logouts were
  completely invisible before — including the legacy-extension cross-window
  cascade, which this now measures directly.

Success looks like: auth_logged_out volume drops after release while
auth_refresh_soft_failure appears in its place, and any remaining logouts
carry a reason/status we can act on.

* fix(telemetry): route auth refresh events through SDK
2026-07-13 11:13:05 -07:00
Saoud Rizwan a3989acc38 fix(sdk): don't log users out when token refresh fails due to network/server errors (#12255)
* fix(sdk): stop misclassifying transient refresh failures as invalid_grant

getValidClineCredentials returned null for BOTH a rejected refresh token and
any transient error (network down, timeout, 5xx) that happened to land after
the access token expired. Callers treat null as 'session dead' — the
extension wipes providers.json over it, logging out every Cline process on
the machine, which is what CLI users then hit as 'Unauthorized: please
re-authenticate'. A laptop waking from sleep past the ~1h token expiry with
a background job (balance/banners/remote-config) refreshing before the
network is up was enough to trigger it — no refresh-token rotation involved.

Now: null means the refresh token was REJECTED (re-auth required); transient
failures throw so callers keep stored credentials and retry later. The
extension's refreshAccessToken catch and the CLI's error surface already
handle the throw correctly with no changes.

* fix(sdk): write providers.json atomically

providers.json was written with a bare writeFileSync while being read
concurrently by every other Cline process (CLI, extension, hub). A reader
catching a partial write parses garbage, which read() silently treats as
EMPTY settings — indistinguishable from being logged out — and any
subsequent save from that process persists the empty state, erasing every
configured provider.

Stage to a pid-unique temp file and rename into place; rename is atomic on
POSIX and replaces on Windows, so readers only ever see a complete file.
2026-07-13 08:58:26 -07:00
Saoud Rizwan d199b1bff9 fix(desktop-app): allow loopback origins for Next dev resources (#12251)
Next 16 blocks dev-resource requests (/_next/webpack-hmr, dev fonts) from
origins that don't match the dev server's own hostname. Browsing the web
dev mode via 127.0.0.1 left the page hanging with 'Blocked cross-origin
request to Next.js dev resource' warnings. allowedDevOrigins is dev-only,
so production/Tauri builds are unaffected.
2026-07-12 22:18:45 -07:00
Saoud Rizwan d41eed1198 feat(desktop-app): env-configurable sidecar bind host, trusted origins, and webview WS endpoint (#12250)
* feat(desktop-app): env-configurable sidecar bind host, trusted origins, and webview WS endpoint

Allows running the desktop app's web dev mode (dev:web + dev:sidecar) inside
a Docker container with published ports:

- CLINE_SIDECAR_HOST: sidecar bind hostname (default remains 127.0.0.1)
- CLINE_SIDECAR_TRUSTED_ORIGINS: comma-separated extra browser origins for
  the sidecar's origin allowlist (validation itself stays on)
- NEXT_PUBLIC_SIDECAR_WS_ENDPOINT: overrides the webview's hardcoded
  ws://127.0.0.1:3126/transport fallback so a browser on the Docker host can
  dial the published port

All defaults are unchanged, so local/Tauri behavior is unaffected when the
env vars are absent. When bound to 0.0.0.0 the printed ready endpoint
advertises 127.0.0.1 since a wildcard bind is not dialable.

* chore(desktop-app): untrack next-env.d.ts

It was added to .gitignore previously but never removed from the index, so
it kept showing as modified: Next.js rewrites the routes.d.ts import path
depending on whether 'next dev' or 'next build' ran last. The file is
regenerated by Next on every dev/build run, and the app's typecheck
(tsconfig.dev.json) excludes webview/, so nothing needs it tracked.

* style(desktop-app): format SIDECAR_HOST declaration
2026-07-12 21:59:07 -07:00
Tomás Barreiro 6309971089 Add the ClinePass limit error to the CLI (#12191)
* Add the ClinePass limit error to the CLI

* Update apps/cli/src/runtime/run-agent.test.ts

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* format code and improve instructions

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-07-11 02:41:55 +02:00
Saoud Rizwan 2d2c669421 fix(cli): reload provider config when switching models (#12232) 2026-07-10 16:57:50 -07:00
Dominic Cooney c5f146a418 Add debug logging for Cline credential lifecycle (ENG-2213) (#12000)
* fix(auth): add early SDK debug logging for Cline credential lifecycle (ENG-2213)

Adds targeted debug-level logging at key points in the Cline/Cline Pass
credential lifecycle to diagnose intermittent logout issues. Credentials
are never logged in cleartext; an 8-hex-digit SHA-256 hash is used instead.

The SDK has two logger layers:
1. ClineCore.logger — session-scoped, threaded from ClineCore.create({logger})
   into session config and the agent event bridge.
2. setSdkLogger() — early/module-level, for components that operate before
   or outside of ClineCore sessions: ProviderSettingsManager (constructed
   at startup), RuntimeOAuthTokenManager, and cline.ts auth functions
   (token refresh). These can't be reached by the session-scoped logger.

Both VS Code (common.ts) and CLI (main.ts) call setSdkLogger() once at
startup. When no logger is registered (or the host filters out debug),
every call is a no-op — logging is never collected in normal use.

Instrumentation points (SDK core, shared by both surfaces):
- ProviderSettingsManager.read(): logs provider IDs, last-used, and whether
  Cline auth is present (with hashed access/refresh token fingerprints)
- ProviderSettingsManager.saveProviderSettings(): logs the provider being
  saved, tokenSource, whether Cline auth was present before/after, and
  flags authDropped when a previously-present Cline auth block disappears
- RuntimeOAuthTokenManager.resolveProviderApiKeyInternal(): logs each
  decision point (no_settings, no_credentials, refresh_start, refresh_null,
  refreshed+saved, not_refreshed) with hashed token fingerprints
- cline.ts refreshClineToken(): logs the refresh request URL, response
  status/errorCode on failure, and new token hashes on success
- cline.ts getValidClineCredentials(): logs the outcome at each branch
  (no_current_credentials, still_valid, needs_refresh, invalid_grant,
  transient_failure_kept_current, transient_failure_expired)

VS Code extension (auth-service.ts):
- readClineCredentials/writeClineCredentials/clearClineCredentials: logs
  credential presence and hashes at each disk I/O point
- refreshAccessToken: logs refresh start, null result (cleared), changed
  (written), or unchanged outcomes
- fetchUserInfoFromApi: logs the GET /api/v1/users/me request and response
  status

What to collect when investigating:

VS Code extension:
- Open the "Cline" output channel (View -> Output -> select "Cline")
- Look for lines containing: [SdkAuthService], providers.read,
  providers.save, oauth.resolve, cline.refresh, cline.getCredentials
- Debug logging is emitted at the DEBUG level; it appears in the output
  channel when IS_DEV=true or in development builds

CLI:
- Set CLINE_LOG_LEVEL=debug environment variable before running cline
- Collect the log file at ~/.cline/data/logs/cline.cli.log (or the path
  set by CLINE_LOG_PATH)
- Look for the same event names as above

Files changed:
- sdk/packages/core/src/auth/auth-debug.ts (NEW): hashSecret,
  setSdkLogger, getSdkLogger, sdkDebug
- sdk/packages/core/src/auth/cline.ts: refresh/getCredentials logging
- sdk/packages/core/src/services/storage/provider-settings-manager.ts:
  read/save logging
- sdk/packages/core/src/runtime/orchestration/runtime-oauth-token-manager.ts:
  resolve logging
- sdk/packages/core/src/index.ts: export early logger utilities
- apps/vscode/src/sdk/auth-service.ts: credential lifecycle logging
- apps/vscode/src/common.ts: register SDK early logger
- apps/cli/src/main.ts: register SDK early logger

* fix(vscode): inline SDK debug metadata into log message string (ENG-2213)

* fix(auth): gate debug logging on CLINE_LOG_LEVEL at runtime (ENG-2213)

* fix(auth): use interpolated debug strings, remove log-level gating (ENG-2213)

* refactor: move early logger to sdk/packages/core/src/logging/early-logger.ts

* fix: address review feedback — early logger registration, log after write, remove getSdkLogger from public API

* fix(vscode): add ISO timestamps to all log lines

* fix core import

* fix import

* fix tests

---------

Co-authored-by: Tomás Barreiro <52393857+BarreiroT@users.noreply.github.com>
Co-authored-by: BarreiroT <tomasmbarreiroi@gmail.com>
2026-07-10 23:40:46 +02:00
Saoud Rizwan 261ee4c313 fix(vscode): show requested line range on read-file chat rows (#12225)
The webview already knew how to render readLineStart/readLineEnd on
readFile tool rows, but the SDK message translator never populated
them, so successive ranged reads of the same file all rendered as
identical bare paths. Extract start_line/end_line from read_files
input (per-file and single-path forms) and render open-ended reads
(start_line only) as "start+".
2026-07-10 13:25:30 -07:00
Saoud Rizwan d45b051c04 fix(cli): detect bun global installs after symlink resolution in auto-update (#12224) 2026-07-10 12:15:26 -07:00
Bee 78c83cdf33 fix(cli): preserve session id when in same session (#12188) 2026-07-10 17:59:55 +08:00
Bee 3266121fa1 feat(desktop): add typography spec (#12215)
* feat(desktop): add typography spec

* remove unused background component
2026-07-09 19:34:28 -07:00
Bee 6467de65a2 fix(plugin): follow up fix for agent-squad (#12216)
Follow up on my last PR where the last commit revert the removal of the regex field from zod schema
2026-07-09 19:33:04 -07:00
Bee 65fe885638 fix(sdk): remove regex from zod schema for agent-squad plugin example (#12214)
* fix(sdk): remove regex from zod schema for agent-squad plugin example

The `HandoffPathInput` schema used negative lookaheads to reject absolute paths and `..` traversal segments. When converted to JSON Schema, this regex caused consumers without lookaround support to fail with `invalid JSON schema: regex lookaround is not supported`.

This change removes the lookaround-based regex from the published schema and moves those checks to runtime validation. It preserves validation for allowed characters, absolute paths, traversal segments, and maximum length while strengthening cross-platform directory containment checks using Node’s path utilities.

* add back logger examples
2026-07-10 10:19:27 +08:00
Bee c3033d6f13 fix(vscode): refreshGroqModels caused cacheReadsPrice undefined error (#12213) 2026-07-10 08:52:06 +08:00
Max cfb1327a1b fix vscode hmr not working (#12212) 2026-07-09 16:49:35 -07:00
Alex Taboada 264af96e1b fix(vscode): prevent infinite loading when initializing task with an image (#12203) 2026-07-09 18:16:52 +02:00
Robin Newhouse 10cb9bd97a Add compaction budget hardening (#12142)
* Add compaction budget projection contract

* Tighten budget projection contract types

* Tighten dropped block action paths

* Add pure compaction budget projection engine

* Fix budget projection truncation accounting

* Drop provider-native blocks during budget projection

* Recompute protected tail after thinking pruning

* Align budget projection test tool results

* Clean up budget projection fixture indentation

* fix(core): narrow compaction protected tail

* Fix budget projection action accounting

* Budget agentic compaction summary input

* Harden agentic summary budget fallback

* Align agentic compaction test tool result

* Align agentic file ops with projected input

* Budget basic compaction projections

* Clarify basic projection budget logging

* Align basic sanitization image expectation

* Align basic compaction budget expectation

* Emit compaction budget emergency telemetry

* Tighten compaction budget telemetry types

* Preserve compaction status notice reasons

* fix(core): account compaction tokens consistently

* fix(core): align skipped compaction token accounting
2026-07-09 02:15:55 -07:00
Saoud Rizwan 2ee18e7f0c chore(cli): release v3.0.39 2026-07-08 21:15:22 -07:00
Saoud Rizwan 0b65506a2b chore(sdk): release v0.0.59 2026-07-08 20:00:37 -07:00
Saoud Rizwan 3502608081 fix(telemetry): emit telemetry from the detached hub daemon process (#12177)
* feat(sdk): emit telemetry from the hub daemon process

The detached hub daemon hosts the LocalRuntimeHost that emits
task.conversation_turn and task.tokens for every hub-backed session
(CLI in prefer-hub mode, desktop app, connectors), but the daemon
entrypoint never created a telemetry handle - startHubWebSocketServer
received telemetry: undefined and every capture in the daemon-side
runtime was a no-op. Sessions billed normally on the backend while
reporting nothing to OTel.

- create a ConfiguredTelemetryHandle in the daemon entry and pass it to
  the websocket server and schedule runtime handlers
- identify from the cached cline account at startup and re-resolve
  periodically, since the long-lived daemon often starts before login
  or outlives an account switch
- flush and dispose the handle on graceful and fatal shutdown

* fix(sdk): flush daemon telemetry when server startup fails

If startHubWebSocketServer throws, dispose the telemetry handle before
rethrowing so failed daemon starts are visible in telemetry instead of
dying silently.

* fix(sdk): bound daemon telemetry flush and reuse settings manager

- Race dispose's flush against a 5s deadline so a hung exporter can't
  keep a crashed daemon alive holding the hub port (before this PR the
  daemon exited immediately on fatal errors; the flush must not change
  that materially).
- Construct ProviderSettingsManager once instead of every identity
  refresh; its constructor runs legacy-migration and provider
  registration side effects, and getProviderSettings re-reads the file
  per call anyway.
- Test the dispose-on-startup-failure path and the cline-hub-daemon
  platform metadata.

* fix(sdk): label daemon telemetry cline_type as hub

Review feedback from @abeatrix: daemon-hosted sessions can be triggered
by the CLI, desktop app, or connectors, so daemon-emitted events should
not share the CLI process's cline_type. Existing values are "cli" and
"VSCode Extension"; the daemon now reports "hub" (with the finer
platform=cline-hub-daemon kept as-is).
2026-07-08 19:35:26 -07:00
Saoud Rizwan ed3107f9ec Revert "docs: add Cline free models page (#12183)" (#12185)
This reverts commit 6bce48aad4.
2026-07-08 19:32:36 -07:00
Renee Huang 6bce48aad4 docs: add Cline free models page (#12183)
Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-07-08 19:22:32 -07:00
Bee 1e1b6af51c fix(sdk): set versioned Cline client-identity headers for Cline provider (#12182)
* fix(sdk): set versioned Cline client-identity headers for Cline provider

* address feedback

* feat: add platform metadata to client context

Include platform, platformVersion, and isMultiRoot in extension client
context for CLI, ACP, and VS Code sessions. This provides downstream
core/session logic with richer runtime information and distinguishes ACP
clients from the standard CLI client.

* lint

* clean up

* fix: resolve client host identity via HostProvider for standalone compatibility

cline-session-factory.ts is also bundled into the standalone cline-core
(JetBrains), where the 'vscode' module resolves to the generated Proxy-stub
module: vscode.env.appName and vscode.version return Proxy objects, which
would flow into X-PLATFORM/X-PLATFORM-VERSION header values and fail at
request serialization.

Resolve the identity through HostProvider.env.getHostVersion() instead —
the VS Code hostbridge returns the identical values (vscode.env.appName,
vscode.version, ClineClient.VSCode, extension version), and JetBrains'
hostbridge returns its real host values, so the standalone stops reporting
itself as the VS Code extension as a bonus. Multi-root detection goes
through HostProvider.workspace.getWorkspacePaths() for the same reason.
Both resolvers degrade gracefully (undefined/false) if the host bridge is
unavailable, in which case the header builder falls back to source-derived
values.

* Add unit test as proof

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-07-08 19:08:25 -07:00
Saoud Rizwan ee49900232 chore(greptile): update telemetry review rules for the monorepo layout (#12180)
The .greptile config was written for the pre-merge standalone cline/sdk
repo and never updated after the monorepo merge:

- the sdk-telemetry-doc-update rule enforced an Event Catalog in DOC.md,
  a file that does not exist in this repo (it now emits a false P2 on
  every PR touching core-events.ts, e.g. #12177)
- rules.md cited PR #357, apps/vscode/src/hub-daemon.ts, and
  apps/vscode/src/telemetry.ts - none of which exist here
- the 'Hub Daemon Metadata Forwarding' section described an argv-based
  metadata payload that was never implemented in this repo; replaced
  with the actual daemon-owned telemetry pattern from #12177
- the opted-out-test rule now describes the real convention: assert the
  event flows through capture (no-op for OptedOutTelemetryService), not
  captureRequired
2026-07-08 16:43:14 -07:00
Saoud Rizwan a1d5589d19 feat: allow selecting Cline free models on the ClinePass provider (#12140)
* feat(llms): include Cline free models in the cline-pass catalog

* feat(vscode): show Subscribed/Free model tabs on the ClinePass provider

* feat(cli): show Subscribed/Free sections in the ClinePass model picker

* fix(cli): drop redundant browse-all entry from ClinePass picker

* fix(cli): show only subscribed models in ClinePass onboarding picker

* feat(cli): include free models and quota explainer in ClinePass onboarding picker

* fix: shorten ClinePass free section copy

* fix(cli): strip redundant free markers from sectioned picker names

* fix: drop free from ClinePass free section copy

* fix: tighten ClinePass free section copy

* refactor: address review feedback on ClinePass free models

- single buildFeaturedModelEntries(providerId) dispatcher, builders private
- rename isClineProvider to isClineManagedProvider (includes cline-pass)
- use isClineManagedProvider in the free-model cost check
- themed tab border, pretty names on free model cards
- clearer cline-pass cost test name

* fix: address ClinePass free-model review blockers

- Stop re-sorting the cline-pass live catalog by release date in
  mergeKnownModels: free models carry OpenRouter release dates, so the
  sort could put a free model first and make it the fallback default
  when the bundled default id rotates out of the live clinePass bucket.
  Preserve the normalize-time order (pass models first) and pin it with
  an end-to-end resolveProviderConfig test.
- Add the browse-all escape to the CLI ClinePass picker when the
  clinePass bucket is empty (bundled fallback after a fetch failure),
  so a subscriber isn't left with a free-models-only picker.
- Rename ErrorRow's local isClineManagedProvider to
  isClineUsageBillingProvider: it only matches the cline provider,
  unlike the shared util of the same name that also matches cline-pass.
2026-07-08 15:08:27 -07:00
Tomás Barreiro 721fda2e99 Add ClinePass limit error (#12162)
* Add ClinePass limit error

* refactor regex

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-07-08 14:54:20 -07:00
Saoud Rizwan 5e78861eb5 fix(vscode): update ClinePass onboarding option copy (#12173) 2026-07-08 13:21:12 -07:00
Robin Newhouse 29798f59f3 Persist VS Code manual compaction sidecar (#11900)
* Persist VS Code manual compaction sidecar

* Fix compaction test isolation

* Address PR feedback on compaction comments

* Fix compaction test core mock hoisting

* fix(vscode): avoid compaction session rebuild

* fix(core): validate active compaction from persisted transcript

* fix(vscode): harden manual compaction sidecar flow
2026-07-08 13:18:22 -07:00
Ara 177d0eb07f Remove Cline model picker recommendation copy (#12170) 2026-07-08 12:15:53 -07:00
Bee 869a87a220 fix(core): use no-emit TypeScript config for checks (#12139)
* fix(core): use no-emit TypeScript config for checks

Update the core package TypeScript config to run checks without emitting files,
allowing broader workspace sources via the package parent rootDir. Simplify the
dev config so it only extends the main package config and avoids duplicated
compiler overrides.

* feedback

* remove dead code
2026-07-08 11:15:16 -07:00
Max 0cfd0bbe05 Fix VS Code F5 webview debug flow (#12027)
* fix vscode f5 settings

- fixed the hot module reloading issue while debugging the extension.
- also fixed issue where deb:webview task wasn't showing as complete

* fix vscode webview dev cleanup

---------

Co-authored-by: Max Paulus 🥪 <max@cline.bot>
2026-07-08 10:53:41 -07:00
yanalialiuk 08f656532f docs: add Atomic Chat local provider setup guide (#11966)
* docs: add Atomic Chat local provider setup guide

Document Atomic Chat alongside Ollama and LM Studio in the local models
overview and add a dedicated provider configuration page.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Update overview.mdx

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Renee Huang <100229782+reneehuang1@users.noreply.github.com>
2026-07-08 10:48:58 -07:00
Tomás Barreiro 10dece6677 Remove all ClinePass GLM 5.1 references (#12107)
* Remove all ClinePass GLM 5.1 references

* fix other references
2026-07-08 14:59:10 +02:00
Sufiyan Khan 885a2936b6 docs(authorizing): remove model-specific wording from generic setup step (#12156)
Step 4 in the IDE setup flow says 'Choose your desired Claude model'
but applies to all providers (OpenAI, Gemini, DeepSeek, local, etc.).
Drop 'Claude' to keep it provider-agnostic.
2026-07-08 21:22:33 +09:00
Dominic Cooney 90c427740d perf(sdk): stop listSessions hot loop from hanging the extension host (#11967)
* perf(sdk): stop listSessions hot loop from hanging the extension host

getStateToPostToWebview rebuilt the full task history on nearly every
streaming/session event, and each rebuild ran persistence-service.listSessions,
which synchronously read + Zod-parsed every session manifest. The 10s metadata
cache meant to absorb this was wiped on every per-turn updateTaskUsage, so each
state post paid the full synchronous scan, saturating the extension-host event
loop (observed as a tight listSessions/readFileUtf8 loop in CPU profiles).

- Debounce/coalesce postStateToWebview: trailing 50ms debounce plus a single
  queued follow-up so bursts collapse into one rebuild; dispose() tears it down.
- Add an async, title-only manifest reader (readSessionManifestTitle) and use it
  in listSessions to resolve titles concurrently off-thread, instead of a
  synchronous readFileSync + full SessionManifestSchema (Zod) parse per row. The
  existing sync manifest methods are left intact.
- On single-session updates, patch just the changed record in the merged-history
  cache in place instead of invalidating it, so frequent per-turn usage updates
  no longer force the next state post to re-enumerate and re-merge every session.

* refactor(sdk): strengthen session history cache patching

Replace patchMetadataHistoryCacheRecord (boolean-returning, metadata-only,
no re-sort) with updateCachedSessionRecord (void, updates prompt +
metadata + updatedAt, re-sorts via shared comparator).

- Void return eliminates the ignorable fallback contract.
- Mirrors all fields the persistence layer writes (prompt, metadata,
  updatedAt) so cache and disk stay consistent.
- Re-sorts after patching so the updated record bubbles to the correct
  position, using a shared compareSessionHistoryRecordsByRecencyDesc
  comparator also used by listHistory.
- Derives updatedAt from the HistoryItem timestamp instead of constructing
  a second clock value.
- Self-invalidates on cache miss so callers never manage the fallback.

Adds tests for in-place patching, re-sorting, per-turn usage hot path,
and cache-miss invalidation.

* fix(sdk): await in-flight state post during dispose

Greptile feedback: dispose() did not await a concurrently-running
runDebouncedStatePost, so an in-flight flushStateToWebview could access
torn-down resources after disposal.

Track the runDebouncedStatePost promise in statePostInFlightPromise.
In dispose(), after setting isDisposed and clearing the timer, await
the in-flight promise (swallowing errors) before tearing down downstream
resources. The !this.isDisposed guard in the loop prevents further
iterations after disposal.

* fix(sdk): address review feedback on state-post debounce and cache patch

Three issues from code review of the listSessions hot-loop fix:

1. dispose() could await the wrong promise. A second debounced timer
   firing while a flush was already running overwrote
   statePostInFlightPromise with a throwaway resolved promise from the
   join path, so dispose() could return while the original flush was
   still executing. Extract the debounce/coalesce state machine into
   StatePostDebouncer, and only track the promise from the call that
   actually starts a new flush loop.

2. postStateToWebview() swallowed flush errors, resolving every pending
   caller even when flushStateToWebview() threw. Callers awaiting
   postStateToWebview() now see the rejection, matching pre-debounce
   behavior.

3. Cache patching derived the cached updatedAt from HistoryItem.ts,
   but the persistence adapter always stamps updatedAt with the
   wall-clock write time. Callers like toggleTaskFavorite() reuse an
   old HistoryItem whose ts predates the write, which let the cached
   ordering diverge from disk until the 10s TTL expired. Stamp the
   cache patch with the write time instead.

Adds unit tests for StatePostDebouncer covering the dispose race and
error-propagation regressions, and a sdk-task-history test for the
stale-updatedAt cache-ordering regression.

* fix(sdk): don't patch cache when session update write didn't land

Beatrix's review feedback: updateSession() ignored the { updated:
boolean } result from host.update() and unconditionally patched the
metadata cache. When persistence returns updated: false (session
deleted/missing, or an optimistic-concurrency retry exhausted by a
racing writer), the webview could show a fake updated record until the
cache TTL expired.

Check the write result: only patch the cache when updated === true,
otherwise invalidate it so the next read re-enumerates from disk.

---------

Co-authored-by: Cline Agent <cline-agent@users.noreply.github.com>
2026-07-08 13:23:18 +09:00
alex-lum e6028168f2 fix(sdk/cli): emit user_id in SDK/CLI telemetry identity attributes (CLINE-2406) (#11581)
* fix(sdk/cli): emit user_id in telemetry identity attributes

Per CLINE-2406, downstream analytics expects an explicit user_id field
in authenticated SDK/CLI OpenTelemetry log attributes.

Changes:
- sdk/packages/core/src/services/telemetry/core-events.ts: add
  user_id: account.id alongside the existing account_id in
  identifyAccount() updateCommonProperties call.
- sdk/packages/core/src/services/telemetry/core-events.test.ts: new
  identifyAccount suite verifying user_id, account_id, distinct_id, and
  org context fields for authenticated user without org, with active org,
  absent/blank id handling, and no-op when telemetry is undefined.
- apps/cli/src/main.ts: after loading Cline provider settings in the
  runtime path, read auth.accountId and call identifyTelemetryAccount so
  subsequent task.* and workspace.* events carry user_id. Document
  user.extension_activated as pre-auth by design for subcommand flows.
- apps/cli/src/main.test.ts: three new tests covering saved accountId
  triggers identity, missing accountId skips identity, non-Cline
  provider skips identity.

* fix(sdk/cli): address review feedback on telemetry identity

- Use trimmed distinctId for user_id in identifyAccount() to keep
  user_id and distinct_id consistent when IDs have whitespace
- Remove fragile type cast in CLI main.ts; ProviderSettings already
  exposes auth.accountId via AuthSettingsSchema
2026-07-07 19:20:03 -07:00
Bee c3f75b3ff0 chore: Cline Code Desktop App update (#12012)
* wip: Cline Code Desktop App

Add Bun/Tauri desktop packaging commands for macOS, Windows, and Linux, including output to dist/desktop. Enforce macOS signing and notarization requirements for shareable builds while allowing an explicit unsigned local test path.

Document desktop packaging prerequisites, ignore generated build artifacts, and wire runtime session connection updates needed by the desktop app.

Clean up and update sidecar functions.
Safe to merge as this is not a published app.

* fixes

* chat

* apply

* ClinePass support

* add build instructions and use system theme

* fix: diff status

* update tool calls display

* connection updates

* lint fix

* fix keydown
2026-07-08 09:08:34 +08:00
Bee 88ce3e0b11 fix(core): emit accurate str_replace diffs (#12102)
* fix(core): emit accurate str_replace diffs

* fixes
2026-07-07 16:02:17 -07:00
Bee dd719dce86 fix(llms): OpenAI Codex model metadata for GPT Subscription provider (#12129)
* fix(llms): OpenAI Codex model metadata for GPT Subscription provider

* add unit tests

* Update stale unit tests

* clarify doc string

* Update docs format

* update old test
2026-07-07 15:57:21 -07:00
Robin Newhouse d5db7eb853 Preserve canonical session history during compaction ENG-1967 (#10651)
* Preserve canonical history with compaction sidecar

* Clarify prepareTurn request projection semantics

* Harden hub compaction sidecar ownership

* Handle compaction sidecar edge cases

* Address compaction sidecar review feedback

* Tighten compaction sidecar safety

* Extract atomic session file writes

* Assert compaction boundary role delimiter

* Simplify compaction source hashing

* Fix compaction smoke test type guard

* Fix async interactive runtime tests

* Avoid dangling compaction path in manifests

* Address compaction sidecar review nits

* fix(cli): await async runtime helper in restart test
2026-07-07 13:19:05 -07:00
Max 11d5ebe8bc chore: schedule nightly VS Code extension publish (#12124)
Co-authored-by: Max Paulus 🥪 <max@cline.bot>
2026-07-07 10:30:04 -07:00
Saoud Rizwan 6f7cc4907f chore(cli): release v3.0.38 2026-07-06 19:08:18 -07:00
Saoud Rizwan 27e3541569 chore(sdk): release v0.0.58 2026-07-06 18:52:52 -07:00
Bee ae9c5b4d9d fix(core): tolerate orphan line-range entries in read_files input (#12104)
Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-07-06 18:48:50 -07:00
Bee f86ca6b36b fix(test): fix stale palette tests (#12105)
Commit 9a9300846 ("restyle chat input…", which folded in PR #12075 "replace cyan accent with new plan/act palette") deliberately rebranded the TUI accents in palette.ts:

Dark act: ANSI "cyan" → #79b8ff (and plan "yellow" → #ffea7f, success "brightGreen" → #99e89b)
Light act: #0969da → #0f72cb (and plan #9a6700 → #867100), re-derived in OKLCH to keep the same hue as the new dark accents with ≥4.5:1 contrast on white
But palette.test.ts:27-35 still asserts the old values ("preserves the existing named ANSI colors" — a test description that's now literally obsolete). So getModeAccent("act", "dark") correctly returns #79b8ff, and the test expecting "cyan" fails.

The fix is to update the two tests to the new palette values (and rename the first test, since the colors are no longer named ANSI colors).
2026-07-06 18:23:49 -07:00
Saoud Rizwan 0e0b11032e chore(sdk): release v0.0.57 2026-07-06 18:06:43 -07:00
Saoud Rizwan a93d850aee feat(cli): tint assistant markdown accents by the mode they were produced in (#12101)
* feat(cli): tint assistant markdown accents by the mode they were produced in

Markdown's prominent elements (headings, bold, list markers, links) were
hardcoded to the act accent. getSyntaxStyle now takes the entry's mode
and colors those elements with the matching accent -- plan segments
render yellow-tinted markdown, act segments blue -- completing the
per-mode transcript coloring. Code token colors stay constant across
modes; styles are cached per theme+mode pair. Unstamped entries follow
the current mode, same fallback as the glyph accent.

* fix(cli): resolve entry mode once for glyph and markdown accents

Address review: the accent and mode props used parallel fallback chains
that could drift; both now derive from a single resolved entryMode. Also
cover the light-theme plan/act markdown accents in tests.
2026-07-06 17:21:45 -07:00
Saoud Rizwan fe25258b0d feat(cli): polish status bar usage display and ClinePass model name (#12077)
* feat(cli): restyle chat input with horizontal rules and slim user bubbles

Replace the tinted-background input field with a minimal frame: full-width
horizontal rules above and below the textarea and a bold accent-colored
prompt glyph, with no background fill. User message bubbles in the
transcript match the new look: a slim neutral-gray bar with the same
glyph, no vertical padding, and no mode-colored tint.

Palette gains getInputRuleColor (neutral adaptive mid-gray for the rules)
and getUserMessageBackground (neutral bubble tint), both built on a shared
OKLAB lift helper extracted from getModeInputBackground. Home view's
robot cursor-tracking offset is adjusted for the input's new left edge,
and the spacer line between the chat input and status bar is removed.

* feat(cli): replace cyan accent with new plan/act palette

Swap the TUI accent colors: act mode goes from ANSI cyan to #79b8ff and
plan mode from ANSI yellow to #ffea7f on dark themes. Light themes get
counterparts derived in OKLCH with the same hue but darkened to hold
>=4.5:1 contrast on white (#0f72cb act, #867100 plan), selected via the
existing getModeAccent theme switch.

All hardcoded "cyan" fg literals across dialogs, model selector, config
view, and onboarding now reference palette.act so the accent is a single
source of truth. The selection highlight follows the act color. The
subliminal OKLAB chroma nudge on input backgrounds/foregrounds now leans
blue (-a, -b) instead of cyan (-a, +b) to match the new accent hue.

* feat(cli): soften success green and use act accent in markdown

Swap the success/diff green from ANSI brightGreen and #22c55e to a muted
sage #87af87 on dark themes (auto-approve banner, git diff +stats, diff
view added-sign color); light themes keep the darker #116329 for contrast
on white. Markdown prominent elements (headings, bold, list markers,
links, table headers) now use the act accent via themePalette instead of
hardcoded one-dark cyan #56b6c2.

* feat(cli): harmonize dark syntax colors with brand accent palette

Rebuild the dark syntax highlighting family around the brand anchors:
functions use the act blue, strings and inline code use the success sage,
types/italics use a dimmed plan yellow, and the remaining hues (keyword
purple, variable coral, number orange, operator ice-blue) are regenerated
in OKLCH at the same pastel lightness/chroma weight (~L 0.78, C 0.11) so
code blocks read as part of the same palette. Light theme keeps its
GitHub-light set.

* fix(cli): brighten success green to match accent palette weight

#87af87 sat at roughly half the OKLCH chroma of the act/plan anchors and
read as gray next to them; #8bd28d (L 0.80, C 0.12) matches their weight.

* fix(cli): brighten success green a step further (#99e89b)

* feat(cli): polish status bar usage display and ClinePass model name

- Cost always renders with two decimals ($0.00) instead of switching to
  four decimals under a cent; the turn summary line drops its three-decimal
  format for the same reason.
- Token count next to the context bar is now just the number; the word
  'tokens' was redundant with the bar right beside it.
- Context window bar shrinks from 8 to 6 cells.
- ClinePass models resolve their friendly models.dev name like every other
  provider and get a (ClinePass) suffix: 'GLM 5.2 (ClinePass)' instead of
  'ClinePass/glm-5.2'.
- ClinePass no longer shows '$0.00 (included with subscription)' -- cost is
  simply hidden for subscription providers.

* fix(cli): place ClinePass suffix after reasoning effort in model name

* fix(cli): format ClinePass model name as 'ClinePass: <model>' prefix

* feat(cli): color transcript entries by the mode they were produced in (#12083)

* feat(cli): color transcript entries by the mode they were produced in

Previously the whole transcript retinted to the current mode's accent on
every plan/act toggle. Entries now record the agent mode active when
they were produced and keep that accent permanently, so a session reads
as a visible history of plan (yellow) and act (blue) segments.

How the mode is captured:
- Live sessions: appendEntry in SessionProvider stamps entries from a
  uiMode ref, covering every creation site including mid-run
  switch_to_act_mode flips (which already call setUiMode through the
  runtime dialog bridge).
- Resumed sessions: hydrateSessionMessages recovers the mode from the
  persisted <user_input mode="..."> wrappers via a new shared
  parseUserInputMode helper, and flips to act at switch_to_act_mode tool
  calls. Transcripts without wrappers stay unstamped and keep the
  current-mode fallback accent, matching the old behavior.
- Restores: the /history resume and checkpoint-restore paths insert
  hydrated history via replaceEntries instead of appendEntry loops, so
  live-entry stamping cannot overwrite hydration's stamps (which would
  lock resumed transcripts to the resume-time accent).

The load-bearing core fix: readPersistedMessagesFile stripped the
user_input wrappers and mode notices from user text on every read
('display sanitization'). That read path also feeds session restarts
(mode toggle, compaction-mode change, model change, fork, recovery),
which re-persist what they read -- so every restart laundered the mode
markers off disk and out of the model's seeded context, leaving nothing
for hydration to recover. Reads now return persisted messages verbatim
and formatting is the display surface's job: the CLI TUI, history
titles, and the VS Code SDK history loader already formatted at their
boundaries; the cline-hub webview history mapping and the CLI HTML
export (which used normalizeUserInput and leaked mode_notice text) now
do too. Connectors only surface assistant text, and the remaining
readMessages consumers are programmatic (usage math, re-seeding,
compaction input) where raw is correct.

* fix(shared): match parseUserInputMode exactly to what the writer emits

Drop the case-insensitive flag and the 'zen' value from the wrapper
regex: formatUserInputBlock only ever writes lowercase act/plan/yolo, so
anything else the parser accepted (uppercase look-alikes in adversarial
content, a zen value with no writer) could never be real persisted data.
2026-07-06 16:00:17 -07:00
Saoud Rizwan 53d1567731 feat(cli): default thinking level picker cursor to Medium instead of Off (#12092)
* feat(cli): default thinking level picker cursor to Medium instead of Off

* chore(cli): drop explanatory comments from thinking level defaults
2026-07-06 15:51:40 -07:00
Saoud Rizwan 9a93008463 feat(cli): restyle chat input with horizontal rules and slim user bubbles (#12074)
* feat(cli): restyle chat input with horizontal rules and slim user bubbles

Replace the tinted-background input field with a minimal frame: full-width
horizontal rules above and below the textarea and a bold accent-colored
prompt glyph, with no background fill. User message bubbles in the
transcript match the new look: a slim neutral-gray bar with the same
glyph, no vertical padding, and no mode-colored tint.

Palette gains getInputRuleColor (neutral adaptive mid-gray for the rules)
and getUserMessageBackground (neutral bubble tint), both built on a shared
OKLAB lift helper extracted from getModeInputBackground. Home view's
robot cursor-tracking offset is adjusted for the input's new left edge,
and the spacer line between the chat input and status bar is removed.

* feat(cli): replace cyan accent with new plan/act palette (#12075)

* feat(cli): replace cyan accent with new plan/act palette

Swap the TUI accent colors: act mode goes from ANSI cyan to #79b8ff and
plan mode from ANSI yellow to #ffea7f on dark themes. Light themes get
counterparts derived in OKLCH with the same hue but darkened to hold
>=4.5:1 contrast on white (#0f72cb act, #867100 plan), selected via the
existing getModeAccent theme switch.

All hardcoded "cyan" fg literals across dialogs, model selector, config
view, and onboarding now reference palette.act so the accent is a single
source of truth. The selection highlight follows the act color. The
subliminal OKLAB chroma nudge on input backgrounds/foregrounds now leans
blue (-a, -b) instead of cyan (-a, +b) to match the new accent hue.

* feat(cli): soften success green and use act accent in markdown

Swap the success/diff green from ANSI brightGreen and #22c55e to a muted
sage #87af87 on dark themes (auto-approve banner, git diff +stats, diff
view added-sign color); light themes keep the darker #116329 for contrast
on white. Markdown prominent elements (headings, bold, list markers,
links, table headers) now use the act accent via themePalette instead of
hardcoded one-dark cyan #56b6c2.

* feat(cli): harmonize dark syntax colors with brand accent palette

Rebuild the dark syntax highlighting family around the brand anchors:
functions use the act blue, strings and inline code use the success sage,
types/italics use a dimmed plan yellow, and the remaining hues (keyword
purple, variable coral, number orange, operator ice-blue) are regenerated
in OKLCH at the same pastel lightness/chroma weight (~L 0.78, C 0.11) so
code blocks read as part of the same palette. Light theme keeps its
GitHub-light set.

* fix(cli): brighten success green to match accent palette weight

#87af87 sat at roughly half the OKLCH chroma of the act/plan anchors and
read as gray next to them; #8bd28d (L 0.80, C 0.12) matches their weight.

* fix(cli): brighten success green a step further (#99e89b)
2026-07-06 15:50:50 -07:00
Saoud Rizwan 678b0ae951 docs: polish README model table grammar and wording (#12098)
Claude-Session: https://claude.ai/code/session_017VJNCE1o6zzcVfpjpTVnt5

Co-authored-by: Claude <noreply@anthropic.com>
2026-07-06 15:29:12 -07:00
cline-cloud[bot] 8b6f2cf0b7 Raise live catalog default input tokens (#11930)
* fix(llms): raise catalog default input tokens

* Lower live catalog default input tokens to 128000

* Lower compaction DEFAULT_MAX_INPUT_TOKENS to 128000

---------

Co-authored-by: Cline Bot <bot@cline.bot>
Co-authored-by: John Simone <john@cline.bot>
2026-07-06 10:48:51 -07:00
Saoud Rizwan 25ef0939cc chore(cli): release v3.0.37 2026-07-03 19:31:47 -07:00
Saoud Rizwan 4f770011e9 chore(sdk): release v0.0.56 2026-07-03 19:11:03 -07:00
Saoud Rizwan a1d69fee6f fix(llms): stop AI SDK from rejecting malformed tool calls before flexible tool executors can handle them (#12061)
* fix(llms): stop rejecting malformed tool calls before tools can handle them

Weak models emit tool calls with schema mismatches (bare string for a
string[] arg) or unparsable JSON. The AI SDK adapter rejected both
before execution, so the lenient union schemas in the core tool
executors never ran. Drop the strict validate callback (tools own input
validation) and add experimental_repairToolCall backed by the shared
jsonrepair parser for arguments that fail JSON parsing.

* docs(llms): fix stale comment referencing removed validate callback
2026-07-03 18:55:31 -07:00
Saoud Rizwan 3575b38122 fix(shared): stop deleting mode_notice from outbound prompts (#12058)
* fix(shared): stop deleting mode_notice from outbound prompts

The mode-switch notice from #12057 never reached the model:
prepareTurnInput sanitizes every outbound prompt with normalizeUserInput
before wrapping it, and #12057 put the mode_notice strip inside
normalizeUserInput -- so the host deleted the notice on every send. The
transcript confirms it: messages sent after a toggle carry the
user_input wrapper but no notice, and models asked about it confabulate
having seen one because the system prompt describes the tag.

Move the strip into a dedicated stripModeNotices() applied only at
display boundaries: formatDisplayUserInput (TUI hydration, title
inference), deriveTitleFromPrompt (session titles), and the TUI queued
prompt echo. normalizeUserInput now preserves notices, with a
regression test pinning the outbound behavior. Side benefit: notices
survive the message-builder history normalization and the pending
prompt queue, so queued sends deliver them too.

* docs(shared): correct formatModeSwitchNotice JSDoc after strip relocation

* refactor(shared): generalize notice stripping to stripTagElements

stripModeNotices becomes a thin policy wrapper (DISPLAY_HIDDEN_TAGS owns
the what-to-hide list in one place) over a generic stripTagElements that
removes whole elements for any tag list -- the remove-element counterpart
to xmlTagsRemoval. Call sites at display boundaries now carry comments
explaining why stripping happens there and not in normalizeUserInput,
which also sanitizes model-bound prompts.

* revert(shared): drop stripTagElements generalization, keep simple stripModeNotices

The generic tag stripper added API surface without a second use case;
stripModeNotices goes back to the direct implementation. The display-vs-
model call-site comments from the same commit stay.
2026-07-03 15:45:34 -07:00
Saoud Rizwan b5468e1227 feat(cli): make plan/act mode switches visible to the model (#12057)
* feat(cli): make plan/act mode switches visible to the model

The mode signal already rides on every user message via the
<user_input mode="..."> wrapper, but nothing ever told the model what
the attribute means, and a manual plan/act toggle produced no inline
signal at all -- only an invisible system prompt swap the model cannot
diff. Two additions:

- The CLI system prompt now explains the mode attribute (both modes,
  since after a switch the transcript still contains messages tagged
  with the other mode) and that the newest message's mode governs.
- A user-initiated toggle stamps the next user message with a
  <mode_notice> block marking the switch, e.g. "The user switched from
  act mode to plan mode before sending this message." Round trips that
  return to the mode the model last saw cancel out. The model-initiated
  switch_to_act_mode path is excluded: its continuation prompt already
  announces the switch.

The notice vocabulary lives in @cline/shared next to the user_input
wrapper it extends, and normalizeUserInput hides the whole element from
transcript display the same structural way it strips the wrapper tags.

* fix(shared): strip mode_notice elements without polynomial regex

CodeQL flagged the lazy dot-all pattern (js/polynomial-redos): with the
global flag, every unmatched opening tag re-scans to the end of the
string, which is quadratic on adversarial transcript content. Replace
it with an indexOf-based splice that removes matched elements in linear
time and leaves unclosed tags intact, with a regression test on 50k
repeated open tags.
2026-07-03 15:05:22 -07:00
Saoud Rizwan 10d1c41b7a fix(cli): prevent empty session from racing a mode-change restart (#12056)
restartWithMessages cleared startupPromise and tore down the active
session before the replacement registered, leaving a window with no
active session and no startup in flight. A message submitted in that
window (e.g. typed right after a plan/act Tab toggle) made ensureReady
boot a blank fresh session, which then won the active slot over the
restarted session carrying the conversation history -- the model
responded as if the conversation had just started.

Publish the restart itself as the in-flight startupPromise so any
concurrent ensureReady waits for the restart instead of booting an
empty session. The barrier is cleared once the restart settles,
keeping failed restarts retryable by the next ensureReady.
2026-07-03 14:10:12 -07:00
Saoud Rizwan b823358867 chore(cli): release v3.0.36 2026-07-03 13:39:40 -07:00
Saoud Rizwan b876945c6d fix(cli): end plan-mode run on switch_to_act_mode and auto-continue with act tools (#12054)
* fix(cli): end plan-mode run on switch_to_act_mode and auto-continue with act tools

The CLI's switch_to_act_mode tool only queued the mode change; it was
applied after the whole turn finished. The model kept running the rest
of the turn with plan-mode tools (no editor) despite the tool result
claiming it now had edit access, so it fell back to editing files via
run_commands (sed/heredocs).

Mirror the VS Code extension's approach: the switch tool now completes
the run (lifecycle.completesRun), the pending mode change rebuilds the
session with act-mode tools, and a canned continuation prompt resumes
the approved plan automatically. Pending mode changes are tagged with
their source (tool vs UI toggle) so a Tab toggle racing a natural turn
completion can never auto-start plan execution the user did not
approve. The synthetic continuation prompt is hidden from transcript
hydration, and the plan-mode prompt/tool description now warn that
switching immediately starts execution.

* refactor(cli): show act-mode continuation prompt on resume instead of filtering it

Displaying the synthetic user message honestly beats exact-string
matching at the display layer, which was brittle and did not cover
other transcript consumers anyway. The live TUI still never echoes it;
it only appears as a user bubble when resuming a session. A
synthetic-message marker plumbed through SendSessionInput is the
principled follow-up if hiding it becomes worth the SDK surface
change.

* Revert "refactor(cli): show act-mode continuation prompt on resume instead of filtering it"

This reverts commit 969a24f9c9.
2026-07-03 13:10:29 -07:00
Saoud Rizwan 453cdea040 chore(cli): release v3.0.35 2026-07-03 10:26:32 -07:00
Saoud Rizwan 091eccdfe2 test: update GLM 5.2 context window assertions for refreshed catalog 2026-07-03 10:13:23 -07:00
Saoud Rizwan a2a46ae600 chore(sdk): release v0.0.55 2026-07-03 09:56:58 -07:00
Saoud Rizwan 82c9e77de2 style: apply formatter to pre-existing drift 2026-07-03 09:56:52 -07:00
Robin Newhouse dfd0e022a4 Add VS Code SDK compaction strategy setting (#11892)
* Add VS Code SDK compaction strategy setting

* Move compaction strategy setting into SDK

* Preserve stub global settings on compaction update

* Keep ApiProvider settings as proto strings

* Address compaction strategy review feedback
2026-07-02 23:58:47 -07:00
Robin Newhouse f0ec6a35bb fix: advertise run commands as shell strings (#12038) 2026-07-02 21:44:25 -07:00
Morgan Carr c09d54f5a2 fix(cli): format structured commands in history export (#12023)
Co-authored-by: Renee Huang <100229782+reneehuang1@users.noreply.github.com>
2026-07-01 15:53:27 -07:00
Tomás Barreiro 984d70a351 Add the subscription promo code when linking to the dashboard subscription page (#12019)
* Add the subscription promo code when linking to the dashboard page

* revert tests
2026-07-02 00:47:30 +02:00
Bee bbe7b6fd49 fix(hub): hydrate tool results in session message mapping (#12011)
* fix(hub): hydrate tool results in session message mapping

Map historical tool call/use and tool result blocks into webview tool events, including same-message results and following user result messages. Add tests to verify hydrated outputs and block ordering so restored sessions render completed tool interactions correctly.

* feedback
2026-07-01 14:28:13 -07:00
Bee be97d951fa fix: first-prompt truncation (#12022)
* Fix basic compaction first-prompt truncation

Issue: shallow sessions on high-output models such as OpenRouter MiniMax M3 could auto-compact immediately and reduce the initial task prompt to only the leading <user_input> wrapper. Harbor still passed the full task into Cline and session metadata retained it, but the model conversation could receive a truncated first message and respond that the request was empty or cut off.

Root cause: the output-runway target used maxInputTokens - maxTokens for every basic compaction. For MiniMax M3, maxTokens is nearly maxInputTokens, producing a tiny target. Basic compaction then used its last-resort first-user truncation path, and raw messages still contain the user_input envelope, so prefix truncation preserved the wrapper instead of the actionable task.

Fix: only use the output-runway target after the transcript has at least five user-assistant pairs, so early/shallow tasks use the normal trigger-based target. Also prevent first-user truncation unless that first user message alone exceeds the trigger budget, preserving normal first-turn prompts while still allowing genuinely oversized prompts to be reduced. Added regression tests for the MiniMax-style shallow prompt case and the oversized-first-prompt escape hatch.

* Fix compaction budget for huge-output models

Avoid collapsing context-derived input budgets when a model reports an output limit nearly equal to its context window, such as MiniMax M3. In those cases, treating context-output as the input budget causes auto-compaction to trigger on normal-sized prompts.

Only use contextWindow - maxTokens when the derived value remains at least half of the context window. Also simplify long-conversation basic compaction targeting to maxInputTokens * 0.5 instead of applying the default target ratio to maxInputTokens - maxTokens.

Adds regression coverage for MiniMax-style context-only metadata so an 18k-token prompt does not compact against an incorrectly collapsed 12k input budget.

* Guard compaction estimator against cumulative metrics

* Address basic compaction target review comments
2026-07-01 13:51:50 -07:00
Robin Newhouse c331a8f4b6 fix(core): use curated default for legacy provider migration (#12030) 2026-07-01 12:42:42 -07:00
Ara f180f1584d Add first-request failure telemetry (#11852)
* fix(vscode): capture first request failure telemetry

* Fix provider failure telemetry review feedback

* test(vscode): avoid extension host mock matchers

* fix(vscode): use session metadata for provider failure telemetry

* test(vscode): document pre-session auth telemetry skip

* fix(vscode): use turn-scoped provider failure gate

* fix(vscode): include cline pass in auth failure checks

* refactor(vscode): keep provider failure turn count in gate
2026-07-01 10:23:36 -07:00
Ara 9197d15abf feat(vscode): recognize Tencent TokenHub provider (#12028) 2026-07-01 09:58:45 -07:00
Ara 6c0d5c97b1 feat(llms): add Tencent TokenHub provider (#12014) 2026-07-01 09:58:10 -07:00
Dominic Cooney 9a8be88e85 fix(protos): self-heal protoc download when bun skips grpc-tools postinstall (#12003) 2026-07-01 08:31:04 +09:00
Ara 60f4a482ca Add onboarding intent telemetry (#11848)
* Add onboarding intent telemetry

* Track prompt submit intent from chat UI
2026-06-30 14:19:51 -07:00
John Choi dbe15202e1 fix(cli): update ClinePass tests for forced-enabled behavior (#11990)
#11986 (Forcefully enable ClinePass on the CLI) hardcoded isClinePassEnabled: true in session-runtime.ts, provider-catalog.ts, and main.ts and removed the ext-cline-pass feature-flag check, but left the corresponding tests asserting the old flag-driven / disabled behavior. They fail on main (and every branch that merges it).

- session-runtime.test.ts: expect getLastUsedProviderSettings called with isClinePassEnabled: true.

- provider-catalog.test.ts: drop the obsolete getBooleanFlagEnabled('ext-cline-pass') assertion (source no longer reads the flag) and its now-unused mock; keep the isClinePassEnabled: true expectation.

- main.test.ts: the ClinePass flag is no longer read during startup, so getBooleanFlagEnabled is never called. Re-target the 'seed identity before flags' ordering assertion at refreshCliFeatureFlagsInBackground (which is still invoked after seeding), and wire that mock through featureFlagMocks.
2026-06-29 22:35:52 +02:00
Bee 8d102db392 chore: remove console logs from compaction test (#11983)
Follow up on #11894, this PR removes the console logging code from the compaction unit test.

Co-authored-by: John Choi <97497948+johnwschoi@users.noreply.github.com>
2026-06-29 13:22:05 -07:00
Max 3dfd5dc31c fix(cli): recover missing interactive sessions on message reads (#11984)
Co-authored-by: Max Paulus 🥪 <max@cline.bot>
2026-06-29 13:21:27 -07:00
John Choi 43ce9f3694 fix(vscode): exclude vitest-owned tests from the mocha integration compile (#11981)
The vscode test (Extension Integration Tests) job is red on main: the updateAutoApprovalSettings suite (added in #11929) is vitest-native but was being collected and run by the Mocha @vscode/test-cli runner, where vitest-only matchers (toHaveBeenCalledWith / toHaveBeenCalledOnce / not.toHaveBeenCalled) are not registered, failing with 'is not a function'.

build-tests.js already excludes bun:test-owned tests from the Node-based out/ tree (single source of truth for the runner split). Extend that same mechanism to also exclude vitest-owned tests (files importing from 'vitest'), so neither bun nor vitest suites are ever compiled into the mocha out/ tree. Verified locally: detection catches the state suite (123 non-mocha test files total) while preserving the existing 60 bun __tests__ exclusions. No coverage lost — these suites run under test:vitest / bun.
2026-06-29 12:51:52 -07:00
Tomás Barreiro f1c73fb48b Remove unused imports (#11988) 2026-06-29 12:39:17 -07:00
Tomás Barreiro abaa8383c4 Forcefully enable ClinePass on the CLI (#11986) 2026-06-29 21:31:51 +02:00
Renee Huang 3a5e372d73 docs: document ClinePass API usage (#11980)
* docs: add MiniMax M3, Qwen3.7 Max, Qwen3.7 Plus models to ClinePass page

* more updates

* explicitly direct to personal org

* making the clinepass page more detailed

* updates to cline provider wording

* docs: document ClinePass API usage

* chore: discard McpHub change from PR

* docs: simplify ClinePass model slug table

* updates

* nit
2026-06-29 10:59:02 -07:00
Saoud Rizwan cf3a59f0e2 chore(cli): release v3.0.34 2026-06-29 09:59:28 -07:00
Tomás Barreiro b3aee68ca5 Merge both options and remove credits link (#11973)
* Merge both options and remove credits link

* Remove import

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-06-29 09:48:57 -07:00
Tomás Barreiro cd8fd29063 Improve the ClinePass step wording (#11974)
* Improve the ClinePass step wording

* Use a blacklist instead

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-06-29 09:21:27 -07:00
Saoud Rizwan 7777d61311 fix(cli): suppress ClinePass notice after onboarding (#11975) 2026-06-29 09:20:35 -07:00
Renee Huang 64fc3f372e docs: add MiniMax M3, Qwen3.7 Max, Qwen3.7 Plus models to ClinePass page (#11849)
* docs: add MiniMax M3, Qwen3.7 Max, Qwen3.7 Plus models to ClinePass page

* more updates

* explicitly direct to personal org

* making the clinepass page more detailed

* updates to cline provider wording
2026-06-29 07:28:38 -07:00
Saoud Rizwan 4175677e71 chore(cli): release v3.0.33 2026-06-28 23:45:56 -07:00
Saoud Rizwan 4934450947 fix(cli): show ClinePass subscription URL fallback (#11961)
* fix(cli): show ClinePass subscription URL fallback

* fix(cli): move ClinePass URL fallback below options
2026-06-28 23:33:58 -07:00
Saoud Rizwan b0a2d8a223 fix(cli): hide ClinePass promo for ClinePass users (#11963)
* fix(cli): hide ClinePass promo for ClinePass users

* fix(cli): expand ClinePass subscription card

* fix(cli): tune ClinePass subscription card height
2026-06-28 23:33:19 -07:00
Saoud Rizwan d9f1d862a5 fix(cli): use adaptive plan accent for ClinePass prompts (#11962) 2026-06-28 23:12:30 -07:00
Saoud Rizwan f8d73f3811 chore(cli): release v3.0.32 2026-06-28 21:43:18 -07:00
Saoud Rizwan 9aac8340dc chore(sdk): regenerate bun.lock for v0.0.54
Align resolved workspace versions in bun.lock with the v0.0.54 package
bumps. bun pm pack substitutes workspace:* deps using the version
recorded in bun.lock, so a stale lock made packed inter-package deps
resolve to 0.0.53, failing check-publish and the node smoke test (which
then pulled the old published @cline/shared from npm).
2026-06-28 21:24:41 -07:00
Saoud Rizwan 242b5ebff6 chore(sdk): release v0.0.54 2026-06-28 20:54:47 -07:00
Tomás Barreiro 7ca41fdb7d Improve ClinePass onboarding UX (#11959)
* Prevent ClinePass onboarding flicker

* Make the clinepass step scrollable and remove details
2026-06-28 20:49:27 -07:00
Saoud Rizwan 000918989f fix(cli): make ClinePass subscription screen selectable (#11957) 2026-06-28 20:12:04 -07:00
Saoud Rizwan 9560b6d625 fix(cli): use ClinePass as one word consistently (#11956) 2026-06-28 18:41:26 -07:00
Saoud Rizwan c7304097a7 fix(cli): update ClinePass provider UI copy (#11953)
* fix(cli): update ClinePass provider UI copy

* fix(llms): rename Cline provider display name

* fix(cli): separate ClinePass billing links
2026-06-28 17:57:11 -07:00
Tomás Barreiro 674a6022ee Add an intermediate step before going to ClinePass model selection (#11947)
* Add an intermediate step before going to model selection

* fix type issues

* use allSettled

* fix(cli): serialize ClinePass subscription checks

* fix(cli): handle missing ClinePass plan as unsubscribed

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-06-28 16:13:25 -07:00
Saoud Rizwan dbf0775384 fix(llms): keep error detail extraction for Error instances (#11949)
PR #11928 added an `instanceof Error` branch to extractErrorMessage to
preserve transport-error wrappers (e.g. "fetch failed: SocketError: ...
(UND_ERR_SOCKET)"), but that branch regressed two cases:

- Generic SDK wrappers like "No output generated. Check the stream for
  errors." were prepended to the real cause instead of being dropped.
- Errors carrying structured detail on responseBody/detail/error fields
  surfaced the bland top-level .message ("Bad Request") instead of the
  detail ("Instructions are required").

The Error branch now drops known generic wrappers in favor of the cause
and extracts structured detail from the error's own fields, while keeping
the transport-wrapper concat behavior #11928 intended.
2026-06-28 15:27:46 -07:00
Saoud Rizwan e130b45eb0 feat(cli): promote Cline Pass in startup notice (#11948) 2026-06-28 13:14:27 -07:00
Tomás Barreiro 6f4dbae86f Improve the ClinePass onboarding experience on the CLI [ENG-2236] (#11946)
* Improve the ClinePass onboarding experience on the CLI

* Update apps/cli/src/tui/views/onboarding/screens.tsx

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* address comments

* style(cli): format ClinePass onboarding warning

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-06-28 10:53:27 -07:00
Saoud Rizwan c7de31ae24 ci(vscode): gate legacy publish on test job (#11936) 2026-06-27 19:33:37 -07:00
Saoud Rizwan 45dddb9a4e ci(vscode): add legacy extension publish workflow (#11935) 2026-06-27 19:17:46 -07:00
Bee e32caee96b fix: basic compaction token budgeting (#11894)
* Improve compaction token budgeting

Use MessageWithMetadata.metrics input/output token counts when estimating message size for compaction, falling back to the existing chars/3 heuristic only when metrics are unavailable. This makes trigger decisions and post-compaction accounting use provider-reported token usage instead of relying only on serialized character estimates.

When an explicit compaction maxInputTokens budget is configured and the model exposes maxTokens, reserve half of the model output budget before triggering compaction. This gives the next provider request room for completion tokens and reduces edge cases where the local context estimate passes but the provider rejects the prompt as exceeding its limit.

Keep explicit reserveTokens and thresholdRatio overrides intact, and add regression coverage for metric-based token estimation, fallback estimation, and output-token-aware trigger budgeting.

* new target tokens and trigger tokens value

* fixes

* use imports and add unit test

* resolveMaxInputTokens

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-06-27 16:04:56 -07:00
Saoud Rizwan 8f6dae0ac0 fix(vscode): sync auto-approve task settings (#11929) 2026-06-27 15:13:14 -07:00
Saoud Rizwan 263b58f8c3 fix(llms): preserve fetch error cause details (#11928) 2026-06-27 14:51:56 -07:00
Saoud Rizwan b193a81ac9 fix: prevent API key field clearing on settings load (#11925)
* fix: prevent API key field clearing on settings load

* fix: cancel API key save on mask hydration
2026-06-27 14:32:14 -07:00
John Choi 92806c60ca fix(agents): derive messageModelInfo in the provider/model runtime path (#11903)
The standalone AgentRuntime({ providerId, modelId }) constructor built the gateway model in resolveRuntimeConfig and returned { ...rest, model } without deriving messageModelInfo. The prebuilt-model path preserves it, and core sessions populate it via buildMessageModelInfo, but standalone SDK callers lost it -- so assistant-message modelInfo and model-tagged telemetry (reasoning tokens, action-follow-through) emitted without provider/model dimensions.

Derive messageModelInfo as { id: modelId, provider: providerId } in that path (family omitted; it is optional and unavailable here). An explicit caller-provided messageModelInfo still wins. Adds provider-form tests covering both the derived and explicit-override cases.
2026-06-26 18:23:54 -07:00
Saoud Rizwan 3a05171e30 fix(sdk): preserve failed run error messages (#11904) 2026-06-26 18:04:53 -07:00
Saoud Rizwan a6e315a4a6 chore(cli): release v3.0.31 2026-06-26 17:37:36 -07:00
Saoud Rizwan 2714f93b45 chore(sdk): drop volatile catalog refresh from v0.0.53
The bun run version catalog regen dropped the xiaomi mimo-v2-omni,
mimo-v2-pro, and mimo-v2-flash models from the live data. mimo-v2-omni
is the xiaomi provider's defaultModelId in builtins.ts, so shipping the
refreshed catalog would point the default at a missing model and broke
the provider-ids test. Revert the catalog to the pre-release state and
ship v0.0.53 as a pure SDK code release; the catalog will refresh in a
later release once upstream data is stable.
2026-06-26 17:17:00 -07:00
Saoud Rizwan 3abeb8a90b chore(sdk): release v0.0.53 2026-06-26 17:03:04 -07:00
Tomás Barreiro 7830472017 Add open subscription page option to the ClinePass options (#11896)
* Add open subscription page option to the ClinePass options

* Address comments
2026-06-27 01:07:11 +02:00
Bee 83339c3c5a refactor: extension to use sdk provider list (#11888)
* refactor(vscode): use string type for api provider in proto

Replace the ApiProvider proto enum with plain string fields across\nmodels.proto and state.proto, and drop the enum<->string conversion\nmappings in api-configuration-conversion.ts. Updates ApiOptions and\nOpenAICompatible settings components accordingly.

* fix custom provider render

* format

* id

* remove extension providers file

* uses includes
2026-06-26 16:05:55 -07:00
Tomás Barreiro 664daf6ded Show cost has been covered by the users subscription (#11889)
* Show cost has been covered by the users subscription

* fix tests

* Update apps/cli/src/tui/components/status-bar.tsx

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* Update wording

* Update tests

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-06-26 23:34:06 +02:00
Tomás Barreiro 5b1f8850af Update coupon code (#11890) 2026-06-26 23:33:11 +02:00
Tomás Barreiro c49d4121a3 Fix SDK tests (#11895) 2026-06-26 14:20:58 -07:00
Tomás Barreiro 7f495a5e99 Open ClinePass subscribe page (#11891) 2026-06-26 22:54:00 +02:00
Max 1e88a708bd upate changelog (#11886)
* upate changelog

* Update CHANGELOG.md

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

---------

Co-authored-by: Max Paulus 🥪 <max@cline.bot>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-06-26 12:50:52 -07:00
Saoud Rizwan 408be18be9 fix(ci): harden ext-vscode stable release workflow (#11887)
* fix(ci): harden ext-vscode stable release workflow

- Resolve previous tag to the latest vX.Y.Z ancestor instead of the most
  recent reachable tag. The nightly workflow now pushes a nightly-main-*
  annotated tag on every main commit, so git describe was resolving the
  release notes' Full Changelog compare link to a nightly tag rather than
  the prior release tag.
- Extract the changelog section by exact version heading (and fail if it
  is missing) instead of always taking the first ## [ block, so a stale
  top entry can no longer ship as the release notes for a different
  version.
- Add a pre-publish Verify Changelog Entry gate so a release cannot be
  published unless CHANGELOG.md leads with the version being released.
- Add a Verify Marketplace Tokens gate so a missing VSCE_PAT/OVSX_PAT
  fails fast before packaging rather than mid-publish.
- In existing-tag mode, require the tag to point at the tested SHA so the
  published artifact always matches what CI verified.
- Add a concurrency group keyed on the tag to prevent duplicate
  concurrent publishes of the same release.

* fix(ci): validate stable release metadata before publish
2026-06-26 12:50:28 -07:00
Saoud Rizwan 5a9c637e32 fix(core): cap MCP tool names at 64 chars for OpenAI-compatible providers (#11885) 2026-06-26 12:42:57 -07:00
Saoud Rizwan 8152572641 fix(vscode): disable MCP marketplace tab from remote config (#11883) 2026-06-26 11:48:07 -07:00
Saoud Rizwan 0736e12e32 fix(vscode): refresh MCP hub after marketplace install (#11882) 2026-06-26 11:41:34 -07:00
Saoud Rizwan 690f80523f fix(vscode): preserve migrated OpenAI-compatible settings (#11880)
* fix(vscode): preserve migrated OpenAI-compatible settings

* test(vscode): cover OpenAI-compatible plan act selections
2026-06-26 10:56:50 -07:00
Saoud Rizwan 4fa1b8a291 fix(vscode): reject approvals from composer feedback (#11874) 2026-06-26 04:01:10 -07:00
Saoud Rizwan ed685b28e0 feat(vscode): allow cancelling queued prompts (#11875) 2026-06-26 03:58:46 -07:00
Saoud Rizwan 38338310d7 fix(vscode): timeout terminal cwd setup (#11871)
* fix(vscode): timeout terminal cwd setup

* refactor(vscode): simplify terminal cwd timeout
2026-06-26 03:49:36 -07:00
Saoud Rizwan 1c4a7885e6 fix(vscode): keep command output pinned to bottom (#11873) 2026-06-26 03:49:05 -07:00
Saoud Rizwan a0517db2fa feat: add shared marketplace uninstall support (#11870)
* feat: add shared marketplace uninstall support

* fix: avoid regex backtracking in marketplace skill sanitization

* fix: address marketplace uninstall review feedback

* fix: clean up remaining marketplace skill installs

* fix: remove marketplace skills from all agents

* fix: keep customize primitive tabs horizontal
2026-06-26 03:47:53 -07:00
Saoud Rizwan 38134ef967 fix(vscode): surface plugin bundled skills (#11868)
* fix(vscode): surface plugin bundled skills

* fix(core): align plugin skill settings lookup
2026-06-25 21:31:45 -07:00
Saoud Rizwan 8715cafce7 fix(vscode): rename user message reset actions (#11869) 2026-06-25 21:30:46 -07:00
Saoud Rizwan 46ee8ea329 feat(vscode): add customize section tabs (#11867)
* feat(vscode): add customize section tabs

* fix(vscode): reset customize section tab

* fix(vscode): reset customize section on initial type
2026-06-25 21:29:30 -07:00
Tomás Barreiro b7d9ea4500 Add a prompt to change to ClinePass when out of credits (#11866)
Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-06-25 21:11:23 -07:00
Saoud Rizwan 5147abc75e fix: hide workflows from customize menu (#11864) 2026-06-25 20:57:10 -07:00
Saoud Rizwan 9abd7ae8c3 fix(vscode): disable command auto-approval by default (#11865) 2026-06-25 20:56:30 -07:00
Saoud Rizwan bf83303bb7 fix(cli): require quoted prompts for one-shot mode (#11861) 2026-06-25 20:54:13 -07:00
Saoud Rizwan bfe5bf841a refactor: share marketplace install logic through core (#11862)
* refactor: share marketplace install logic through core

* fix: harden shared install helpers

* refactor: share mcp marketplace arg parsing
2026-06-25 20:34:42 -07:00
Tomás Barreiro eb362df3ba List ClinePass features in the CLI not-subscribed message (#11846)
* List ClinePass features in the CLI not-subscribed message

* fix tests
2026-06-26 05:22:15 +02:00
Saoud Rizwan f735ddcb7a fix(vscode): handle escape while editing user messages (#11860) 2026-06-25 20:20:33 -07:00
Saoud Rizwan 5b63d3e9c8 fix(vscode): preserve raw structured terminal commands (#11857) 2026-06-25 20:19:40 -07:00
Saoud Rizwan 1ff6a54825 fix: wrap customize tabs on narrow screens (#11855) 2026-06-25 19:17:49 -07:00
Saoud Rizwan b1a3cb6cfc chore(cli): release v3.0.30 2026-06-25 18:03:44 -07:00
Saoud Rizwan 78f1736723 test(cli): widen help terminal so long flag descriptions don't wrap
The --thinking description added in #11656 is long enough that at 120
columns commander wraps it, splitting "omitted leaves provider default"
across two lines. The TUI e2e assertion uses a contiguous getByText, so it
failed on the ubuntu-only TUI test leg, blocking the SDK publish gate.
Widen the help terminal to 200 columns so long descriptions render on a
single line.
2026-06-25 17:51:56 -07:00
Tran Binh Minh 28a014c1c6 docs: fix outdated skills enable path (#11838)
The Skills note pointed users to "Settings → Features → Enable Skills,"
but that toggle no longer exists — the Features settings section has no
Skills entry and skills are loaded by default. Point users to the actual
Skills menu (scale icon → Skills tab), consistent with the access path
already documented later in the same page.

Fixes #11740

Co-authored-by: Minhkunn <minh.12072k6@gmail.com>
2026-06-25 17:32:09 -07:00
Saoud Rizwan fed291e37a chore(sdk): release v0.0.52 2026-06-25 17:27:03 -07:00
Saoud Rizwan bb68351123 fix(vscode): avoid duplicate followup answer bubbles 2026-06-25 17:18:09 -07:00
Saoud Rizwan 84cb15813a fix(vscode): polish queued prompt panel 2026-06-25 17:17:48 -07:00
Saoud Rizwan c3671de7de fix(vscode): disable subagents (#11847) 2026-06-25 17:02:25 -07:00
Saoud Rizwan 26f737913f fix(vscode): show direct user messages immediately (#11845)
* fix(vscode): show direct user messages immediately

* fix(vscode): address pending chat bubble review
2026-06-25 16:53:15 -07:00
Dominic Cooney 50797dd82d vscode(ENG-2203): Migrate legacy MCP files, formats to shared settings file. (#11818)
* Migrate legacy MCP files, formats to shared MCP settings.
2026-06-26 08:23:02 +09:00
Saoud Rizwan 923ee3e137 fix(vscode): enable auto-approval defaults (#11840)
* fix(vscode): enable auto-approval defaults

* test(vscode): update file edit e2e for auto-approval defaults
2026-06-25 15:03:09 -07:00
Saoud Rizwan bbf5bb2302 fix(vscode): hide auto-approve notifications toggle (#11843) 2026-06-25 15:01:46 -07:00
Saoud Rizwan 0220b9a506 fix(vscode): queue chat submits during active turns (#11839)
* fix(core): avoid requeueing terminal failed prompts

* fix(vscode): avoid duplicate queued follow-up messages

* fix(vscode): keep approvals pending for queued messages

* fix(vscode): queue chat submits during active turns

* fix(core): restore pending prompt requeue on send failure

* test(core): remove pending prompt status churn
2026-06-25 15:01:10 -07:00
Ara 1b573275f8 fix(vscode): remove test server (#11842) 2026-06-25 14:39:59 -07:00
Robin Newhouse d7d74e0b89 fix(llms): preserve OpenRouter reasoning disable semantics (#11656)
* fix(llms): preserve OpenRouter reasoning disable semantics

* fix(llms): clarify reasoning token usage

* fix(llms): use OpenRouter reasoning effort none

* refactor(cli): extract reasoning resolution helper

* fix(cli): clarify thinking defaults

* feat(agents): capture unexpected reasoning token telemetry

* fix(cli): preserve reasoning on model change

* refactor(llms): table-drive reasoning token extraction

* fix(sdk): catalog unexpected reasoning telemetry
2026-06-25 14:35:05 -07:00
Saoud Rizwan bebc581652 fix(core): add non-interactive command guidance (#11815)
* fix(core): add non-interactive command guidance

* fix(core): refine non-interactive command guidance
2026-06-25 12:34:32 -07:00
Saoud Rizwan 21ebee3638 fix(sdk): keep SAP model filtering in clients (#11837) 2026-06-25 12:31:59 -07:00
Saoud Rizwan 7d78c5f074 refactor(vscode): attach checkpoints to user edits (#11832)
* refactor(vscode): attach checkpoints to user edits

* fix(vscode): improve checkpoint edit controls

* fix(vscode): align checkpoint edit actions

* fix(vscode): gate checkpoint restore edits
2026-06-25 12:21:17 -07:00
Saoud Rizwan 352a23a6da fix: stabilize SAP AI Core provider setup (#11833)
* fix: Filter SAP AI Core models based on mode-availibility

* chore: fix model picker test

* fix: harden SAP AI Core model filtering

* fix: pin SAP Cloud SDK to 4.6.0

* fix(vscode): simplify SAP AI Core model filtering

---------

Co-authored-by: David Knaack <david.knaack@sap.com>
2026-06-25 12:13:53 -07:00
Saoud Rizwan 176662ee20 fix(vscode): show pending state before queued prompt appears (#11836)
* fix(vscode): show pending state for chat sends

* fix(vscode): keep chat input editable during pending sends
2026-06-25 12:08:05 -07:00
Saoud Rizwan 309ad9da72 fix(vscode): show queued prompts while streaming (#11835)
* fix(vscode): show queued prompts while streaming

* fix(vscode): refine queued prompt updates
2026-06-25 11:54:44 -07:00
Saoud Rizwan 16a6926985 fix: improve ask option selection UI (#11824)
* fix: disable hover for selected ask options

* fix: mark ask options disabled after selection

* fix: hide duplicate ask option echoes
2026-06-25 11:51:43 -07:00
Saoud Rizwan d6db723c9d fix(vscode): keep command output pinned to bottom (#11825)
* fix(vscode): keep command output pinned to bottom

* fix(vscode): address command output scroll review
2026-06-25 11:31:54 -07:00
Tomás Barreiro ce1fc20a9f Limit the ClinePass CLI url to the CLI (#11811)
* Limit the ClinePass CLI url to the CLI

* fix tests

* Remove unused import

* Fix run-agent

* fix run-aent error message
2026-06-25 20:31:31 +02:00
Tomás Barreiro b780a5ec00 Fix ClinePass error mapping on VSCode (#11807)
* Fix ClinePass error mapping on VSCode

* refactor

* fix types

* remove unused constants

* refactor
2026-06-25 19:29:13 +02:00
Tomás Barreiro 285cd6d54c Fix vscode tests (#11831) 2026-06-25 10:16:39 -07:00
Tomás Barreiro ff845539e4 Fix createRequire (#11829) 2026-06-25 18:17:16 +02:00
Saoud Rizwan 05b0aa7dcb fix(vscode): flush state after model selection (#11822)
* fix(vscode): flush state after model selection

* fix(vscode): drain state after teardown cleanup
2026-06-25 04:13:40 -07:00
Saoud Rizwan 6cdd882acb fix(vscode): remove dead settings (#11819) 2026-06-25 03:38:47 -07:00
Saoud Rizwan d055b0941f feat(vscode): add marketplace (#11816)
* feat(vscode): add customize marketplace

* style(vscode): format marketplace imports

* fix(vscode): open marketplace mcp tab from configure

* fix(marketplace): redact authorization headers
2026-06-25 02:57:15 -07:00
Saoud Rizwan 21a0e4c4f1 fix(vscode): remove delay when sending message (#11817)
* fix(vscode): show new chat immediately on send

* fix(vscode): restore chat input after new task failure
2026-06-25 02:56:55 -07:00
Saoud Rizwan 54d022b536 fix(vscode): simplify auto-approve menu (#11814)
* fix(vscode): remove all-commands auto-approve option

* fix(vscode): clear legacy all-commands approval

* fix(vscode): remove external path auto-approve options

* Revert "fix(vscode): clear legacy all-commands approval"

This reverts commit 39af65f894.

* fix(vscode): ignore legacy all-commands approval

* fix(vscode): use all-commands auto-approve flag

* Revert "fix(vscode): use all-commands auto-approve flag"

This reverts commit 8b093ce654.
2026-06-25 02:46:07 -07:00
Dominic Cooney a1709d37e5 fix(vscode): make compact button run real SDK compaction (#11764)
* fix(vscode): make compact button run real SDK compaction

The compact button (and the typed /compact and /smol commands) sent the
literal text "/compact" to the model as a normal chat message. In the SDK
adapter only /workflow and /skill are expanded as runtime commands, so the
model received "/compact" as a prompt and improvised a fake "Conversation
Summary" without actually reducing the context window (CLINE-2503).

Wire the same SDK effect the CLI's /compact (alias /smol) uses:

- sdk-compaction.ts: compactSessionMessages(), the VSCode analog of the CLI's
  compactInteractiveMessages -- a manual-mode createContextCompactionPrepareTurn
  over the current transcript, force-enabling compaction and forwarding
  telemetry/sessionId.
- sdk-compaction-coordinator.ts: reads the active session transcript, runs the
  manual compaction, and restarts the session with the compacted messages via
  replaceActiveSession (same sequencing as a mode rebuild), preserving the
  session id and emitting a CLI-style status line. Guards no-session, mid-turn,
  and empty-transcript cases.
- SdkController.compactTask() exposes it; the condense slash handler now calls
  it instead of the no-op ask response.
- Webview: the compact-confirm button and typed /compact + /smol (with an active
  task) route to the condense RPC instead of sending literal text.

Adds unit tests for the helper, the coordinator, and the webview send routing.

* chore(vscode): drop trailing newline in condense handler (biome)

* test(vscode): cover manual compact flow

* test(vscode): use portable compact matcher

* test(vscode): assert compact calls without vitest matchers

* test(vscode): keep compact assertion type safe

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-06-25 02:28:07 -07:00
Saoud Rizwan b4157df509 fix(vscode): refresh Cline model info from catalog (#11806) 2026-06-25 02:20:22 -07:00
Saoud Rizwan 60dee73f0f fix(vscode): use SDK for mistake limit (#11808)
* fix(vscode): use SDK mistake-limit recovery

* test(vscode): cover mistake-limit stop response
2026-06-25 02:19:58 -07:00
Saoud Rizwan 2d66bd475e feat: add checkpoints (#11813)
* feat: add SDK-backed VS Code checkpoints

* fix: remove stale checkpoint view changes reset
2026-06-25 02:19:14 -07:00
Renee Huang a1374ae4a5 docs: add ClinePass subscription page and reorganize sidebar nav (#11672)
* docs: add ClinePass subscription page and reorganize sidebar nav

* docs: polish ClinePass copy and add cross-links

* more wording updates

* polishing

* docs: update 5x to 2-5x API rate limits

* add beta label to clinepass

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-06-24 22:03:39 -07:00
John Choi b8c62ecbae fix(onboarding): restore ClinePass models in onboarding (SDK parser dropped clinePass) (#11805)
* fix(onboarding): restore ClinePass models in onboarding

Root cause: the SDK's fetchClineRecommendedModels (@cline/core) silently
dropped the clinePass list. Its ClineRecommendedModelsData type and
normalizeResponse only handled recommended/free, so the recommended-models
endpoint's clinePass entries were stripped before reaching the extension.
Result: the onboarding ClinePass option appeared but the model list was always
empty ('No ClinePass models are available right now'), regardless of the
ext-cline-pass flag. This also affected any SDK consumer (CLI/JetBrains).

Also reverts the pre-login regression from #11798: that PR gated the first
onboarding screen on the extension-side clinePassEnabled flag, which is only
populated after login (featureFlagsService.poll runs on auth), so the ClinePass
option disappeared on the pre-login 'How will you use Cline?' screen.

Changes:
- @cline/core cline-recommended-models: parse/clone clinePass; include it in
  the type and offline fallback; treat clinePass-only responses as non-empty.
- OnboardingView: gate the ClinePass option on the webview useHasFeatureFlag
  (works pre-login) instead of the extension-side clinePassEnabled.
- Revert the extension-side clinePassEnabled plumbing added in #11798
  (FeatureFlagsService.getClinePassEnabled, state payload, ExtensionMessage,
  ExtensionStateContext default).

* test: add clinePass to recommended-models SDK mocks

ClineRecommendedModelsData now requires clinePass; update the mocked SDK
results in refreshClineRecommendedModels.test.ts so check-types passes.

* fix(onboarding): only offer ClinePass when models are available

Gate the ClinePass option on isClinePassEnabled AND models.clinePass.length > 0.
Previously, when the flag was on but the recommended-models request fell back
(or returned no clinePass entries), the option still appeared and routed users
into the ClinePass model step's empty state, where signup is disabled -- a dead
end instead of staying on Free/Frontier/BYOK.

* chore: trim ClinePass gate comment to one line
2026-06-24 17:41:14 -07:00
Bee 96b29d787e fix: normalize JSON-like tool inputs by schema (#11803) 2026-06-24 17:04:57 -07:00
Tomás Barreiro 5b96beb583 Link the CLI to the promo (#11794) 2026-06-25 01:48:09 +02:00
Dominic Cooney f2af0d700c refactor: simplify sdk terminal execution (#11789) 2026-06-25 08:43:41 +09:00
Tomás Barreiro a9c47f88fd Remove unused retry function (#11804) 2026-06-25 08:38:41 +09:00
John Choi 49884a1194 fix(sdk): batch outdated-read rewrites in MessageBuilder to preserve provider prefix caches (#11471)
* fix(sdk): batch outdated-read rewrites in MessageBuilder to preserve provider prefix caches

MessageBuilder previously rewrote stale read_files results to
'[outdated - see the latest file content]' eagerly on every re-read.
Each rewrite mutates bytes in the middle of the provider-facing
transcript, invalidating provider prefix caches (DeepSeek/Anthropic/
MiniMax-style) from that message to the end of the conversation. Agents
re-read files constantly (read -> edit -> verify), so long sessions paid
full uncached input price on most requests.

Now pending outdated rewrites accumulate and only commit once the total
reclaimable bytes cross a 64KB threshold, then apply as a single batch
(one cache break amortized over a large context saving). Committed
rewrites are sticky so subsequent requests stay byte-stable.

* fix(sdk): count only reclaimable locator bytes when batching outdated-read rewrites

Addresses review feedback (greptile P1, codex P2): pendingBytes was
incremented with the whole tool-result block size once per outdated
locator, so multi-file read_files results were overcounted (N stale
locators = N x block bytes), crossing the batch threshold far earlier
than intended and partially defeating the cache-stability guarantee.

Now estimateOutdatedReclaimBytes attributes bytes per stale entry in
the parsed read result (falling back to full text size only when the
whole block is outdated, matching replaceOutdatedReadContent), counted
once per block. Adds a multi-locator regression test where a 3-file
read result is invalidated file-by-file and must only commit when the
actual reclaimable bytes cross the threshold.

Real-session replay improved from 12.6% to 19.7% net-token reduction
with the accurate counting (commits defer longer, breaks amortize
better).

* fix(sdk): drop committed outdated rewrites when history is rolled back

Addresses review P1: committedOutdatedRewrites survived checkpoint
restore/clearHistory (the orchestrator reuses one MessageBuilder), so a
read that became the latest again after rollback stayed rewritten to
'[outdated...]' forever, hiding live file content from the provider.

Two guards: re-validate committed locators against the current index at
apply time, and clear the committed set in resetIndexes — that path only
fires on non-append-only history changes, where the provider prefix is
already broken, so stickiness loses nothing.

Adds a rollback regression test (commit rewrite, restore to before the
re-read, assert full content returns).

* test(sdk): trim redundant comments in rollback regression test

* fix(sdk): keep outdated-rewrite batching state across fresh message rebuilds

Addresses review feedback: the runtime provider path rebuilds Message
objects every request (agentMessagesToMessages constructs new literals),
so the identity-based reindex check fails each build and resetIndexes
fires. Clearing committedOutdatedRewrites there (added for the rollback
P1) recounted already-committed bytes as pending on every request — once
the first 64KB committed, every newly-stale small read rewrote
immediately, degenerating to eager behavior in steady state.

committedOutdatedRewrites now survives resetIndexes. Rollback
correctness is preserved without it: the apply-time re-validation is
identity-free, and commitOutdatedRewrites now prunes committed locators
that are no longer outdated in the current index plus entries whose
tool_use_id left the transcript. Both prunes are no-ops in append-only
growth since outdatedness is monotonic.

Adds two regression tests that route messages through the real
agent-message codec round-trip (fresh objects per build, as production):
steady-state deferral of a small newly-stale read after a committed
large one, and rollback restoring full content.

* fix(sdk): batch orphaned read results and count stale image bytes

Addresses robinnewhouse review (two pre-approval follow-ups):

1. Tool-name lookups went through toolNameByIdCache only, so a
   tool_result orphaned by compaction/rollback (paired tool_use gone)
   was invisible to the batching scan and pruned from committed state —
   reverting its rewrite mid-transcript in exactly the history-shrinking
   case the batching needs to survive. resolveToolName now falls back to
   tool_result.name at all three lookup sites (transform, reindex,
   commit scan).

2. estimateOutdatedReclaimBytes attributed only text/file entries, but
   replaceOutdatedReadContent also replaces stale image siblings
   (flagged by codex too). Image-heavy sessions accrued ~0 pending bytes
   and never crossed the threshold. The estimator now counts stale image
   payload bytes using the same positional marker counting as the
   rewriter (countOutdatedImageEntries).

Both regression tests fail before this change: orphaned result keeps
its committed rewrite through a codec round-trip, and a 4KB stale image
crosses a 2KB threshold that its ~70-byte text marker alone would not.

* perf(sdk): retune outdated-rewrite threshold to 128KB for executor caps

The 64KB default was calibrated before executor-layer output caps landed
(#11480/#11504: read_files/run_commands/search now cap at 48K chars).
With reads bounded at ~48K, 64KB sat awkwardly — one stale read can't
cross it, two overshoot — making it the worst non-extreme threshold in a
post-cap cost sweep.

Re-measured eager vs batched on 48K-capped transcripts (DeepSeek 10x
cache pricing): batching still beats eager 44-61%, confirming the
mechanism remains valuable after the caps (never-rewrite is now +35%
worse in long sessions). 128KB (~2-3 capped reads) is cheapest in both
short and long shapes, ~5-12 points better than the old 64KB.

Bumps LARGE_CONTENT test fixture to ~140KB so single-large-read commit
tests still exceed the raised threshold.

* fix(sdk): batch structured read tool results

* fix(sdk): resolve orphaned tool names for aggregate truncation

* test(sdk): trim redundant message builder cache tests

* style(sdk): trim message builder comments

* test(sdk): allow schedule history test more time on windows

* fix(sdk): preserve infinity outdated rewrite threshold

* perf(sdk): retune outdated rewrite threshold to 64KB

* Revert "test(sdk): allow schedule history test more time on windows"

This reverts commit ac21ef1702.

* test(sdk): fold message builder cache stability coverage

* fix(sdk): address stale read batching review
2026-06-24 16:17:34 -07:00
Dominic Cooney 49da86f60c chore: update biome vscode settings (#11788) 2026-06-25 08:16:50 +09:00
John Choi 84477dd84f fix(onboarding): gate ClinePass on reliable extension-side flag (#11798)
* fix(onboarding): show ClinePass models reliably + label the group ClinePass

Two issues:

1. Nightly feature-flag race. ClinePass was gated twice by two different flag
   clients: the recommended-models endpoint is server-gated by ext-cline-pass
   (PostHog-node), while the webview independently re-checked ext-cline-pass via
   PostHog-js to decide whether to show the option and keep the models. These
   reads race and disagree (mid auth/identify handshake, or when PostHog
   remote-config scripts are blocked by the webview CSP), so the ClinePass
   option could appear with an empty model list.

   Fix: make the server-gated payload the single source of truth. Onboarding
   shows the ClinePass option iff the payload contains ClinePass models
   (getUserTypeSelections now takes hasClinePassModels), and
   getRecommendedModelsData no longer re-filters response.clinePass on the
   webview flag. Removes the second racy webview PostHog read entirely.

2. Group label. The ClinePass group rendered as the raw provider id (CLINE-PASS).
   Render it with the product's proper casing (ClinePass). Model ids/names are
   intentionally left as-is (e.g. cline-pass/minimax-m3), since that's what the
   model is called.

* fix(onboarding): gate ClinePass on reliable extension-side flag

The ext-cline-pass flag is rolled out to internal cohorts only (QA/Cline
team/ClinePass Beta), not GA. Onboarding read it via the webview posthog-js
client, which is unreliable during onboarding (CSP blocks PostHog remote
config in Nightly, and it evaluates before auth/identify resolves) -- so
eligible team members saw ClinePass with an empty list / not at all.

Read the flag from the extension-side featureFlagsService instead (the same
server-evaluated source Settings/catalog already use), plumbed into webview
state like worktreesEnabled. Onboarding now shows ClinePass iff the flag is
enabled AND the payload contains ClinePass models, so the option and the
list are always in sync.

- FeatureFlagsService.getClinePassEnabled()
- getStateToPostToWebview: clinePassEnabled
- ExtensionState type + webview default
- OnboardingView gates on state.clinePassEnabled
2026-06-24 16:15:53 -07:00
Saoud Rizwan bd662d81f6 fix: bundle SAP AI Core provider auth (#11796)
* fix: bundle SAP AI Core provider auth

* fix: serialize SAP service-key auth calls
2026-06-24 13:45:00 -07:00
Tran Binh Minh d8a3086eaa docs(mcp): set type=streamableHttp in remote server example (#11670) (#11690)
The remote-server JSON example omitted the `type` field. Because the
config schema's z.union lists the SSE branch before streamableHttp
(intentionally, for backward compat), an untyped remote entry silently
resolves to the deprecated legacy SSE transport — the opposite of the
docs' own "Streamable HTTP (recommended)" guidance.

Add `"type": "streamableHttp"` to the example, rename the heading to
match, and add a sentence explaining that omitting `type` defaults to
legacy SSE.

Fixes #11670

Co-authored-by: Minhkunn <minh.12072k6@gmail.com>
2026-06-24 20:59:31 +02:00
Tomás Barreiro 8ecd136e52 Update the clinePass model list live (#11792)
* Generate the model list dynamically

* Do not return known models

* Make both calls in parallel

* Remove modelsDev catch on model generation

* readd error catching
2026-06-24 20:44:36 +02:00
Robin Newhouse 14a28b0559 fix(core): avoid nullable editor old_text schema (#11784) 2026-06-24 04:31:01 -07:00
Dominic Cooney c5378d1847 Merge pull request #11777 from cline/dpc/sdk-migration-simpler-login
SDK migration: move apps/vscode to bun + Cline SDK

### Description

This is the integration branch that moves the VSCode extension onto the Cline SDK and the bun toolchain. Major facts:

- **`apps/vscode` now runs on the Cline SDK.** The extension consumes `@cline/core`, `@cline/llms`, and `@cline/shared` through an adapter layer in `apps/vscode/src/sdk/` (single codepath — no `CLINE_SDK` flag). The webview still talks gRPC; the adapter translates between the gRPC handlers and SDK calls.
- **`apps/vscode` is folded into the root bun workspace.** Package management and task running move from npm/node to **bun**; the extension links the local `@cline/*` packages via `workspace:*` instead of pinned published versions. **Node remains the runtime** (extension host, standalone `cline-core`, esbuild `platform: node`, prebuild ABI targets).
- **npm lockfiles deleted; root `bun.lock` is authoritative** (`apps/vscode`, `webview-ui`, and `testing-platform` per-package lockfiles removed).
- **CI updated** for the new layout: the `ext-vscode-*` workflows install once at the root with bun and build the SDK before the extension build.
- **VSCode extension version bumped to `4.0.0`.**

### Test Procedure

Validated locally before opening:

- `bun run lint` — clean.
- Typechecks across SDK packages, `@cline/cli`, `@cline/cline-hub`, plus `apps/vscode` extension + webview `tsc` — all clean.
- Extension esbuild bundle and both webviews (`apps/vscode/webview-ui`, `apps/cline-hub`) build.
- Unit suites: `apps/vscode` bun-unit (932 pass), webview-ui vitest (247 pass), and SDK package suites (llms 323, agents 41, shared 202) pass.

Watching CI here for the authoritative cross-platform signal.

### Type of Change

-   [x]  New feature (non-breaking change which adds functionality)
-   [x] ♻️ Refactor Changes
-   [x] 🏃 Workflow Changes

### Pre-flight Checklist

-   [ ] Changes are limited to a single feature, bugfix or chore (split larger changes into separate PRs)
-   [x] Tests are passing (`bun test`) and code is formatted and linted (`bun run format && bun run lint`)
-   [x] I have reviewed contributor guidelines
2026-06-24 15:46:39 +09:00
Dominic Cooney b5388e5c8d chore(vscode): bump version to 4.0.0 2026-06-24 14:48:03 +09:00
Saoud Rizwan 65b3977bc5 fix(vscode): forward OCA reasoning effort to SDK sessions (#11739) 2026-06-24 14:43:39 +09:00
Dominic Cooney f4a46cf8e7 fix(deps): drop global vite override so cline-hub webview keeps vite 8
The bun migration relocated apps/vscode/webview-ui overrides to the root
package.json, including vite ^7.1.11. As a workspace-wide override this
forced vite 7 onto apps/cline-hub/src/webview, which targets vite 8 and
uses rolldownOptions in its vite.config.ts. That broke `bun run -F
@cline/cli build` (cline-hub build:webview) with TS2769 on rolldownOptions.

Removing the global override lets each workspace resolve its declared
vite: webview-ui stays on vite 7.3.5, cline-hub resolves vite 8.0.16.
Both webviews build and the webview-ui vitest suite (247 tests) passes.
2026-06-24 14:36:34 +09:00
Dominic Cooney 0b47033a0f fix(core): suppress cross-package import lint in SAP handler-factory test 2026-06-24 14:23:23 +09:00
Saoud Rizwan c94ef4b750 fix(vscode): revert OpenAI-compatible metadata limit plumbing (#11775)
* fix(vscode): stop deriving output limits from model metadata

* fix(vscode): send OpenAI-compatible output token limit (#11776)
2026-06-24 14:12:00 +09:00
Tomás Barreiro 3e855f7d3f Fix other instances of issues with the litellm model list (#11773)
* Fix other instances of issues with the litellm model list

* address comment
2026-06-24 14:12:00 +09:00
Saoud Rizwan 2cd062ce68 fix(vscode): keep model metadata out of provider settings (#11772)
* fix(vscode): keep model metadata out of provider settings

* fix(vscode): prune stale provider model metadata

* docs(vscode): explain provider metadata pruning
2026-06-24 14:12:00 +09:00
Tomás Barreiro be6999209e Prevent injecting other models into the LiteLLM model list (#11771) 2026-06-24 14:12:00 +09:00
Saoud Rizwan 9a1e6121c7 fix(llms): align SAP AI Core provider config (#11759)
* fix(core): align SAP AI Core mode config

* fix(llms): map SAP AI Core credentials to service binding
2026-06-24 14:12:00 +09:00
Tomás Barreiro 4ae0ff4d5a Build the SDK sourcemaps (#11757)
* Build the SDK sourcemaps

* Do not minify

* do not minify packages when building sourcemaps
2026-06-24 14:12:00 +09:00
BarreiroT 18737f1448 Map ClinePass model information 2026-06-24 14:12:00 +09:00
Saoud Rizwan b56ce72fc7 fix(core): forward SAP provider options to gateway (#11756) 2026-06-24 14:11:59 +09:00
Max 425182c7c0 Remove chat scroll action button (#11734)
Co-authored-by: Max Paulus 🥪 <max@cline.bot>
2026-06-24 14:11:59 +09:00
Max 30fe302a71 fix retry after cline login issue (#11646)
Co-authored-by: Max Paulus 🥪 <max@cline.bot>
2026-06-24 14:11:59 +09:00
BarreiroT eadbc09ffd Update generated models 2026-06-24 14:11:59 +09:00
BarreiroT 3178487bdd fix cline-pass options 2026-06-24 14:11:59 +09:00
Saoud Rizwan f58941c500 fix(core): add OCA legacy reasoning effort (#11746) 2026-06-24 14:11:59 +09:00
Saoud Rizwan 8da5ffa874 fix: wire up SAP provider (#11745)
* fix(vscode): wire SAP AI Core session config

* fix(vscode): remove redundant SAP base URL mapping
2026-06-24 14:11:59 +09:00
Dominic Cooney 9ed95d1dc2 fix(llms): restore provider-request capture wiring lost in SDK migration 2026-06-24 14:11:59 +09:00
Dominic Cooney 5fc7341312 chore: regenerate bun.lock after rebase onto main 2026-06-24 14:11:37 +09:00
Dominic Cooney d5b38f38cb fix(vscode): preserve OrgClinePass error UI through SDK rebase 2026-06-24 14:11:37 +09:00
Tomás Barreiro 386401b41f Identfy accounts for feature flag resolution (#11741)
* Identify accounts for Feature Flag resolution

* simply code
2026-06-24 14:11:37 +09:00
Max 6471d2475a if search result is undefined then don't crash the extension (#11733)
Co-authored-by: Max Paulus 🥪 <max@cline.bot>
2026-06-24 14:11:37 +09:00
Max Paulus 🥪 fada079558 remove gap between approve bar and input box 2026-06-24 14:11:37 +09:00
BarreiroT 31d50ff54a Add log 2026-06-24 14:11:37 +09:00
BarreiroT efebc6380b Fix imports 2026-06-24 14:11:36 +09:00
Saoud Rizwan 2acc25cf6e test(vscode): raise vitest testTimeout to 20s to fix import-cost flakes (#11729)
Several vitest suites lazily await import() their subject inside the first
test (so vi.mock factories apply first). That import pulls in heavy workspace
packages (@cline/core, @cline/llms, @cline/shared), and on loaded CI runners
the first test in a file intermittently exceeds the 5s default timeout and
fails the nightly (observed in catalog.test.ts, now resolveModelInfo.test.ts).
Set a global 20s testTimeout so import cost attributed to the first test does
not cause flakes.
2026-06-24 14:11:36 +09:00
Saoud Rizwan 1ebe99c676 test(vscode): add invalidateProviderListings to auth-service mock controllers (#11727)
#11720 (feature flag resolution on startup) added a
controller.invalidateProviderListings() call to AuthService.sendAuthStatusUpdate
but did not update the test's mock controllers, which only stubbed
postStateToWebview. The new call threw on the mocks, so the throw happened
before postStateToWebview ran (failing the 'polls feature flags' test) and
caused subscribeToAuthStatusUpdate to delete the handler in its catch block
(failing the 'removes subscription on cleanup' test). Add the now-required
invalidateProviderListings stub to the mock controllers.
2026-06-24 14:11:36 +09:00
BarreiroT f5bbbd7f08 Log feature flags 2026-06-24 14:11:36 +09:00
Saoud Rizwan 8e4a2b8a19 fix(sdk): repair exposed provider auth routing (#11721)
* fix(sdk): repair exposed provider auth routing

* fix(sdk): use accessible ZAI coding plan default

* fix(sdk): use live Poolside model default

* docs(vscode): explain SDK provider key fallback
2026-06-24 14:11:36 +09:00
Tomás Barreiro dfdeffb558 Fix ModelAutocomplete selection (#11718) 2026-06-24 14:11:36 +09:00
Tomás Barreiro 1d22a51e30 Fix Feature Flag resolution on startup (#11720)
* Fix Feature Flag resolution on startup

* remove irrelevant test
2026-06-24 14:11:36 +09:00
Saoud Rizwan c827537386 test(vscode): use toBe instead of toMatchObject in proto conversion test (#11712)
api-configuration-conversion.test.ts is picked up by both the vitest
runner and the mocha-based vscode-test integration runner (.vscode-test.mjs
globs src/shared/**/*.test.js). vitest's jest-compat matcher toMatchObject
does not exist in the mocha runtime, so the test passed under vitest but
threw "toMatchObject is not a function" in the integration suite, failing
the nightly publish. Assert the two provider fields with toBe, which works
under both runners.
2026-06-24 14:11:36 +09:00
Saoud Rizwan f73121e369 test(vscode): warm catalog import to fix flaky 5s timeout (#11711)
The first test in catalog.test.ts paid the cost of dynamically importing
./catalog (which pulls in @cline/core, @cline/llms and @cline/shared)
inside its own 5s test timeout, intermittently failing CI/nightly runs.
Warm the import once in beforeAll so the cost falls outside any per-test
clock.
2026-06-24 14:11:36 +09:00
Saoud Rizwan 52bbda183d feat(vscode): expose additional SDK providers (#11703)
* feat(vscode): expose additional SDK providers

* fix(vscode): reserve skipped provider enum slots

* fix(vscode): keep Z.AI Coding Plan provider-specific
2026-06-24 14:11:36 +09:00
Saoud Rizwan 80036cef44 fix(sdk): route LiteLLM model fetches through SDK (#11705)
* fix(vscode): improve LiteLLM model fetch errors

* fix(vscode): align LiteLLM fetch return contract

* fix(sdk): improve LiteLLM private model fetch

* chore(vscode): drop duplicate LiteLLM fetch changes

* fix(vscode): route LiteLLM refresh through SDK
2026-06-24 14:11:36 +09:00
Saoud Rizwan b37f872a1f fix(vscode): honor OpenAI-compatible model settings (#11710)
* fix(vscode): honor OpenAI-compatible model settings

* fix(vscode): simplify OpenAI-compatible model bridge

* fix(vscode): respect OpenAI-compatible image support
2026-06-24 14:11:35 +09:00
Max ebe2b6498b fix(vscode): use Codex OAuth credentials (#11691) 2026-06-24 14:11:35 +09:00
BarreiroT 365250c785 Fix tests 2026-06-24 14:11:35 +09:00
BarreiroT 1e208472da fix tests 2026-06-24 14:11:35 +09:00
Tomás Barreiro ee974ea863 Fix ClinePass auth (#11680)
* Return local providers with ClinePass in the new extension

* Fix import

* Fix ClinePass auth
2026-06-24 14:11:35 +09:00
Tomás Barreiro a6416d02c8 Return local providers with ClinePass in the new extension (#11678)
* Return local providers with ClinePass in the new extension

* Fix import
2026-06-24 14:11:35 +09:00
Max Paulus 🥪 55cfeaec65 fix broken CI tests 2026-06-24 14:11:35 +09:00
Max Paulus 🥪 acfe779b20 fix local build not picking up .env file 2026-06-24 14:11:35 +09:00
Max Paulus 🥪 cff8c22f06 update vscode ignore
vsix bundling failed because of some unignored files
2026-06-24 14:11:35 +09:00
Max Paulus 🥪 8f2be20858 fix broken integ tests 2026-06-24 14:11:35 +09:00
Max Paulus 🥪 f1fd189631 remove storage tests from vitest
- these run under node resolution so they can see "bun:test" imports.
- these tests will get run by scripts/run-bun-unit-tests.ts instead
2026-06-24 14:11:34 +09:00
Cline Agent 9b03cb878d fix: repair SDK ClinePass webview rebase
Restore the webview feature-flag hook needed by the ClinePass onboarding/settings UI, but implement it against the existing posthog singleton instead of posthog-js/react so tests do not pull in a second React copy.

Make ClinePass settings follow the SDK provider-catalog pattern: render the Cline account card, resolve models with useProviderModels("cline-pass"), and persist selections with useProviderConfig/useProviderModelSelection for providerId="cline-pass". Remove the stale origin/main props that tried to drive the SDK-era ClineModelPicker, which is intentionally Cline-provider specific.

ClinePass remains hidden by the ext-cline-pass flag in settings/onboarding, and its model info hides token usage costs because billing is subscription-based.
2026-06-24 14:11:34 +09:00
Cline Agent 3dc020185f fix: post-rebase ClinePass plumbing for SDK migration
Resolve type-check and test breakages from rebasing the ClinePass
feature (origin/main) onto the SDK migration branch:

- provider-keys: re-add cline-pass to ProviderKeyMap and
  NON_SDK_PROVIDER_DEFAULTS (removed by the 'remove unused code'
  commit which predated ClinePass), so getProviderModelIdKey and
  getProviderDefaultModelId handle the cline-pass provider.
- provider-id: register 'cline-pass' in KNOWN_API_PROVIDERS so the
  Record<ApiProvider, true> constraint is satisfied.
- refreshClineRecommendedModels: add optional 'clinePass' field to
  ClineRecommendedModelsData so the RPC handler can map it into the
  proto response without a type error.
- refreshClineRecommendedModelsRpc: guard models.clinePass with ?? []
  for the same reason.
- handleClinePassProviderSelection: pass undefined (not null) to
  accountService.switchAccount to match the SDK signature.
- provider-keys.test: remove a duplicate closing brace left by the
  conflict resolution.
- Biome formatting (asNeeded semicolons) applied by check-types.
2026-06-24 14:11:34 +09:00
Dominic Cooney 4829f08b3f fix(vscode): reliable MCP OAuth on the SDK extension (ENG-2108, CLINE-2304) (#11529)
* fix(vscode): store MCP OAuth in shared settings file like the CLI (ENG-2108)

VSCode stored MCP OAuth tokens in a single mcpOAuthSecrets secrets blob
keyed by sha256(name:url), while the CLI/SDK store per-server oauth state in
cline_mcp_settings.json. The two never interoperated (CLI auth was invisible to
VSCode), and VSCode's read-whole-blob/write-whole-blob through StateManager's
non-refreshing cache meant concurrent windows clobbered each other's tokens.

- Store MCP OAuth state in the shared settings file in @cline/core's format.
- Reads are fresh from disk; writes are scoped read-modify-write of one
  server's oauth key via updateMcpServerOAuthState (now atomic temp+rename).
- Replace the vscode:// callback flow with HTTP-based token collection via
  authorizeMcpServerOAuth (same local loopback flow the CLI uses).
- Reconnect an unauthenticated server when its tokens appear (e.g. CLI auth).
- One-time migration of legacy mcpOAuthSecrets tokens into the shared file.
- Remove McpOAuthRedirectResolver, mcpOAuthFlow, completeOAuth, and the
  mcp-auth URI callback route.

* feat(vscode): add --instances/--random-port to MCP OAuth test server

Lets you start several independent test servers, each on its own OS-assigned
random port, so you can add multiple streamableHttp MCP servers to Cline at
once and exercise concurrent OAuth flows. baseUrl now reflects the actually
bound port so discovery metadata and redirect URIs stay correct under random
ports.

* fix(vscode): stop MCP OAuth handshake writes from livelocking the settings watcher (ENG-2108)

Now that codeVerifier/clientInformation live in the shared settings file, the
MCP SDK's per-connect-attempt saveCodeVerifier() writes were tripping the
settings watcher, which re-entered updateServerConnections -> connectToServer
-> another write, looping forever. It was especially bad with two+
unauthenticated servers, where each server's verifier churn re-triggered the
other (visible as a flickering, ever-changing codeVerifier nonce).

The watcher now compares a connection-relevant fingerprint (full per-server
config minus the oauth block, plus a boolean for whether an access token
exists) and skips writes that only churn OAuth-handshake fields. A token
appearing/disappearing still changes the fingerprint, so CLI/other-window
authorization continues to trigger a reconnect via serverGainedOAuthTokens.

* feat(vscode): print paste-ready MCP settings fragment from OAuth test server

On startup the test server now emits an mcpServers JSON fragment (nested
transport shape, matching cline_mcp_settings.json) alongside the banner, so you
can paste it straight into the settings file instead of hand-writing it. With
--instances the entries get distinct names (oauth-test-1, ...), each carrying
its actual bound port.

* fix(vscode): atomic MCP settings writes + fingerprint gate; drop timer guards (CLINE-2097)

Deleting one MCP server could empty the whole list. Root cause: settings
writes were non-atomic (fs.writeFile), so chokidar (and any other process)
could read a transient empty/torn file mid-write and reconcile to zero servers.
The previous fix only masked this with a per-process isUpdatingClineSettings
boolean cleared on a 300ms timer — it did nothing for the CLI or other windows
and was racy.

Replace both timer guards (isUpdatingClineSettings, isUpdatingFromRemoteConfig)
with two deterministic, process-agnostic mechanisms:

- writeSettingsFile(): atomic temp-file + rename for every settings write, so
  any reader always sees a complete file. Holds for any number of concurrent
  writers (CLI, multiple windows, SDK OAuth handshake).
- content fingerprint: the watcher reconciles only when the connection-relevant
  view changed. writeSettingsFile pre-seeds the fingerprint so our own write is
  a no-op, while a genuine change from any other process is still processed.
  Because reconcile is idempotent and reads are never torn, a missed
  suppression is at worst a redundant reconnect, never data loss.

All RPC writers (toggle disabled, autoApprove x2, timeout, add, delete) and the
remote-config sync now go through writeSettingsFile. Removes all setTimeout(.,
300) flag juggling.

* feat(vscode): add a non-guessable 'frozzle' tool to the MCP OAuth test server

The MCP OAuth test server now serves tools/list + tools/call exposing a
'frozzle' tool whose output cannot be derived without calling it (reverse the
string and swap each letter's case, wrapped in guillemets). This gives an eval
a reliable end-to-end signal that the OAuth-authenticated MCP round-trip really
happened: a correct 'frozzle <text>' answer can't be hallucinated. The
transform is easy to verify at a glance and invertible. Adds frozzle.test.ts.

* fix(sdk): drop lingering OAuth callback sockets on close so deny->approve re-auth works (ENG-2108)

The local OAuth callback server's close() called Server.close(), which only
stops accepting new connections and lets existing keep-alive sockets linger.
The browser / global-fetch connection pool keeps such a socket to the fixed
callback port (1456) alive. So after the user denied an MCP OAuth request and
retried, the retry's approve callback could be delivered over the pooled socket
to the FIRST (already-settled) server. That server's settle() was a no-op, so
waitForCallback() never resolved, finishAuth()/token exchange never ran, and no
token was saved — the server stayed unauthenticated (the deny->approve repro).

Call server.closeAllConnections() in close() so no pooled socket outlives the
server. Adds a regression test driving a keep-alive agent across close().

* fix(vscode): actually reconnect MCP server when toggled back on (ENG-2108)

toggleServerDisabledRPC only flipped the in-memory disabled flag and set status
to 'connecting', but never rebuilt the connection. A disabled server's
connection has no live transport/client, so re-enabling left it stuck on the
yellow 'connecting' indicator forever and never re-advertised its tools to the
agent.

Tear down and rebuild the connection through deleteConnection + connectToServer
(which opens a real transport when enabled, or a disconnected stub when
disabled), then notifyWebviewOfServerChanges so the SDK session's tool list is
refreshed. OAuth state is preserved (deleteConnection doesn't clear it). Adds
McpHub.toggleServerDisabledRPC.test.ts.

* fix(vscode): reload MCP tools silently without chat spam (ENG-2108)

Restarting the SDK session to pick up MCP tool changes appended visible chat
messages ('MCP tools changed - reloading...' and 'MCP tools reloaded
successfully...') plus a completion_result banner. Toggling several servers
piled up many of these. Tool reloading should be transparent.

Emit only the session status transitions (running -> idle) via
emitSessionEvents([], ...) instead of appendAndEmit, so no chat messages or
completion banner are shown. Genuine reload failures still surface an error
message. Updates sdk-mcp-coordinator.test.ts accordingly.

* docs(mcp): clean up comments to describe current behavior

Revise comments across the MCP OAuth and settings code to document the code as
it stands, dropping references to prior implementations, task IDs, and
before/after narration. Also reflow the auth-server regression test to the
repository's formatter. No behavior change.

* fix(vscode): atomic fallback write in remote MCP sync; document sync OAuth I/O

Make the no-McpHub branch of syncRemoteMcpServersToSettings write via an
atomic temp-file + rename so a concurrent reader never observes a torn or
empty settings file, matching every other settings write.

Document why the OAuth state read-modify-write in McpOAuthManager is
synchronous: it serializes this process's shared-file updates without a
Promise queue, which we prefer over async I/O for reliability of the
cross-process settings file.

* fix(mcp): serialize settings read-modify-writes

* docs(vscode): clarify MCP settings create race

* fix(vscode): create MCP settings atomically

* fix(cli): keep clearing missing MCP OAuth state a no-op

* fix(vscode): avoid yielding while holding MCP settings lock (#11596)

* fix(mcp): async lock acquisition for VSCode MCP settings/OAuth writes

Add updateMcpSettingsFile/updateMcpServerOAuthStateAsync to @cline/core that
yield the event loop while acquiring the cross-process settings lock instead of
blocking it with Atomics.wait. The critical section stays synchronous and the
mutator stays pure, so the lock is never held across an await and serialization
is preserved without an in-process queue.

Route the VSCode extension host's OAuth state writes (McpOAuthManager) through
the async variant so a connection-time OAuth callback can no longer freeze the
extension host event loop or deadlock against an in-flight updateMcpSettingsFile
whose lock-releasing continuation needs the loop.

Unify the sync and async acquisition paths on a shared reentrancy guard
(activeLocks) so a nested settings update on the same file fails fast instead of
self-deadlocking.

Tests: contended async serialization asserting zero Atomics.wait calls, async
stale-lock reclaim, reentrancy fail-fast, and uncontended run+release.

* fix(mcp): bootstrap missing settings file inside the lock; tidy docs

Creating the MCP settings file now happens in one place: the locked
read-modify-write helpers. A missing file reads as an empty settings object, so
the first write to a fresh path (e.g. a fresh-install `cline mcp add`) creates
it inside the lock instead of throwing ENOENT. The SDK (updateMcpSettingsFile /
updateMcpSettingsFileSync) and the VSCode lock helper share this contract, so
callers no longer need to pre-create the file. Add regression tests for the
SDK, the CLI wizard addServer(), and the VSCode helper on a missing path.

Also flag the synchronous SDK entry points (updateMcpSettingsFileSync,
updateMcpServerOAuthState) as preferring their async siblings, with a TODO to
delete them once all callers migrate, and tighten the lock-helper doc comments
to describe current behavior.

* fix(vscode): finish npm->bun migration in dev tooling, tasks, and docs

The npm->bun migration (#11632) updated package scripts, .vscodeignore and .vscode-test.mjs but left a trail of npm/npx/node invocations in editor configs, dev scripts, and docs. Following the breadcrumbs from 'npm run protos':

- .vscode/launch.json: standalone-core debug uses 'bun <file>.ts' (was npx tsx); Open Storybook uses 'bun run' (was npm run).
- .vscode/tasks.json: all task commands use 'bun run' (was npm run).
- scripts/run-extension-host.sh and .claude/hooks/claude-code-for-web-setup.sh: 'bun run' (was npm run).
- debug-harness/server.ts: shebang 'bun'; build steps use 'bun run protos', 'bun esbuild.mjs', 'bunx vite build' (were npm/node/npx).
- dev script shebangs (test-hostbridge-server, test-standalone-core-api-server, testing-platform-orchestrator, interactive-playwright): '#!/usr/bin/env bun' (was npx tsx).
- WebviewProvider HMR hint, e2e README, copilot-instructions, PR template, mcp-oauth-test-server docs, generate-state-proto message, tsconfig.test comment, state-keys test comment: bun.

Left untouched (correct per .clinerules/bun-and-node): Node-runtime invocations (node build.mjs), prebuild-install --target=<node>, vsce, 'npm install -g cline' (user CLI install), and App.stories.tsx mock chat fixtures.

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
Co-authored-by: Cline Agent <cline-agent@users.noreply.github.com>
2026-06-24 14:11:34 +09:00
Dominic Cooney 82d1846a45 Migrate apps/vscode from npm/node to bun (#11632)
* chore(vscode): migrate package management & build from npm/node to bun

Fold apps/vscode (+ webview-ui, testing-platform) into the root bun
workspace so the extension consumes the local @cline/* SDK packages via
workspace symlinks instead of pinned published versions, eliminating the
SDK vendoring cycle. Node remains the runtime (extension host, standalone
cline-core, esbuild platform:node, prebuild-install ABI target).

- root: drop "!apps/vscode", add nested members, relocate overrides to
  root, add trustedDependencies [better-sqlite3, grpc-tools]
- apps/vscode: @cline/* -> workspace:*, scripts -> bun/bunx,
  npm-run-all -> bun --parallel, drop cross-env; keep esbuild + vite;
  declare previously-hoisted phantom deps (nice-grpc-common, playwright)
- package-standalone.mjs: npm install -> bun install (isolated dist dir)
- CI: setup-bun + single root bun install --frozen-lockfile, build:sdk
  before extension build, better-sqlite3 binary + zero-test guards;
  publish workflows intentionally keep setup-node for vsce/ovsx
- docs/comments: curated pass (keep-list vs rewrite-list), add
  apps/vscode/docs/bun-migration-notes.md guard doc
- delete npm lockfiles (root bun.lock authoritative)

Deferred to follow-up PRs: test-runner migration to bun test (Phase 4)
and devDep cleanup (Phase 6).

* test(vscode): add bun test foundation for the vitest-native unit suites

Phase 4a of the test-runner migration. Adds a bun test runner that
reaches full parity (582 pass / 0 fail / 50 files) with the existing
vitest SDK-adapter + model-catalog suite, without touching the
@vscode/test-cli integration tests or the webview vitest suite.

- bunfig.toml: [test] preload
- src/test/bun-test-preload.ts: mock.module() shadows `vscode` and
  `@cline/core` with their unit-test stubs (bun's onResolve plugin hook
  does not intercept host/symlinked specifiers); seeds real @cline/core
  export names as undefined to satisfy bun's strict ESM named-import
  linking; full vitest->bun:test shim (vi.fn/mocked/spyOn, describe/it/
  expect/before*/after*)
- scripts/run-bun-tests.ts: mirrors vitest.config.ts include[] exactly and
  runs with --parallel for per-file mock isolation (bun test's single-process
  default lets mock.module clobber across files)
- test:bun script

* test(vscode): migrate node-side unit suite from mocha to bun test

Phase 4b of the test-runner migration. The standalone mocha unit runner
(.mocharc spec: __tests__/* + test/services/**) was already broken under
bun (mocha was a phantom dependency — only @types/mocha/ts-node were
declared, npm hoisted mocha transitively). Migrate it to `bun test`.

- codemod 77 files: import { ... } from "mocha" -> "bun:test", renaming
  before->beforeAll / after->afterAll at imports and call-sites; chai,
  should and sinon kept as libraries (they work under bun test)
- convert sinon.stub() on ESM namespace exports to mock.module()/spyOn
  (bun loads real ESM: "ES Modules cannot be stubbed")
- scripts/run-bun-unit-tests.ts: runs the .mocharc spec set with one
  isolated `bun test` process per file (Bun.spawn + concurrency pool),
  restoring vitest-forks module-registry isolation (bun's single-process
  default lets mock.module leak across files)
- scripts/codemod-mocha-{to-bun,this}.ts: one-shot migration tooling
- test:unit now runs the bun unit runner; CI calls bun + a non-zero
  pass-count guard instead of `bunx nyc ... mocha`
- tsconfig: add root node_modules/@types to typeRoots so `bun:test`
  types resolve under tsc; cast loose os.userInfo mocks in shell.test

Result: unit suite 58 files / 880 pass / 0 fail; vitest set still
582/0. @vscode/test-cli integration tests and webview vitest unchanged.

* chore(vscode): remove dead mocha-runner deps and artifacts

Phase 6 cleanup after the bun test migration. The standalone mocha unit
runner is gone (replaced by scripts/run-bun-unit-tests.ts), so its
config and now-unused devDependencies are removed.

- remove dead files: .mocharc.json, tsconfig.unit-test.json,
  src/test/requires.ts, .nycrc.unit.json
- remove unused devDeps: @types/mocha, @types/proxyquire, ts-node,
  tsconfig-paths, cross-env, npm-run-all, nyc, proxyquire, husky
  (root owns the husky hook; chai/should/sinon stay — used as libs)
- install:all -> single root `bun install` (workspace covers webview-ui)
- drop .mocharc.json / .nycrc*.json from CI paths-filters and
  .vscodeignore; add bunfig.toml to the filters

Verified: check-types clean, unit 880/0, vitest 582/0.

* fix(vscode): import bun:test globals in tests that relied on ambient @types/mocha

CI Quality Checks (clean `bun install` without @types/mocha) surfaced
TS2582/TS2304 "Cannot find name 'describe'/'it'/'beforeEach'" in test
files that used the global mocha/jest test functions without importing
them. The Phase 4b codemod only rewrote files that imported from
"mocha"; these used ambient globals, so they were missed (and passed
locally because a stale @types/mocha lingered in node_modules).

Add explicit `bun:test` imports (before->beforeAll, after->afterAll in
TelemetryService.test.ts). chai/sinon stay as libraries.

Verified against a clean tree (no @types/mocha): check-types 0 errors,
unit suite 58 files / 880 pass / 0 fail.

* style(vscode): biome-format migrated test files + codemod scripts

The mocha->bun:test codemod and manual import edits left formatting that
didn't match biome (the CI `format` check, which validates files changed
since main, flagged them). Also narrow setup.ts's bun:test import to the
actually-used beforeEach/afterEach (describe/it only appear in a JSDoc
example), fixing a noUnusedImports lint error.

ci:check-all (check-types + lint + format) now passes locally.

* fix(webview-ui): declare phantom deps + pin React 18 types under bun workspace

Folding webview-ui into the bun workspace changed its install topology
from an isolated npm flat tree to the shared hoisted store, surfacing
two classes of pre-existing latent issues that npm hoisting had masked:

1. Phantom dependencies: src imports `marked`, `unist`, `unist-util-visit`
   and `@heroui/theme` directly but never declared them. Declared them
   (marked ^15, unist-util-visit ^5, @types/unist ^3, @heroui/theme 2.4.26).
2. React types: @testing-library/react's optional peer pulls @types/react@19
   into a resolvable location; tsc mixed it with the toolkit's React 18
   types (React 19 dropped Component.refs), breaking 452 JSX usages. Pin
   react/react-dom type resolution to webview-ui's React 18 copy via
   tsconfig paths.

build:webview (tsc -b && vite build) and ci:check-all now pass.

* fix(vscode): restore @types/mocha for integration build + add bun:test types

The @vscode/test-cli integration runner still uses mocha, and
tsconfig.test.json compiles all src/**/*.test.ts (including bun-migrated
files) to out/. So:
- restore @types/mocha (integration compile needs the mocha ambient types)
- add `bun` to tsconfig.test.json types + root @types to both tsconfig
  typeRoots so `bun:test` resolves under tsc for the migrated tests

* fix(vscode): declare glob — phantom dep used by package-standalone.mjs

scripts/package-standalone.mjs imports `glob` but it was never declared
(resolved transitively under npm's flat hoist). Under the bun workspace
store it's unresolvable, failing postcompile-standalone with
ERR_MODULE_NOT_FOUND. Declare glob ^11 (modern named-export API).

compile-standalone now produces dist-standalone/standalone.zip.

* fix(ci): strip ANSI before vitest zero-test guard grep

The vitest summary line colorizes the count ("Tests  <ansi>582 passed"),
so the count isn't adjacent to the "Tests" label in raw bytes and the
guard regex failed even though 582 tests passed. Strip ANSI escapes
before matching.

* fix(vscode): declare minimist — phantom dep in testing-platform-orchestrator

scripts/testing-platform-orchestrator.ts imports `minimist` (undeclared,
resolved transitively under npm hoist). Declare it so the testing-platform
integration job runs under the bun workspace store.

* fix(vscode): restore tsconfig-paths for integration runner; tp-orchestrator uses bun

Phase 6 over-removed tsconfig-paths: test-setup.js (loaded by the
@vscode/test-cli mocha integration runner) requires it to resolve @/
aliases in the compiled out/ tree — the extension host test runner failed
with "Cannot find module 'tsconfig-paths'". Restore it. Also switch the
testing-platform spawn from `npx ts-node index.ts` to `bun index.ts`
(bun runs TS natively; avoids the removed ts-node).

* fix(vscode): route tests by bun:test import marker; integration runner stays mocha

The mocha->bun codemod swept up tests that the Node-based @vscode/test-cli
integration runner compiles/runs, which cannot load the `bun:test` builtin
(and some need the real VSCode host). Establish a single source of truth:
a *.test.ts is bun-runner-owned IFF it imports "bun:test".

- run-bun-unit-tests.ts: discover files by the bun:test import marker
  (not fixed globs), so every migrated file runs under bun.
- build-tests.js: generate a tsconfig that excludes all bun:test files
  from the integration compile (json5-parsed), so out/ never contains
  bun:test; gitignore the generated config.
- .vscode-test.mjs: exclude the bun unit dirs from the runner globs.
- revert host-dependent tests (hostbridge/*, extension, terminal,
  FileContextTracker host bits) and 3 files with sinon-on-ESM/behavioral
  issues (ClineIgnoreController, mentions, TelemetryService) back to
  mocha; they run on @vscode/test-cli as before.

Verified: check-types 0 errors; compile-tests 0 bun:test in out/;
bun unit 65 files/962 pass/0 fail; vitest 582/0.

* fix(vscode): declare mocha — phantom dep for @vscode/test-cli integration runner

The @vscode/test-cli extension host loads `mocha` at runtime to run the
integration suite, but only @types/mocha was declared (npm hoisted the
mocha package transitively; bun's store does not expose it). The host
failed with "Cannot find module 'mocha'". Declare mocha ^11.7.4 (matches
@vscode/test-cli's own range).

* fix(vscode): robust Windows protoc-gen-ts_proto plugin resolution under bun

build-proto.mjs hardcoded node_modules/.bin/protoc-gen-ts_proto.cmd for
Windows, but bun's workspace store places/extensions the bin shim
differently (hoist + .cmd/.bunx), so Windows protos failed with
"protoc-gen-ts_proto: The system cannot find the file specified". Probe
the local + root .bin with known shim extensions instead. Also update
the testing-platform usage string (ts-node -> bun).

* fix(vscode): generate node .cmd wrapper for ts-proto plugin on Windows

The previous probe found bun's `.bunx` shim, but protoc cannot exec it
("%1 is not a valid Win32 application"). Instead, on Windows generate a
small .cmd wrapper that runs the resolved protoc-gen-ts_proto JS via
`node`, which protoc can execute regardless of package manager. POSIX
path (direct JS bin) is unchanged.

* fix(vscode): package VSIX with --no-dependencies (bundled) to stop monorepo traversal

Under the bun workspace, @cline/* are workspace:* symlinks pointing to
../../../../sdk/packages/*. vsce, walking the dependency tree, followed
them out of apps/vscode and packaged the whole monorepo (../, ~84MB incl.
root node_modules and .env), which crashed vsce's secret scanner and
failed all e2e jobs.

The extension is fully esbuild-bundled into dist/extension.js, so vsce
should not walk node_modules at all. Add --no-dependencies to every
vsce/ovsx package/publish path (e2e build, marketplace, nightly), and
tighten .vscodeignore to drop nested node_modules and dev-only inputs
(scripts, proto, testing-platform, bunfig, esbuild.mjs, etc.).

Result: VSIX is 39 files / ~7 MB and the secret scan passes.

* docs(vscode): tighten bun/node comments and consolidate into a clinerule

- add .clinerules/bun-and-node.md (eternal-now: bun=tooling, node=runtime,
  keep-list, and the bun:test-vs-mocha test routing rule); remove the
  apps/vscode/docs/bun-migration-notes.md migration doc and point
  .clinerules/general.md at the rule (single-line bullet matching the file).
- fix the hotfix-release note: there is no infra step that regenerates the
  lockfile; a CHANGELOG+version bump leaves bun.lock consistent (workspace
  versions aren't pinned) and publish runs --frozen-lockfile.
- reframe runner/preload comments to describe the code as-is (drop
  "migrated off mocha"/codemod history); add a TODO on the bun-test preload
  to migrate suites off the vitest `vi` shim to native bun:test and delete it.
- remove the one-shot mocha->bun codemod scripts.

* fix(debug-harness): pin debugee VSCode version so bundled Playwright can drive it

The harness downloaded "stable" VSCode (currently 1.125 / Electron 42),
which the bundled Playwright cannot drive — `_electron.launch()` hangs
until its 60s timeout (Electron started and a window appeared, but the
launch handshake never completed). Default to a known-good version
(1.103.0, matching the e2e CI matrix) and allow override via
VSCODE_TEST_VERSION.

* fix(webview): render under bun workspace — dedupe React, drop stale codicons link

The webview mounted but crashed before rendering (blank sidebar; e2e
"Login to Cline" never visible) with "Cannot read properties of null
(reading 'useRef')" — the classic two-React-copies / null hook dispatcher.
Under the bun workspace, sibling packages pull react@19 into the shared
store and a transitive webview dep resolved a second React instance into
the vite bundle. Add resolve.dedupe + pin react/react-dom to webview-ui's
own React 18 copy.

Also drop the separate `<link>` to node_modules/@vscode/codicons in the
webview HTML: the webview's index.css already @imports codicons, so the
font is bundled into the build assets. Under bun that node_modules path
is a symlink to the root store (outside the webview localResourceRoots)
and isn't packaged with --no-dependencies, so the link 404'd; the bundle
covers it. Re-scope the .vscodeignore nested-node_modules exclude so it
no longer shadows the codicons re-include.

* fix(debug-harness): disable GPU so the debugee renders in headless/VM envs

On headless/VM GPU stacks the debugee Electron's GPU process crash-loops
("Exiting GPU process during initialization" / CreateCommandBuffer
kTransientFailure), killing the window before Playwright finishes
attaching and tripping the 60s launch timeout. Force software rendering
(--disable-gpu and friends) for a stable harness launch.

* fix(debug-harness): survive launch failures; configurable, longer launch timeout

The harness crashed (whole bun process exited) whenever VSCode launch
failed/timed out: Playwright emits a late unhandled rejection on the dead
CDP transport after we've already handled the launch error, and the
default behavior takes the HTTP server down with it — forcing a full
restart just to retry.

- Add process-level unhandledRejection/uncaughtException guards so stray
  async errors are logged and the server keeps serving (retry via `launch`).
- On launch failure, close the orphaned Electron so a retry isn't blocked.
- Make the _electron.launch timeout configurable (--launch-timeout) and
  raise the default to 120s for cold launches; document VSCODE_TEST_VERSION.

* fix(ci): address review feedback — vsix --no-dependencies, drop stale coverage path, Windows shell

- ext-vscode-publish-stable.yml: add --no-dependencies to the release-artifact
  `vsce package` (Max's catch). Without it, vsce follows the @cline/* workspace
  symlinks out of the package and bloats the .vsix with the whole monorepo.
- ext-vscode-test.yml: drop the stale apps/vscode/coverage-unit/lcov.info upload
  path (Max's catch). That file was produced by the removed nyc unit-coverage
  step (.nycrc.unit.json); nothing generates it now.
- ext-vscode-test-e2e.yml: the better-sqlite3 assert step ran under the Windows
  runner's default pwsh and failed to parse the POSIX test. Pin it to `shell: bash`
  (Git Bash ships on windows-latest); the non-e2e job already defaults to bash.

---------

Co-authored-by: Cline Agent <cline-agent@users.noreply.github.com>
2026-06-24 14:11:34 +09:00
Max 8636272eb5 Improve onboarding funnel metrics (#11650)
* improve onboarding metrics

* fix onboarding page view dedupe

---------

Co-authored-by: Max Paulus 🥪 <max@cline.bot>
2026-06-24 14:11:34 +09:00
Max Paulus 🥪 815b0346f6 fix package lock issues post rebase 2026-06-24 14:11:34 +09:00
Max 84fe95de4d fix(vscode): persist Vertex provider settings (#11565)
Co-authored-by: Max Paulus 🥪 <max@cline.bot>
2026-06-24 14:11:34 +09:00
Max 36be3333d4 fix(vscode): preserve legacy task metadata on resume (#11570)
Co-authored-by: Max Paulus 🥪 <max@cline.bot>
2026-06-24 14:11:34 +09:00
Dominic Cooney 96c0bd70a4 chore(vscode): remove stale HuggingFace provider test 2026-06-24 14:11:33 +09:00
Max Paulus 🥪 6f621e4bf2 fix standalone e2e test 2026-06-24 14:11:33 +09:00
Max Paulus 🥪 d7ea201c25 bump sdk version 2026-06-24 14:11:33 +09:00
Dominic Cooney e3b3e6b0ad fix(vscode): simpler login UX (ENG-2174) — remove inline provider gate, restore debug harness (#11534)
* remove inline no-usable-provider sign-in banner; rely on inference-time errors

The "Sign in to Cline or set up a provider" banner gated chat input on a
parallel provider-usability heuristic that mis-detected BYOK setups
(Bedrock profile/IAM, Vertex ADC) and its sign-in button discarded the
device code. Remove the component and the hasUsableProvider plumbing.

Auth/config problems now surface at inference time, where handling
already exists:
- cline provider without a token -> emitClineAuthError -> ErrorRow
  renders the Sign in button with the device-code display
- any other misconfigured provider -> say:"error" row

Also deletes the now-dead sdk/provider-usability module and adds a test
that failed session start emits a plain chat error.

* restore debug-harness server deleted in 0bfbfb944

Commit 0bfbfb944 ("delete unused files") removed src/dev/debug-harness/server.ts
as dead code, but it is a dev tool launched directly via
`npx tsx src/dev/debug-harness/server.ts` (see its README and
.clinerules/debug-harness.md) — no static import graph reaches it, which
is why the unused-file analysis flagged it. The README, the .clinerules
docs, and the CLINE_CAPTURE_BROWSER / __clineHandleUri hooks in
extension.ts and utils/env.ts that exist solely for this harness all
survived the deletion, leaving them dangling.

Restored verbatim from 0bfbfb944~1; verified it boots and listens on
:19229.
2026-06-24 14:11:33 +09:00
Saoud Rizwan 53d0f50494 fix(vscode): restart session when user switches provider (#11507)
* fix: format Cline OAuth tokens in provider config

* fix(vscode): restart SDK session on provider switch

* fix(vscode): serialize SDK provider restarts

* chore(vscode): keep provider switch PR scoped

* fix(vscode): simplify deferred provider restarts
2026-06-24 14:11:33 +09:00
Mikołaj Kondratek 141a61fd45 fix: thread proxy/CA-aware fetch into the SDK inference path (#11462)
* fix: thread proxy/CA-aware fetch into the SDK inference path

The main agent loop did not receive the host's proxy/CA-aware fetch, so
on JetBrains and the CLI inference over a corporate proxy or to a
self-signed/private-CA endpoint failed with "unable to get local issuer
certificate". This regressed at the SDK cutover: the pre-SDK CLI
(2.18.0) constructed provider clients with a proxy-aware fetch directly,
while the SDK agent loop fell back to bare global fetch (CLINE-2353).

Two layers:
- App (cline-session-factory.ts): always build CoreSessionConfig.
  providerConfig and carry the proxy-aware fetch from @/shared/net, not
  just for Bedrock. In VSCode this fetch is global fetch, so behavior is
  unchanged there; in the standalone (JetBrains) build it is undici with
  EnvHttpProxyAgent.
- SDK (handler-factory.ts): forward providerConfig.fetch into
  createGateway both as the top-level fallback fetch and per provider, so
  the gateway's provider clients use it. Passing undefined is a no-op
  (registry resolves config?.fetch ?? defaults?.fetch ?? fallbackFetch),
  so other SDK consumers are unaffected.

The SDK change covers every host that supplies a fetch; the app change
covers VSCode and JetBrains. The CLI builds its session config through a
separate path (apps/cli) that does not yet wire a proxy-aware fetch, so
CLINE-2353 on the CLI surface is addressed in a follow-up.

Adds a handler-factory unit test asserting the host fetch is forwarded
to createGateway at both the top level and per provider.

* fix: deterministically install proxy dispatcher in standalone core

The proxy/CA-aware undici dispatcher is installed as a side effect of
loading @/shared/net (it calls setGlobalDispatcher with EnvHttpProxyAgent
in the standalone build). The standalone entry cline-core.ts did not
import that module, so the dispatcher was only installed incidentally
when some other transitively-imported module happened to pull it in. A
future change to the import graph could silently drop proxy/CA support on
JetBrains.

Import @/shared/net for its side effect, first, so the install is
deterministic and runs before any network use (CLINE-2353).

Standalone-only hardening; VSCode uses global fetch and is unaffected.
2026-06-24 14:11:33 +09:00
Saoud Rizwan 7578a1de36 fix(vscode): fix duplicate tool row when changing plan/act mode during pending tool approval (#11437)
* fix(vscode): suppress duplicate tool row when a mode change clears a pending approval

Switching plan/act while a tool approval was pending duplicated the
approval row in chat. clearPending resolved the pending approval as
denied, which unblocks the core; the core then emits the denied tool
call's content_start/content_end events before the mode coordinator's
abort lands. The interactive deny paths record the denial in the
message translator state so those events are suppressed, but
clearPending skipped that step, so the translator rendered the events
as a fresh say:tool row next to the still-visible approval ask.

clearPending now records the denial through recordDeniedToolApproval
before resolving, mirroring resolvePendingToolApproval. This covers all
clearPending callers: mode changes, task cancel, and task clear.

* refactor(vscode): trim the clearPending denial fix to its minimal shape

Keep clearPending's original structure, only inserting the denial
recording before the resolve. Drop the end-to-end suppression test:
translator suppression for recorded denials is already covered by
message-translator-approval-denial.test.ts, and the clearPending
recording is covered by the extended unit assertion.
2026-06-24 14:11:33 +09:00
Saoud Rizwan 5aa60cf116 fix(vscode): restore aggressive pin-to-bottom auto scroll in chat view (#11436)
* fix(webview): restore aggressive pin-to-bottom auto scroll in chat view

The auto-scroll effect only fired on groupedMessages.length changes, but in
the SDK-migrated extension new content can appear in the chat without the
message list length changing:

- The Thinking placeholder row is driven by turnState alone (e.g. the plan
  to act switch auto-continues the task with no new message), and it was
  appended to the rendered list inside MessagesArea where the scroll hook
  never saw it.
- New tool messages merge into the trailing tool group, and the thinking
  placeholder gets swapped for a real reasoning row at constant length.

Fixes:
- Lift the thinking placeholder computation out of MessagesArea into a new
  useDisplayedGroupedMessages hook so ChatView feeds the same list to both
  Virtuoso and useScrollBehavior; the placeholder appearing now pins to
  bottom like a real message.
- Key the pin effect on the tail message ts (skipping the placeholder) in
  addition to list length, covering in-place tail changes.
- Re-engage auto scroll when turnState.phase transitions into streaming. In
  the old extension every turn start was accompanied by a user send/button
  click that reset disableAutoScrollRef; turnState-driven turn starts like
  plan to act auto-continue have no webview-side action, so handle it in
  the scroll hook.

* refactor(webview): replace scroll fix with minimal single-file version

Same three behaviors as the previous commit (pin when the thinking
placeholder appears, pin on in-place tail changes, re-engage auto scroll
when a turn starts streaming) but implemented as two small effects in
MessagesArea, which already has both the rendered list and scrollBehavior
in scope. Reverts the useDisplayedGroupedMessages hook extraction and the
ChatView/useScrollBehavior changes; net diff vs the base branch is now
one file.
2026-06-24 14:11:33 +09:00
Dominic Cooney b62e572ea9 test(vscode): exercise full SDK structured edit flow in file-edit e2e (#11442)
* test(vscode): exercise full SDK structured edit flow in diff.test.ts e2e (ENG-2042)

The SDK runtime executes structured (OpenAI-format) tool calls instead of parsing XML-style tool syntax out of assistant text. Teach the e2e mock server to stream an editor tool call for edit_request (arguments split across deltas to exercise fragment reassembly), answer the SDK's follow-up tool-result request (role:'tool' message) with turn-ending completion text, and remove the classic XML-era EDIT_REQUEST/REPLACE_REQUEST responses.

diff.test.ts now covers the full approval flow: approval ask row -> Save -> editor tool writes the file -> completion text, verifying the edit on disk and restoring the git-tracked fixture afterwards. The old 'test.ts: Original <-> Cline's Changes' diff-tab assertions are unreachable under the SDK executor architecture (the editor executor writes via Node fs and does not route through DiffViewProvider); this behavioral difference is documented in the test file.

* test(vscode): address review feedback on diff.test.ts e2e

- Scope the mock server's tool-result follow-up detection to edit_request conversations so tool results from other (future) scenarios don't mis-route to EDIT_REQUEST_COMPLETE.

- Move the fixture readFileSync inside the try block and guard the finally restore, so a failed read doesn't bypass cleanup attribution or write undefined back to the fixture.

* docs(vscode): rephrase diff e2e comments to describe current behavior

Comments described historical behavior (XML-style tool-call parsing that predates the SDK runtime), which is confusing to readers of the current code. Rephrase them to describe the code as it exists now.

* test(vscode): rename diff.test.ts to file-edit.test.ts and drop duplicated preamble

The test no longer touches a diff editor (the SDK editor executor writes files directly after approval), so the 'Diff Editor' name was misleading. Rename the file and describe block to match what it asserts: the file-edit approval flow.

Drop the first half of the test (send hello, wait, New Task, check history), which duplicated chat.test.ts, and the mock server's 500ms delay that existed only to support an 'API Request...' visibility assertion that no longer exists.
2026-06-24 14:11:33 +09:00
Robin Newhouse 268462ddcf fix(vscode): stabilize SDK e2e login flow (#11441) 2026-06-24 14:11:33 +09:00
Dominic Cooney 072e237d01 fix(vscode): persist skill disable to SKILL.md frontmatter so the model honors it (ENG-1995) (#11294)
* fix(vscode): persist skill disable to SKILL.md frontmatter so the model honors it (ENG-1995)

The VS Code skill toggle only updated extension state (globalSkillsToggles /
localSkillsToggles), but the SDK builds the model's skill list and the `skills`
tool from each SKILL.md's frontmatter `disabled` flag. As a result, disabling a
skill in the sidebar left it fully available to the model, including in new
tasks.

toggleSkill now also writes the `disabled` flag to the skill's SKILL.md
frontmatter (no-op for remote skills, which have no backing file), via new
helpers updateSkillMarkdownDisabledState / setSkillDisabledInFrontmatter in
skills.ts. Adds unit tests for both helpers.

* fix(vscode): don't rewrite skills with malformed frontmatter (ENG-1995)

parseYamlFrontmatter fails open on invalid YAML, returning the full original
document as the body. updateSkillMarkdownDisabledState would then prepend a
second `---` block on a disable, corrupting the file. Bail out and leave the
file untouched when frontmatter fails to parse. Adds tests for the malformed
disable/enable cases.

Addresses Greptile review feedback on #11294.

* test(vscode): assert malformed-skill fixture is actually invalid YAML (ENG-1995)

Add a guard test that parseYamlFrontmatter reports hadFrontmatter and a
parseError for the shared malformed fixture, so the two "leave file untouched"
tests can't silently pass via a different code path if the fixture ever became
valid YAML.

Addresses Greptile review feedback on #11294.

* fix(vscode): resolve @cline/shared/storage subpath in mocha unit-test compile

The CommonJS mocha unit-test runner uses classic "node" moduleResolution,
which does not read the `exports` subpath maps in @cline/* package
manifests, so `@cline/shared/storage` (imported by
src/sdk/telemetry-settings-sync.ts) failed with TS2307 when test files
transitively reach the SDK adapter. Mirror the explicit paths mapping
already added to tsconfig.test.json for the integration-test compile.

* fix(vscode): restore E2E mock auth in SDK auth service so e2e tests can sign in

The SDK migration replaced classic AuthService (which swapped in
AuthServiceMock under E2E_TEST) with sdk/auth-service.ts, losing the
mock path. "Login to Cline" then invoked the real SDK OAuth flow and
opened a native browser dialog the Playwright tests cannot interact
with, so helper.signin() never authenticated and chat.test.ts +
diff.test.ts failed on every platform (the failures also reproduce on
the base branch).

- auth-service.ts: under E2E_TEST=true (and CLINE_ENVIRONMENT=local),
  exchange the well-known test code with the local mock API server and
  persist credentials to providers.json — no browser. Replaces classic
  AuthServiceMock (see origin/main src/services/auth/AuthServiceMock.ts).
- chat.test.ts/diff.test.ts: wait for the mock turn to complete before
  clicking New Task; SDK history is persisted at turn end, so navigating
  mid-turn races the write and Recent never shows.
- diff.test.ts: the footer Start New Task button only appears for
  attempt_completion turns under SDK TurnState; use the header New Task
  button like chat.test.ts.
2026-06-24 14:11:32 +09:00
Saoud Rizwan 31cff1f75e fix(vscode): auto-continue the task when switching from plan to act (#11401)
* fix(vscode): enforce stop-before-start ordering for same-id session restarts

The app reuses the taskId as the sessionId whenever it replaces or
resumes a session (mode/MCP rebuilds, follow-up resume, history
restore), but the old session's stop ran fire-and-forget, and core
cleanup is keyed by sessionId across multiple awaits. A stop still in
flight when the same-id replacement started could tear down the live
successor: late sessions-map deletes, a late 'ended' emission, or a
stalled status write landing on the replacement.

Adopt the sequencing invariant the CLI has always used: never start a
same-id session while its stop is in flight. SdkSessionLifecycle tracks
in-flight stops in a pendingStops map keyed by sessionId, and
startNewSession awaits the pending stop for a reused id before starting
(with a log line so a wedged stop is diagnosable). Fresh-id starts
never wait. fireAndForgetSend additionally captures the ActiveSession
by object identity at send time so a send settling after a same-id
replacement cannot flip the successor's run state.

* fix(vscode): auto-continue the task when switching from plan to act

In plan mode, the model's switch_to_act_mode tool call flipped the toggle
but ended the run as aborted: the beforeModel stop hook fired after
turn-started, leaving a dangling api_req_started spinner rendered as
'API Request Cancelled', and nothing continued the task after the
act-mode rebuild. Manually toggling after a presented plan had the same
dead end.

The tool now declares lifecycle.completesRun so the run ends cleanly
after the tool result, and the queued mode change rebuilds the session
and auto-continues with a hidden continuation prompt. A manual plan to
act toggle auto-continues only when the agent is idle after presenting
its plan (not running and awaiting_followup; a pending ask_question
blocks mid-run so it cannot false-positive). Composer content rides
along: typed text becomes the continuation, attachments are forwarded
and echoed, attachment-only toggles count as consumed. The RPC reports
consumption only after the send was actually handed to the session, and
the webview then clears only the exact submitted content, so failures
and racing input never lose composer state. Failures before the send
undo the optimistic running flip, report an error phase, and roll the
mode back when the session was never replaced.

Hidden prompts (the act continuation and the pre-existing task
resumption prompt) shifted editMessageAndRegenerate's visible-to-SDK
user message ordinal mapping; the new sdk-user-message-mapping module
skips them in their persisted user_input-wrapped shape, counts
attachment-only messages (which have visible bubbles), ignores
tool-result rows, and attachment-only resumes now echo a bubble to keep
both transcripts aligned. Follow-ups sent during a rebuild wait on
waitForPendingRebuild instead of resuming a parallel session that the
rebuild would kill.

The plan-mode system prompt and tool description require explicit user
approval in a message sent after the plan was presented, preventing the
model from self-escalating to act mode.

* fix(vscode): move the turn phase to error when a task resume fails

askResponse optimistically sets the turn phase to streaming before
delegating to the followup coordinator, but the coordinator's resume
catch only posted an error row, leaving the footer stuck on
Thinking/Cancel. Resume failures (auth errors, session start errors)
now report back via onResumeFailed so the controller can set the phase
to error.
2026-06-24 14:11:32 +09:00
Saoud Rizwan 9a981f81d7 fix(webview): use consistent reasoning selector component in extension provider settings (#11399)
* fix(webview): use themed components and reasoning selector in generic provider settings

The catalog-backed GenericProviderSettings path (deepseek, gemini, mistral,
and other migrated providers) rendered its model picker with raw unstyled
HTML select/input/button elements, unlike every other provider which uses
the VS Code webview-ui-toolkit components. Swap ModelPickerWithManualEntry
to VSCodeDropdown/VSCodeOption/VSCodeTextField/VSCodeButton, reusing the
DropdownContainer and re-init key workaround from common/ModelSelector.

Also render ReasoningEffortSelector in GenericProviderSettings when the
selected model's catalog info has supportsReasoning, persisting the effort
through the provider config reasoning patch, matching ClineModelPicker.
This is driven by the catalog capability flag rather than provider id.

* fix(webview): re-sync custom model id field after async config hydration

The controlled customModelId state was initialized once at mount, but the
provider config and model catalog both hydrate asynchronously, so the lazy
initializer could capture a placeholder value and leave the custom model
text field stale once the committed selection loaded. Sync the field via an
effect keyed on the committed model id and its in-list status, depending on
derived values rather than the models object whose identity can change
every render while the catalog loads.
2026-06-24 14:11:32 +09:00
Robin Newhouse d8b1ce54d4 fix(vscode): expand remote workflow/skill slash commands before send ENG-2036 (#11388)
* fix(vscode): expand remote workflow/skill slash commands before send

The SDK-backed extension sent `/workflow` text to the model verbatim, so
remote-config workflows never ran. Expansion is host-driven (the agent loop
never auto-expands), and the controller's pre-send path did none — matching
the CLI's `buildUserInputMessage`, resolve slash commands via a
controller-owned UserInstructionConfigService that watches the workspace
(including `.cline/remote-config/`), refreshed after each remote-config sync.

Fixes ENG-2036.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(vscode): guard instruction watcher against post-dispose race

Reject in ensureUserInstructionService when the controller is already
disposed so a slash-command resolution that yielded across dispose() can't
resurrect a file watcher that nothing will stop. Also log the post-expansion
length handed to parseMentions. Addresses Greptile review.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-24 14:11:32 +09:00
Max Paulus 🥪 6bc771ba9d include optional deps so that CI passes 2026-06-24 14:11:32 +09:00
Max Paulus 🥪 87338cacd1 fix broken tests 2026-06-24 14:11:32 +09:00
Max Paulus 🥪 827a4a2616 bump sdk version 2026-06-24 14:11:32 +09:00
Max Paulus 🥪 13d6fe3876 add vertex support to extension 2026-06-24 14:11:32 +09:00
Mikołaj Kondratek 82c3d8d771 fix(sdk): make model-not-found API errors actionable in the webview (#11378)
When a provider returns a model-not-found error (e.g. Anthropic's HTTP 404
for a retired model such as claude-3-haiku-20240307), the SDK strips the
status and delivers only the terse body, which collapses to the bare label
"model: <id>". reshapeErrorForWebview fell through to returning that raw
string, so ErrorRow rendered a label-like fragment in red with no hint that
the model is gone or how to recover.

Detect these in the plain-text branch of reshapeErrorForWebview and rewrite
them into a sentence that names the model and tells the user to switch models
in API Configuration settings, then retry. The model switch is framed as a
precondition rather than a parallel option so users don't loop on Retry.

Detection is text-based because the HTTP status is unavailable at this point.
The keyword match is anchored to the word "model" with a not-found signal in
the same sentence, so unrelated errors that merely mention a model (plan
gating, deprecated features) are left untouched. Adds tests for the bare
label form, a generic "does not exist" form, and two negative cases (plan
gating and an auth error mentioning a model) that must pass through unchanged.
2026-06-24 14:11:32 +09:00
Max Paulus 🥪 ee0c63a216 fix telemtry opt flag migration 2026-06-24 14:11:31 +09:00
Dominic Cooney 11e668ca23 fix(vscode): resolve @cline/shared/storage subpath in test compile + vitest
The CommonJS integration-test tsconfig (moduleResolution: node) and the
vitest config did not resolve the @cline/shared/storage exports subpath
imported by src/sdk/SdkController.ts, breaking 'compile-tests' (TS2307)
and 3 vitest SDK suites. Add explicit path/alias mappings to the built
dist so both resolve without changing module emit. Compile-time/test-only;
emitted JS still uses the real package specifier.
2026-06-24 14:11:31 +09:00
Max Paulus 🥪 50a4bfb435 migrate telemetry value in extension 2026-06-24 14:11:31 +09:00
Max Paulus 🥪 2c53d36e6d bump sdk version 2026-06-24 14:11:31 +09:00
Max Paulus 🥪 e4ab360c3c fix claude-code setting loading/persistence 2026-06-24 14:11:31 +09:00
Max Paulus 🥪 5363b2a6d2 fix task history delete 2026-06-24 14:11:31 +09:00
Max Paulus 🥪 51d3865ec5 fix model selector not showing most up to date model in providers.json 2026-06-24 14:11:31 +09:00
Max Paulus 🥪 2a070cddf5 fix ui test 2026-06-24 14:11:31 +09:00
Max Paulus 🥪 401154a42e fix ci checks 2026-06-24 14:11:31 +09:00
Robin Newhouse 3b02d5162e refactor(vscode): remove MCP marketplace ENG-1591 (#11217)
* refactor(vscode): remove MCP marketplace

* test(vscode): clarify MCP marketplace removal test

* docs: update MCP server controls docs
2026-06-24 14:11:31 +09:00
Max Paulus 🥪 710cceab90 fix anthropic provider settings persistence 2026-06-24 14:11:30 +09:00
Max Paulus 🥪 a469377d57 remove baseUrl from providers.json when unchecking box in ui 2026-06-24 14:11:30 +09:00
Max Paulus 🥪 1689abf479 fix ollama and lmtudio settings persistence 2026-06-24 14:11:30 +09:00
Max Paulus 🥪 abd84b80cb fix openrouter apikey persist to providers.json 2026-06-24 14:11:30 +09:00
Max Paulus 🥪 9d4aa03d4d fix vscodelm provider settings persist 2026-06-24 14:11:30 +09:00
Max Paulus 🥪 91077fd49e persist bedrock settings to providers.json 2026-06-24 14:11:30 +09:00
Max Paulus 🥪 6aa4c33918 don't block user input when hasNoUsableProvider == true 2026-06-24 14:11:30 +09:00
Mikołaj Kondratek aa8e11ad47 fix(bedrock): treat profile/IAM/credential-chain auth as a usable provider (#11313)
The inline "Sign in to Cline or add an API key" gate appeared and disabled
chat for Amazon Bedrock users who configured AWS Credentials (access key +
secret), an AWS profile, or relied on the default AWS credential chain, even
though the provider was fully usable (issue #11270).

hasUsableProvider() decided Bedrock usability solely via resolveApiKey(),
which maps bedrock -> awsBedrockApiKey. Bedrock's three non-API-key auth
modes leave that field empty, so buildBedrockProviderConfig() would build a
working session while the gate reported the provider unusable. The Cline
login state is irrelevant here: the gate is computed for the active-mode
provider, and the "Sign in to Cline" button is just one of two generic
remedies, which is what made the symptom look like a logged-out state.

Add a Bedrock branch that classifies usability per auth mode, reusing
resolveBedrockAuthentication() so the gate and the session builder agree on
what each mode means:
- api-key: usable only when awsBedrockApiKey is non-blank (unchanged, now
  also rejects whitespace-only keys)
- profile / iam / default credential chain: usable, deferring credential
  resolution to request time (mirrors buildBedrockProviderConfig and the
  existing keyless-provider philosophy)

Manually verified on a real setup across all four auth modes: pre-fix the
gate blocked chat for access-key and profile auth; post-fix the gate clears
and chat works. API-key mode was never gated incorrectly.

Tests: add Bedrock coverage for every auth mode, including api-key with a
blank and with an unset key (both not usable), the SigV4 repro, profile
(explicit/inferred/awsUseProfile), the bare credential-chain config, and
plan-mode resolution plus plan/act isolation.
2026-06-24 14:11:30 +09:00
Ara ca128cfd9f Fix SDK task size in delete tooltip (#11277)
* fix: show SDK task size in delete tooltip

* fix: address SDK task size review feedback

* fix: simplify SDK task size caching
2026-06-24 14:11:30 +09:00
Max Paulus 🥪 78b1f30133 remove unused code/files
fix broken webview build

remove unused code/files

fix broken webview build
2026-06-24 14:11:30 +09:00
Max Paulus 🥪 1f31738b32 delete unused files 2026-06-24 14:11:29 +09:00
Max Paulus 🥪 6eeee7c4e6 Add edit and regenerate for VS Code chat messages
Allow user feedback messages in the VS Code extension to be edited inline and regenerated from that point. Adds a TaskService RPC, truncates persisted SDK history before the selected visible user prompt, and starts a new session with the edited prompt. Also ensures the regenerated active task appears in extension history while SDK history catches up.
2026-06-24 14:11:29 +09:00
Max Paulus 🥪 e88e0c4994 fix webview-ui tests 2026-06-24 14:11:29 +09:00
Max Paulus 🥪 4258b338df show model list if possible for openai compatible 2026-06-24 14:11:29 +09:00
Max Paulus 🥪 7b24026291 fix onboarding model selection not persisting 2026-06-24 14:11:29 +09:00
Max Paulus 🥪 9a6c972b02 remove provider-specific views and just use genericprovidersettings.tsx 2026-06-24 14:11:29 +09:00
Max Paulus 🥪 fae824bff0 dry up duplicate code and create useProviderModelSelection 2026-06-24 14:11:29 +09:00
Max Paulus 🥪 9011051f76 dry up provider api key logic 2026-06-24 14:11:29 +09:00
Max Paulus 🥪 c4068823a4 dry up some duplicate code 2026-06-24 14:11:29 +09:00
Max Paulus 🥪 323492108d fix onboarding models 2026-06-24 14:11:28 +09:00
Max Paulus 🥪 58b41fbaf5 fix failing biome/lint 2026-06-24 14:11:28 +09:00
Mikołaj Kondratek 3df2d6c780 Remove unused import 2026-06-24 14:11:28 +09:00
Mikołaj Kondratek 60b1ab4d57 fix(sdk): drop dead autoContinue branch in mode rebuild
cf25cd66a ("make extension plan mode more similar to CLI") removed the
file-level ACT_MODE_CONTINUATION_PROMPT constant and stopped passing the
autoContinue / continuationPrompt options when rebuilding a session for
a mode change, but left the corresponding block inside
rebuildSessionForMode in place. The block still references the deleted
constant, so tsc fails on the SDK migration branch with TS2304: Cannot
find name "ACT_MODE_CONTINUATION_PROMPT".

No caller passes options to rebuildSessionForMode anymore, so the block
is dead. Drop the block and narrow the signature to take only newMode.
Existing tests already invoke rebuildSessionForMode(<mode>) with no
second argument and assert that fireAndForgetSend is not called on a
mode rebuild, so they keep passing.
2026-06-24 14:11:28 +09:00
Mikołaj Kondratek 0755b58da1 fix(terminal): capture standalone terminal output on Windows and harden PowerShell command handling (#11133)
* fix(terminal): surface standalone terminal spawn diagnostics

Add Logger calls at every chokepoint of the standalone terminal pipeline
so the (currently silent) failure modes around JetBrains-hosted
cline-core become debuggable from cline-core-service.log.

Lines added, all using the existing Logger facility (no new
dependencies, no behavioral changes):

* StandaloneTerminalProcess.run() now logs:
  - `[StandaloneTerminalProcess] run() entered: shell=… cwd=… args=…`
    on entry, before the try block;
  - `[StandaloneTerminalProcess] spawned pid=… for shell=…` right
    after child_process.spawn returns;
  - `[StandaloneTerminalProcess] close: code=… signal=… fullOutputLen=…`
    inside the `close` handler (the `fullOutputLen` reveals when the
    child exits 0 with empty pipes — the symptom in issue #10948);
  - `[StandaloneTerminalProcess] child error: …` in the `error`
    handler;
  - `[StandaloneTerminalProcess] spawn threw synchronously: …` in
    the outer catch.

* StandaloneTerminalManager.runCommand() now logs entry
  (`[StandaloneTerminalManager] runCommand terminalId=…: <cmd>`) and
  attaches a `.catch` to the previously fire-and-forget
  `process.run(…)` Promise so an unhandled rejection surfaces as
  `[StandaloneTerminalManager] process.run rejected for terminal …`
  instead of disappearing.

* CommandExecutor.execute() extends the existing "Executing command
  in … terminal" line with `mode=<terminalExecutionMode>` and
  `managerCtor=<manager.constructor.name>`, so it's possible to
  confirm whether the `vscodeTerminal` path is in fact backed by a
  `StandaloneTerminalManager` on JetBrains (it is — see
  notes/issue-10948-…md).

* CommandOrchestrator.orchestrateCommandExecution() logs the
  `process.once("completed")` event with `exitCode`/`signal`/
  `terminalType`, the "resolved completed" return branch with the
  line/byte totals, and emits a `WARN` on the silent "still running"
  fall-through. The last one matters because the original repro
  reported "Command executed successfully (exit code 0)" with empty
  output — the WARN makes that branch loud the next time it fires.

These logs are what made the two distinct bugs in #10948 visible
(see the 2026-05-28 update in
notes/issue-10948-terminal-output-investigation-2026-05-27.md). They
stay in to keep the next regression debuggable.

Refs: cline/cline#10948

* fix(terminal): keep Windows child stdio attached to parent pipes

The non-cmd Windows branch in StandaloneTerminalProcess.run() spawned
the shell (powershell.exe in practice) with `detached: true` and no
`windowsHide`. When cline-core is launched by the JetBrains plugin it
has no console of its own, so Windows CreateProcess allocates a NEW
console for the detached child and the child's stdio routes to that
new console instead of the pipe handles the parent created. From the
parent's point of view the pipes immediately EOF, `close` fires with
`code=0`, and `fullOutput` is 0 bytes — exactly the symptom reported
in cline/cline#10948 ("Command executed successfully (exit code 0)"
with no output and no filesystem effect).

This bug applies to every command the agent runs through the
standalone terminal path on Windows, not just the
double-wrapped-PowerShell cases (verified by re-running a clean
`dir <file>` after the diagnostics from the previous commit landed:
`run() entered` and `spawned pid=<num>` both fired, then `close: code=0
fullOutputLen=0`).

Fix:

* `detached: process.platform !== "win32"` — keep the existing
  POSIX behavior (a separate process group helps `tree-kill`), but
  drop it on Windows where `tree-kill` walks the PID tree with
  `taskkill /T` and doesn't need a process group.
* `windowsHide: true` — matches every other `child_process.spawn`
  call site in cline-core (git, MCP, hooks, browser) and flips on
  `CREATE_NO_WINDOW`, keeping the child attached to our pipes
  without popping a console window.

Verified on Windows 11 + IntelliJ IDEA 2026.1 + Cline plugin
1.1.59-Internal: `dir <path>`-style probes now produce a non-zero
`fullOutputLen` in the close log, and the captured output bytes
match what would have been visible interactively. PowerShell
double-wrapping (the other half of #10948) is handled in a
follow-up commit.

Refs: cline/cline#10948

* fix(terminal): harden PowerShell command wrapping for standalone shell

`StandaloneTerminalProcess.getShellArgs()` blindly wrapped every
PowerShell command as `["-Command", command]`. That has two
end-user-visible failure modes on Windows, both observed in
cline/cline#10948:

1. The agent's `run_commands` tool call sometimes arrives already
   prefixed with `powershell -Command "…"`. We then spawned
   `powershell.exe -Command 'powershell -Command "…"'`, and the
   outer shell shredded the inner single/double-quote pairs while
   re-parsing its `-Command` argument. The inner pwsh saw
   quote-empty `Test-Path` calls, fell through to the `else` branch
   and reported "File not found" — to ITS stdout, which the outer
   inherited but the file deletion the LLM intended never ran.
2. The user's `$PROFILE` script ran on every spawn, leaking
   non-deterministic noise (e.g.
   `%windir%\System32\REG.exe : The module '%windir%' could not be
   loaded`) into the captured output and confusing the agent.
3. Bonus: the POSIX branch used `["-l", "-c", command]`. The `-l`
   re-sources login files on every command, which is slow and lets
   greeter scripts leak into output.
4. Bonus: the cmd branch used `["/c", command]`. `/d` skips
   AutoRun, `/s` makes the embedded-quote handling deterministic.

Fix:

* PowerShell branch returns
  `["-NoProfile", "-NonInteractive", "-Command", unwrap(command)]`.
  `-NoProfile` suppresses (1) the spurious profile noise that
  contaminated the captured output, and `-NonInteractive` ensures
  the child doesn't deadlock waiting on a prompt no one will answer.
* `unwrapPowerShell(command)` strips a leading
  `powershell|pwsh [.exe] -Command|-c "…"` (or single-quoted)
  wrapper that the LLM sometimes emits, fixing the double-pass
  argument-quoting destruction. If the command does not match the
  exact wrapper shape it is returned verbatim — worst case is "no
  change", preserving pre-fix behavior.
* cmd branch returns `["/d", "/s", "/c", command]`, matching the
  canonical helper in cline/sdk/packages/shared/src/parse/shell.ts.
* POSIX branch returns `["-c", command]`, dropping the unhelpful
  `-l`. Also matches the SDK helper.

Verified on Windows 11 + IntelliJ IDEA 2026.1 + Cline plugin
1.1.59-Internal in combination with the previous "keep Windows
child stdio attached" commit: `Remove-Item CHANGELOG.md` now
deletes the file, the agent's verification `Get-ChildItem CHANGELOG*`
returns nothing, and the profile-load REG.exe error no longer leaks
into captured output.

Refs: cline/cline#10948

* refactor(terminal): tone down standalone terminal diagnostics

The diagnostics added while chasing #10948 were intentionally loud so the
two bugs were visible. Now that the fixes are in, reduce them to a normal
operating posture:

* Demote fine-grained traces to `debug`: the per-spawn `spawning …` and
  `spawned pid=…` lines, `StandaloneTerminalManager.runCommand`, and the
  orchestrator's `resolved completed` summary.
* Drop the orchestrator's `completed event` line entirely — the
  `resolved completed` debug line already carries exit code, signal, and
  line/byte totals.
* Stop echoing the full command in the manager line and stop echoing the
  args vector in the spawn line. The command is still logged once at
  `info` by CommandExecutor (unchanged, pre-existing), so we go back from
  three command echoes to one. Commands routinely embed secrets
  (Authorization headers, tokens), so fewer copies on disk is better.

Kept loud on purpose:

* `info` on `close: code=… fullOutputLen=…` — the single line that proves
  the Windows stdio-capture fix and the most useful per-command signal.
* `warn` on `resolved without completion event` — the silent-success
  canary for the #10948 failure mode.
* `error` on child error / synchronous spawn failure / unhandled
  process.run rejection.

Refs: cline/cline#10948

* fix(terminal): tighten PowerShell unwrap regex and extract to a pure module

Two review follow-ups for the #10948 shell-arg handling:

1. The wrapper-strip regex used a greedy `([\s\S]*)` body, so a command
   like `powershell -Command "foo" "bar"` would match with the body
   captured as `foo" "bar`, silently rewriting a command into something
   different. Replace the body with a tempered match `((?:(?!\1).)*)`
   that cannot contain the captured delimiter, so anything other than
   exactly one quoted token is returned verbatim. Worst case is now
   "no change" rather than an incorrect rewrite. The legitimate
   double-wrapped case from #10948 (outer ", inner ') still unwraps.

2. `getShellArgs` and `unwrapPowerShell` were private methods on
   StandaloneTerminalProcess, untestable without spawning a process.
   Move them to a pure `shellArgs.ts` module. `getShellArgs` now takes
   an injectable `platform` (defaulting to `process.platform`) purely so
   the win32-vs-posix branch is testable; behavior is unchanged. This
   also gives us a single local seam to later consolidate onto the
   canonical `@cline/shared` helper (tracked as a follow-up).

No behavioral change beyond the regex correctness fix.

Refs: cline/cline#10948

* test(terminal): cover shell-arg construction and PowerShell unwrap

Add mocha unit tests (matching the repo's node:assert/strict + __tests__/
convention so the existing mocharc spec globs pick them up) for the newly
extracted shellArgs module:

* unwrapPowerShell: double-quote and single-quote wrappers, powershell.exe
  -c form, the #10948 nested-quote repro (inner quotes preserved),
  non-wrapped passthrough, and the two regressions the tightened regex
  must reject (`… "foo" "bar"` and a command that merely mentions
  powershell mid-string).
* getShellArgs: PowerShell -> -NoProfile -NonInteractive -Command (with
  unwrap), cmd -> /d /s /c, POSIX -> -c. The injectable platform arg lets
  these run on any CI host.

This closes the M1 review finding (the regex was the riskiest line in the
change and had zero coverage) and exercises the cmd/POSIX flag changes
called out in M2.

Refs: cline/cline#10948

* docs(terminal): drop issue references and clarify windowsHide comment

Remove inline issue-number references from source comments and a test
name; that context belongs in the commit history, not the code. Also add
a one-line note that windowsHide is a no-op on non-Windows platforms,
since it is set unconditionally while the surrounding comment is
Windows-specific.

No behavior change.

* refactor(terminal): drop warn on the non-completion return path

The orchestrator's final fall-through return is a normal, expected path:
the process resolved via `continue` without a `completed` event (e.g. a
terminal mode without shell integration, or proceed-while-running flows).
Logging it at `warn` cries wolf on healthy runs, so remove it. The
genuine failure mode this was meant to catch surfaces through the
`close`/error logs and the result string itself.

* fix(terminal): address review feedback on standalone spawn paths

Three follow-ups from code review:

* StandaloneTerminalManager.runCommand: the unawaited process.run()
  .catch only logged. run() emits "error" for failures it catches, but a
  rejection escaping without an "error" event would leave the outer
  promise (resolved via the "continue"/"error" events) pending forever,
  stalling the caller. Re-emit "error" from the catch so both paths stay
  consistent. Cannot trigger today (no await outside run()'s try/catch)
  but the guard exists precisely for future rejections.

* shellArgs POSIX branch: document that dropping the login flag (`-l`)
  is intentional and relies on the child inheriting the parent's PATH via
  process.env, with a note that a GUI-launched IDE without a login PATH
  is the edge case to watch.

* StandaloneTerminalProcess cmd.exe branch: add windowsHide:true. The
  console-allocation/window-pop problem is not exclusive to the non-cmd
  branch; a console-less parent could pop a window for cmd.exe too.
  No-op on non-Windows.
2026-06-24 14:11:28 +09:00
Ara 9c030a93b6 Remove Explain Changes feature (#11278)
* chore(vscode): remove explain changes entry points

* chore(vscode): remove explain changes feature
2026-06-24 14:11:28 +09:00
Max Paulus 🥪 f9fcf7f5ce make extension plan mode more similar to CLI
- basically, don't auto continue when agent switches to act mode
2026-06-24 14:11:28 +09:00
Max Paulus 🥪 c4cf2743d6 fix zai insufficient credits issue 2026-06-24 14:11:28 +09:00
Max Paulus 🥪 1dbfb50038 fix tool use name sanitization 2026-06-24 14:11:28 +09:00
Max Paulus 🥪 9b548347ef fix broken tsc 2026-06-24 14:11:28 +09:00
Dominic Cooney 2dc5791d46 fix(vscode): exclude vitest src/sdk suites from CommonJS test compile
compile-tests runs 'tsc -p tsconfig.test.json' (module: commonjs) over all
src/**/*.test.ts for the VS Code integration runner. The new src/sdk vitest
suites use top-level 'await import(...)' (after vi.mock), which is invalid
under CommonJS and fails with TS1378. The integration runner never runs
src/sdk anyway (.vscode-test.mjs only globs core/test/utils/shared/
integrations/hosts/services); these run via 'npm run test:vitest'. Exclude
src/sdk/**/*.test.ts from the integration compile.
2026-06-24 14:11:27 +09:00
Dominic Cooney 35e3bb4787 fix(vscode): restore biome --config-path so lint resolves apps/vscode/biome.jsonc
The rebase dropped '--config-path ./biome.jsonc' from the lint/format/
postprotos scripts and removed the '!!**/.vscode-test' ignore from
biome.jsonc. Without the explicit config path, biome auto-discovered the
root biome.json instead of apps/vscode/biome.jsonc, applying the wrong
rule severities (449 errors at error level for rules that are off/info in
the nested config). Restore both to match origin/main and apply the
pending buf format fix to models.proto.
2026-06-24 14:11:27 +09:00
Dominic Cooney 205a539491 fix(vscode): show running state for in-progress commands
The command row reflects an executing state while a command runs. The
message translator includes the command-output marker on the running
command row so the webview renders it as executing; the row is finalized
with output and a completed flag when the command ends.

Also remove the unused onChange parameter from the foreground run_commands
path: the SDK runtime does not pass it, so it had no effect. Foreground
command output is surfaced to the chat at completion, not incrementally.

Fixes CLINE-2298 and CLINE-2162
2026-06-24 14:11:27 +09:00
Dominic Cooney 0175548dd8 fix(vscode): re-enable approval buttons for consecutive asks
The footer Approve/Reject buttons stayed disabled when a second consecutive
approval ask arrived. The button configs are shared singletons (e.g.
BUTTON_CONFIGS.tool_approve), so two identical asks return the same object
reference and the effect that reset the processing latch never re-ran.

Key the processing latch on the ask identity (anchored turn timestamp plus the
button labels) rather than the config object reference, using a ref-based latch
so each new ask re-enables the buttons. Adds a regression test.

Test plan:

1. Ask the agent to generate two requests to ls /tmp at once

2. Approve (or reject) the first request

3. Check that the buttons for the second request are enabled
2026-06-24 14:11:27 +09:00
Dominic Cooney 6acc231da5 feat(vscode): add the VS Code Language Model (vscode-lm) provider
Run Cline inference through the VS Code Language Model API (vscode.lm), enabling
models contributed by any extension that registers a language model chat
provider with VS Code. GitHub Copilot is the most common such vendor, but the
implementation is vendor-agnostic — it selects models via
vscode.lm.selectChatModels and has no Copilot-specific logic.

- VsCodeLmHandler implements the Cline SDK ApiHandler and is registered with the
  SDK handler registry; the model selector travels as a vendor/family[/version/id]
  string in modelId and is parsed back here. Selector segments are
  percent-encoded so values containing slashes round-trip intact.
- Native tool calling: tool definitions are passed to sendRequest and tool calls
  are surfaced as tool-call chunks; tool results round-trip as
  LanguageModelToolResultPart, with structured tool output serialized to text and
  a trailing user message appended when a turn ends on tool results so models can
  read the output.
- Gated to VS Code: registration is conditioned on the vscode.lm API being
  present, and the provider is hidden in the UI on hosts without it (JetBrains).

Depends on @cline/{shared,llms,agents,core} 0.0.42-nightly.1780514867, the first
published SDK build with the custom-registered-handler routing this provider
needs.
2026-06-24 14:11:27 +09:00
Ara d2ac39455b Fix approval chat replies rendering as tool errors (#11246)
* fix(vscode): route approval chat replies as user feedback

* fix(vscode): suppress approval reply denial errors

* fix(vscode): hide rejected approval tool failures

* chore(vscode): clarify denied approval suppression helper
2026-06-24 14:11:27 +09:00
Max Paulus 🥪 87543e6f47 Persist OpenRouter provider config via catalog hook 2026-06-24 14:11:27 +09:00
Max Paulus 🥪 3757a8fd0d persist openai-codex provider model settings
- also don't show a sign in button if openai-codex is the only provider
2026-06-24 14:11:27 +09:00
Max Paulus 🥪 0e9a5c0fc1 Persist Cline model selections to provider config 2026-06-24 14:11:27 +09:00
Dominic Cooney 8aba515d6b fix(vscode): declare missing direct deps @grpc/proto-loader and @opentelemetry/api-logs
Both packages are imported directly from source but were never declared in
apps/vscode/package.json, so they only resolved transitively. On a clean
install this broke:

- @grpc/proto-loader — imported by scripts/proto-utils.mjs,
  src/standalone/utils.ts and src/standalone/hostbridge-client.ts; its absence
  made `npm run protos` (and therefore the whole build) fail on a fresh checkout.
- @opentelemetry/api-logs — imported by the OpenTelemetry telemetry providers;
  its absence produced TS2307 "Cannot find module" errors under tsc.

Versions are pinned to align with the existing dependency families already
declared in this package (@grpc/grpc-js ^1.9.x → proto-loader ^0.7.13;
the @opentelemetry/* 0.56.x line → api-logs ^0.56.0). The npm and bun
lockfiles are updated accordingly (the api-logs change also dedupes several
previously-nested copies to a single hoisted entry).
2026-06-24 14:11:27 +09:00
Max Paulus 🥪 a2194f4908 show legacy task history that is not saved in the ~/.cline folder 2026-06-24 14:11:26 +09:00
Max Paulus 🥪 f5d0b4fd49 add migration telemetry 2026-06-24 14:11:26 +09:00
Ara 61ea8688c6 fix(vscode): reuse approved tool rows (#11213)
* fix(vscode): reuse approved tool rows

* fix(vscode): clear stale approved tool rows
2026-06-24 14:11:26 +09:00
Dominic Cooney 94f5a47a59 sdk migration: squashed pre-2026-06-02 work
Omnibus squash of the 10 oldest SDK-migration commits (authored 2026-05-27
through 2026-06-02), collapsed during the 2026-06-09 rebase onto origin/main.

Squashed commits:
- sdk migration: squashed pre-2026-05-27 work
- sdk migration: squashed 06-05-2026 -- instead of listHistory, use host.get(sessionId) instead
- updat gitignore
- fix xai provider
- fix(vscode): forward Bedrock region + AWS auth to the SDK gateway
- fix(vscode): keep in-progress MCP OAuth flow across reconnects
- fix(vscode): wire auto compact into SDK sessions (#11197)
- fix(vscode): compact Codex OAuth before input cap (#11194)
- fix unauthed user flow
- fix(llms): strip Cerebras reasoning history (#11214)
2026-06-24 14:11:26 +09:00
John Choi 864419d8b0 Fix ClinePass onboarding model grouping (#11768) 2026-06-23 17:38:30 -07:00
Tomás Barreiro 218db38544 Add to the status bar [ENG-2216] (#11766)
* Add  to the status bar

* Update tests
2026-06-24 01:24:46 +02:00
Bee a3b3295461 fix(llms): includes session id for OpenRouter caching (#11744)
* Improve OpenRouter sticky session routing

OpenRouter prompt caching was getting weaker cache hit rates because requests did not include a stable session_id. Without that identifier, OpenRouter can route consecutive turns away from the same upstream cache context even when cache_control is present, causing more fresh input billing.

Propagate explicit runtime sessionId metadata from agents into model requests without synthesizing fallback session or conversation ids. Add provider metadata for sticky sessions so OpenRouter maps metadata.sessionId to the JSON body session_id field, while keeping the mechanism extensible for header-based providers.

Apply sticky-session metadata in the AI SDK provider fetch wrapper, preserving explicit wire values when callers already set them. Move trimNonEmpty and omitUndefinedValues into @cline/shared for reuse, and cover the JSON-body, header, and no-fallback paths with tests.

* Tighten sticky session fetch body handling

Treat init.body null as an explicit no-body override so the sticky-session wrapper does not inspect or parse a Request body that the caller has intentionally overridden.

Model fetch body text by source, which removes the unreachable JSON-body injection branch and makes the Request rewrite path explicit. Also type the provider fetch mocks so sticky-session assertions compile cleanly in editors.

* merges model-handle default metadata with per-request metadata
2026-06-23 12:07:17 -07:00
Max d09270940f fix(hub): validate dashboard websocket host and origin (#11724)
* fix(hub): validate browser websocket origins

* test(hub): cover websocket host and origin gates

* fix(hub): allow explicit bind host for dashboard socket

* fix(hub): default protect browser routes

* fix(hub): normalize default port origins

---------

Co-authored-by: Max Paulus 🥪 <max@cline.bot>
2026-06-23 10:14:25 -07:00
Saoud Rizwan 23c9bf1c05 fix: prevent 'ERROR: EMPTY CONTENT' message when error occurs (#11726)
* fix: prevent empty assistant history replay

* refactor: move tool call filtering after empty guard
2026-06-23 09:33:10 -07:00
Bee 52531d935a refactor: add connector configure path and share catalog to shared pkg (#11730)
* feat(hub): add connector configure path and share catalog via @cline/shared

Add connector.channels/configure/delete_config Hub commands that persist
connector settings to disk without starting connector processes or calling
provider auth APIs. This means settings (including tokens) are saved as-is
without verification; callers are responsible for supplying valid values.

Shared catalog and platform definitions:
- Move ConnectorCatalogEntry, CONNECTOR_CATALOG, listConnectorCatalog, and
  all ConnectorPlatformDef/FieldDef/SecurityDef types + CONNECTOR_PLATFORMS
  into sdk/packages/shared/src/connectors/platforms.ts
- Export everything from @cline/shared index so CLI and Hub use the same
  definitions without duplication
- Reduce apps/cli/src/connectors/catalog.ts and
  apps/cli/src/wizards/connect/platforms.ts to thin re-export shims that
  preserve existing CLI import paths

New Hub connector handlers (sdk/packages/core):
- connector-handlers.ts: handles connector.channels (list available/active/
  configured), connector.configure (validate fields and write settings.json
  under ~/.cline/data/connectors/), connector.delete_config (remove entry
  and clean up empty file)
- Settings are stored as ConnectorSettingsFile (version 1) at
  ~/.cline/data/connectors/settings.json; reads are lenient/defensive
- Wire handlers into hub-server-transport.ts dispatch switch
- connector-handlers.test.ts: unit tests for configure, channels, and
  delete_config covering field validation, conditional fields, security
  constraints, and settings round-trips

Hub WebSocket auth helpers (hub-websocket-server.ts):
- Extract isLocalHubHostName / isLocalHubOrigin as named, tested exports
- Allow unauthenticated WebSocket upgrades from local origins (localhost,
  127.0.0.1, ::1) so the Hub UI can connect without an auth token
- hub-websocket-server.test.ts: extend tests to cover new local-origin logic

Add connector.channels, connector.configure, connector.delete_config to
HubCommandName union in sdk/packages/shared/src/hub.ts

* apply feedback

* export connector settings json path

* apply feedback from robin
2026-06-22 22:25:49 -07:00
Tomás Barreiro 19d4248381 Add Organization error messages to the Cline CLI (#11737)
* Add Organization error messages to the Cline CLI

* Fix tests

* Fix tests

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-06-22 17:18:29 -07:00
Tomás Barreiro b2b29678a2 Add a button to unselect an org on ClinePass error (#11738)
* Add a button to unselect an org on ClinePass error

* Fix comments
2026-06-23 01:47:15 +02:00
Bee ad30714608 fix(hub-ui): Use development mode conditionally for connector (#11728)
* fix(hub-ui): Use development mode conditionally for connector

Refactor connector CLI launch logic to conditionally apply Bun-specific
flags (`--conditions=development`) only when running under Bun or Node
runtimes with access to the source entrypoint. When running from a
compiled binary (e.g., packaged Cline app), use the execPath directly
without Bun flags.

- Extract `buildCliConnectCommand` function to encapsulate launcher
  and args resolution logic based on runtime detection
- Use `withResolvedClineBuildEnv` for environment variable setup
- Export `__test__` object to enable unit testing of internal logic
- Add tests covering Bun source entrypoint and compiled binary cases

* address feedback
2026-06-22 16:41:47 -07:00
Bee 9eebc44f24 chore: Optimize Cline Hub webview bundle (#11731)
* Optimize Cline Hub webview bundle

Issue:

The Cline Hub webview build emitted Vite's large chunk warning. The main entry bundle was over 2 MB minified, and the build output included many Shiki language/theme chunks plus eagerly bundled Streamdown math/Mermaid support that the hub did not need on startup.

Changes:

- Split heavyweight Cline Hub views behind React.lazy/Suspense so settings, customization, and chat code do not all land in the initial app shell.

- Replace root Shiki highlighter usage with shiki/core and lazily loaded, explicitly supported languages/themes.

- Route Streamdown through a local HubStreamdown wrapper that uses the local CodeBlock renderer and keeps Mermaid as a lazy diagram plugin.

- Remove unused Streamdown math/code/Mermaid packages, add direct lazy Mermaid support, and add targeted Rolldown chunk groups for Mermaid parser/layout/markup assets.

Before/after bundle measurements:

| Metric | Before | After |

| --- | ---: | ---: |

| Total generated JS | 24,356 KiB | 5,800 KiB |

| Total generated JS gzip | 4,805.5 KiB | 1,434.6 KiB |

| Main entry chunk | 2,044.02 kB | 373.88 kB |

| Main entry gzip | 617.25 kB | 117.83 kB |

Verification:

- bun -F @cline/cline-hub build:webview

- bun biome check apps/cline-hub/src/webview/vite.config.ts apps/cline-hub/src/webview/src/App.tsx apps/cline-hub/src/webview/src/components/ai-elements/code-block.tsx apps/cline-hub/src/webview/src/components/ai-elements/message.tsx apps/cline-hub/src/webview/src/components/ai-elements/reasoning.tsx apps/cline-hub/src/webview/src/components/ai-elements/streamdown.tsx apps/cline-hub/src/webview/package.json

* address feedback
2026-06-22 16:40:01 -07:00
Tomás Barreiro 6ddf48d227 Forcefully send users to their personal dashboard when going to subscribe (#11725)
* Forcefully send users to their personal dashboard when going to subscribe

* fix trailing slash
2026-06-22 22:27:31 +02:00
Saoud Rizwan ee59f81706 chore(cli): release v3.0.29 2026-06-20 00:30:51 -07:00
Saoud Rizwan 92f6e28f13 fix(cli): hide costs for Cline free models (#11686)
* fix(cli): hide Cline free model costs

* fix(cli): harden free model cost lookup
2026-06-20 00:20:07 -07:00
Saoud Rizwan 27a78f0248 chore(sdk): release v0.0.51 2026-06-19 19:15:00 -07:00
Saoud Rizwan 2c4980f42a fix(sdk): resolve Cline Z.ai model metadata aliases (#11685)
* fix(sdk): resolve Cline Z.ai model metadata aliases

* fix(sdk): preserve Cline model alias overrides

* test(sdk): update Cline provider model list expectation
2026-06-19 19:05:47 -07:00
Saoud Rizwan 64c5e48edb Revert "fix(cli): resolve Cline model display names by just model name (#11668)" (#11684)
This reverts commit c497698beb.
2026-06-19 18:27:29 -07:00
Saoud Rizwan 3c23f80a94 fix(sdk): deflake run_commands timeout telemetry tests 2026-06-19 14:59:46 -07:00
Saoud Rizwan 4be362892f chore(cli): release v3.0.28 2026-06-19 14:52:50 -07:00
Saoud Rizwan cdff084652 chore(sdk): release v0.0.50 2026-06-19 14:11:25 -07:00
Robin Newhouse 299a4a9520 fix(sdk): harden parallel tool guidance (#11598)
Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-06-19 12:25:20 -07:00
Saoud Rizwan c497698beb fix(cli): resolve Cline model display names by just model name (#11668)
* fix(cli): resolve Cline model display names by slug

* fix(cli): type model display metadata

* test(cli): cover model display fallback paths
2026-06-19 12:21:57 -07:00
Ara 292133989b fix(sdk): cap assistant text in provider messages (#11478) 2026-06-19 11:41:04 -07:00
Ara f5aa035a6a fix(sdk): default-on tool result truncation, tightened limits + budget accounting in MessageBuilder (#11475)
* fix(sdk): default-on tool result truncation, name fallback, tool_use budget accounting

- Truncate every tool result (MCP/custom tools included), not just an allowlist
- Resolve tool names from tool_result.name when the paired tool_use is gone
- Count tool_use.input strings toward the aggregate provider request budget
- Protect any binary carrier block ({type, data}) from truncation, not just images
- Don't let failSession cleanup errors mask the original turn error

* fix(sdk): tighten MessageBuilder limits with named options and env overrides

Folds #11474 into the default-on truncation branch and addresses review
feedback from both PRs:

- per-result tool cap drops to 8k chars; MessageBuilderOptions constructor
  with CLINE_MESSAGE_BUILDER_* env overrides for A/B testing
- env parsing is positive-only so '=0' cannot silently disable a limit
  (greptile on #11474)
- aggregate budget stays at 6MB: budget truncation rewrites mid-transcript
  bytes and breaks provider prefix caching, so it must stay a rare overflow
  valve rather than the steady state (johnwschoi on #11474)
- user file attachments get a dedicated 50k cap instead of inheriting the
  aggressive tool-result cap (codex on #11474)
- isBinaryContentLike restricted to known binary block types so textual
  {type, data} payloads can no longer dodge every cap (codex/greptile)
- tool_use.input strings become last-resort budget truncation candidates,
  making the aggregate budget reclaimable when oversized model-generated
  arguments carry the overflow (robinnewhouse/greptile)

* fix(sdk): constrain binary tool result truncation

* style(sdk): remove disallowed comment formatting

* Revert "style(sdk): remove disallowed comment formatting"

This reverts commit 9d8b66726d.

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-06-19 11:15:42 -07:00
Saoud Rizwan bbcae25542 fix(cli): refine marketplace primitive rows (#11659) 2026-06-19 10:57:37 -07:00
Tomás Barreiro 9b914912ac Rename Cline Pass to ClinePass (#11657) 2026-06-19 18:36:07 +02:00
Saoud Rizwan 8c369eeed9 docs(vscode): remove marketplace readme language links (#11658) 2026-06-18 18:09:02 -07:00
Saoud Rizwan 85223a07cf fix(cli): apply auto-approve toggles immediately (#11653) 2026-06-18 15:58:31 -07:00
John Choi eb2687677c feat(onboarding): add Cline Pass to the signup flow (#11609)
* Add Cline Pass provider to VS Code extension

* Fix the model picker

* ClinePass specific options

* Add feature flag to the api options

* Extend flag usage

* fix type error

* Use the right model info

* Fix tests

* Hide price info for clinePass models

* feat(onboarding): add Cline Pass as optional user-type in signup flow

Surface Cline Pass as a recommended-but-optional onboarding choice gated
behind the ext-cline-pass feature flag, alongside Free / Frontier / BYOK.
When selected, signup provisions the cline-pass provider and ClinePass
model fields; price info is hidden since cost is covered by the
subscription. Falls back cleanly to the existing flow when the flag is
off. Adds unit tests for the new helpers.

* feat(onboarding): nudge + Cline Pass subscribe link in signup flow

Strengthen the Cline Pass onboarding option with a 'Recommended' copy
nudge (text + ordering, no badge/paid-default), and add an additive
subscribe affordance on the post-signup 'Almost there!' step that links
to {appBaseUrl}/dashboard/plan. Client-only: reuses existing appBaseUrl
from useClineAuth and the existing dashboard subscribe page; no backend
change. Shown only when the user selects Cline Pass; existing flow and
other user-types are unchanged.

* refactor(onboarding): redirect Cline Pass signup to subscription page

Replace the manual 'Get Cline Pass' callout with an automatic redirect:
after a Cline Pass user completes account creation, open the dashboard
subscription signup page (/onboarding/individual-plan) in the browser.
Keep the free option first and default-selected, with Cline Pass shown
second and labeled '(Recommended)'.

* chore(onboarding): tighten inline comments

* fix(onboarding): constrain Cline Pass selection and clear stale subscribe redirect

Addresses review findings:
- Never save a non-Cline-Pass model id under the cline-pass provider:
  Cline Pass selection no longer falls back to a free model when the
  Cline Pass list is empty, the generic model search is hidden for Cline
  Pass, finishOnboarding guards on a cline-pass/ id, and the CTA is
  disabled (with an empty state) when no Cline Pass model is available.
- Clear the pending subscription redirect when the user backs out, signs
  in, or navigates away from Cline Pass, and re-check userType in the
  redirect effect so a late auth update can't force the paid flow.

* fix(onboarding): add userType to handleFooterAction deps

The signup handler reads userType (to set the pending Cline Pass
subscribe flag); make that dependency explicit rather than relying on
the transitive finishOnboarding dependency.

* fix(onboarding): gate Cline Pass model data by feature flag

* fix(onboarding): address Greptile review on Cline Pass signup

- Log when the Cline Pass provider write is skipped due to an unexpected
  (non cline-pass/) model id, so the otherwise-silent no-op is observable.
- Open the subscription page for already-authenticated users by invoking
  the redirect helper directly after accountLoginClicked resolves, not
  only via the auth effect (which never re-runs when clineUser is
  unchanged).

* fix(onboarding): preserve ClinePass selection through login + one-word branding

- handleAuthCallback no longer forces the provider back to 'cline' on
  login when the user picked Cline Pass during onboarding; it preserves a
  'cline-pass' selection per mode. Only 'cline-pass' configs are affected
  (which require the ext-cline-pass flag), so all other logins are
  unchanged.
- Open the subscription page solely from the clineUser auth effect; the
  signup action no longer also calls it directly (prevents the login
  browser and subscribe page opening at once / dead-ends for already-
  authenticated users). Signup flow is otherwise untouched.
- Rename user-facing 'Cline Pass' to one-word 'ClinePass' (card titles,
  step title, empty state) and the model group label to 'clinepass'.

* chore(onboarding): normalize ClinePass branding in comments, trim verbose comments

* fix(merge): resolve ApiOptions redeclare + apply biome semicolon formatting

- Remove duplicate CLINE_PASS_FEATURE_FLAG const in ApiOptions.tsx (the
  shared import from constants/featureFlags supersedes main's local const).
- Apply biome format (semicolons) to onboarding/controller files so they
  match main's current style and pass Quality Checks.

* fix: restore clean no-semicolon formatting, keep only real ClinePass changes

The previous merge-recovery commit ran a local biome that incorrectly added
semicolons across entire files, bloating the diff by ~2000 LOC. Restore the
files to their clean pre-format state (matching main's asNeeded style) so the
diff reflects only the actual ClinePass onboarding changes (~420 LOC). Keeps
the handleAuthCallback provider-preservation fix and the ApiOptions duplicate
const removal.

* chore(onboarding): trim redundant inline comments

* chore: match main in refreshClineRecommendedModels (drop snake_case cline_pass handling)

* chore: trim featureFlags.ts comment

* fix(onboarding): open ClinePass subscribe page from App, not OnboardingView

handleAuthCallback marks the welcome view completed (unmounting OnboardingView)
before it pushes the auth-status update that sets clineUser. The redirect effect
lived in OnboardingView, so the pending-subscribe intent was lost on unmount and
the subscription page never opened for new ClinePass users.

Move the pending intent to a module-level store (clinePassSubscribe.ts) and run
the redirect from an effect in App, which outlives the onboarding unmount.

---------

Co-authored-by: BarreiroT <tomasmbarreiroi@gmail.com>
2026-06-18 14:45:34 -07:00
Tomás Barreiro 6fe5acb2a4 Resolve feature flags using the user id on startup (#11652) 2026-06-18 23:24:42 +02:00
Tomás Barreiro 99fb5101e5 Improve ClinePass error handling (#11637)
* Make sections expandable and add ClinePass

* fix label

* Map clinePass models to clinePass and the rest to cline

* gate the models behind the feature flag

* Do not allow custom model on cline pass

* Do not show the count on the mode list

* fix clinepass model list

* fix focus when expanding a section

* update the model data when the provider doesn't match

* Add ClinePass to the onboarding screen

* Fix the auth flow not starting

* Hide option behind a feature flag

* Update icon

* Remove unrelated changes

* Hide the custom model id

* fix model names

* remove custom model id option

* fix tests

* Fix names

* Display the clinepass models in the onboarding

* Throw a specific error when the user isn't subscribed

* properly render the error message

* fix error detection

* refactor

* Update the ResponseErrorHandler type

* re-add trailing slash
2026-06-18 22:18:12 +02:00
Tomás Barreiro 58db9887ee Clear organization on ClinePass selection on the VSCode extension (#11648)
* Clear organization on ClinePass selection on the VSCode extension

* Disable remote config if ClinePass is selected

* Disable remote config if ClinePass is selected

* Revert formatting changes

* fix issue
2026-06-18 22:04:49 +02:00
Ahmad Shahzad b16a28cd6f feat(vscode): add Fireworks GLM 5.2 and Kimi K2.6 Fast, fix DeepSeek V4 Flash cache-read price (#11642)
Update the Fireworks model registry in apps/vscode/src/shared/api.ts:

- Add accounts/fireworks/models/glm-5p2: new general-purpose model with
  a 1,048,576-token context window (131,072 output).
- Add accounts/fireworks/routers/kimi-k2p6-fast: standardizes the
  Kimi K2.6 router on the `-fast` naming convention used for /routers.
  The existing kimi-k2p6-turbo entry is retained for now to avoid
  breaking user workflows; the turbo variant is expected to be removed
  in a future release once the fast variant is the only one served.
- Correct the cache-read price for accounts/fireworks/models/deepseek-v4-flash
  from 0.03 to 0.028 to match the published rate.

Pricing sourced from https://docs.fireworks.ai/serverless/pricing.
2026-06-18 21:18:01 +02:00
John Choi 114aa3f161 Add friendly Cline Pass entitlement error UI (#11606)
* Add Cline Pass provider to VS Code extension

* Fix the model picker

* ClinePass specific options

* Add feature flag to the api options

* Extend flag usage

* fix type error

* Use the right model info

* Fix tests

* Hide price info for clinePass models

* Add friendly Cline Pass entitlement error UI

When a Cline Pass model returns a 403 ENTITLEMENT_ERROR (user not subscribed to the required model plan), the chat dumped the raw serialized error JSON and pointlessly auto-retried it.

- Add ClineErrorType.Entitlement, classified before the generic 403/auth path.
- Skip auto-retry (and the retries-exhausted message) for entitlement errors.
- Render a dedicated EntitlementError card: friendly headline, env-aware 'Get Cline Pass' subscribe link (clineUser.appBaseUrl, falling back to production), and a Retry button; backend detail is shown as muted support text.
- Add unit/component tests and a Storybook story.

* Scope entitlement to individual case; fix path-prefixed subscribe URL

- ClineError: only classify the individual 'not subscribed' ENTITLEMENT_ERROR as Entitlement (checks message and details.message). Org-account variant falls through to generic handling rather than showing a misleading 'Get Cline Pass' card.

- EntitlementError: build the subscribe URL with a relative path against a trailing-slash-normalized base so path-prefixed self-hosted/proxy app URLs (e.g. https://proxy.example.com/cline/app) are preserved instead of resetting to origin.

- Tests: add org-exclusion case, path-prefixed URL case, and assert the retry askResponse payload (yesButtonClicked).

* Trim redundant inline comments

* Add provider test: pre-stream 403 entitlement error classifies correctly

Documents and locks in that a 403 ENTITLEMENT_ERROR (which rejects completions.create before streaming, as an OpenAI SDK APIError) propagates through ClineHandler with the code intact so ClineError classifies it as Entitlement. Confirms the error does not rely on the mid-stream chunk.error path.

* Guard subscribe URL build against malformed appBaseUrl

Wrap new URL() in try/catch so an invalid appBaseUrl from the auth context omits the subscribe link instead of throwing TypeError and crashing the EntitlementError card mid-render. Addresses Greptile review feedback; adds a malformed-URL test.

* test: trim entitlement error UI coverage

* Use one-word 'ClinePass' in user-facing copy

Renames the display text in the entitlement card (headline, helper, button) and related tests/story/comments from 'Cline Pass' to 'ClinePass'. Also normalizes EntitlementError.test.tsx formatting to the webview Biome style.

* fix: skip subagent retries for ClinePass entitlement errors

---------

Co-authored-by: BarreiroT <tomasmbarreiroi@gmail.com>
Co-authored-by: Tomás Barreiro <52393857+BarreiroT@users.noreply.github.com>
2026-06-18 12:06:40 -07:00
Tomás Barreiro 64743d2911 Improve the ClinePass UX (#11644) 2026-06-18 20:39:52 +02:00
Tomás Barreiro d7292993e9 Add Cline Pass to the extension (#11556)
* Add Cline Pass provider to VS Code extension

* Fix the model picker

* ClinePass specific options

* Add feature flag to the api options

* Extend flag usage

* fix type error

* Use the right model info

* Fix tests

* Hide price info for clinePass models

* refactor

* remove redundancy

* fix syncModeConfigurations
2026-06-18 20:19:46 +02:00
Tomás Barreiro 8110cc46d8 Add the ClinePass onboarding flow (#11590)
* Make sections expandable and add ClinePass

* fix label

* Map clinePass models to clinePass and the rest to cline

* gate the models behind the feature flag

* Do not allow custom model on cline pass

* Do not show the count on the mode list

* fix clinepass model list

* fix focus when expanding a section

* update the model data when the provider doesn't match

* Add ClinePass to the onboarding screen

* Fix the auth flow not starting

* Hide option behind a feature flag

* Update icon

* Remove unrelated changes

* Hide the custom model id

* fix model names

* remove custom model id option

* fix tests

* Fix names

* Display the clinepass models in the onboarding
2026-06-18 19:00:18 +02:00
Saoud Rizwan a3cd39da14 feat(cli): update hub dashboard design with customizations break out (#11631)
* feat(cli): refresh hub dashboard navigation

* fix(cli): address hub dashboard review feedback

* feat(cli): polish hub dashboard mockup styling

* fix(cli): polish hub dashboard pages

* fix(cli): brand hub browser metadata

* fix(cli): simplify marketplace entry details

* fix(cli): consolidate customization catalog lists

* fix(cli): dedupe marketplace installed rows

* fix(cli): preserve marketplace controls in consolidated rows

* fix(cli): align marketplace plugin controls

* fix(cli): flatten marketplace plugin tool layout

* fix(cli): place marketplace path below tags

* fix(cli): show primitive icons in marketplace rows

* fix(cli): sort sessions by displayed created time

* fix(cli): restore hub account navigation

* fix(cli): refine hub account navigation

* fix(cli): reuse hub account credentials

* fix(cli): support uninstalling local primitives
2026-06-17 21:55:31 -07:00
Saoud Rizwan a51b156383 feat(cli): add hub primitive catalogs (#11624)
* feat(cli): add hub marketplace

* chore(cli): clean up marketplace install code

* fix(cli): harden marketplace review issues

* feat(cli): add marketplace uninstall actions

* fix(cli): keep marketplace dialog actions in sync

* fix(cli): uninstall marketplace cards directly

* fix(cli): hide stale marketplace installed notice

* feat(cli): split hub marketplace pages

* fix(cli): show full marketplace descriptions

* fix(cli): avoid duplicate marketplace descriptions

* fix(cli): support marketplace deep refreshes

* fix(cli): hide marketplace placeholder icons

* fix(cli): remove marketplace card icons

* fix(cli): ignore featured marketplace entries

* chore(cli): clean marketplace follow-up code

* fix(cli): address marketplace review findings
2026-06-17 17:17:38 -07:00
Saoud Rizwan 7966bb9f21 chore(cli): release v3.0.27 2026-06-16 23:56:02 -07:00
Saoud Rizwan d92e4797ed fix(cli): reject accidental prompt fallback for bad args (#11615) 2026-06-16 23:52:43 -07:00
Saoud Rizwan 0de0c94555 fix(cli): clarify plugin MCP OAuth failure handling (#11614) 2026-06-16 23:38:28 -07:00
Saoud Rizwan 94b82fca3c feat(cli): add prefilled MCP install wizard command (#11610)
* feat(cli): add prefilled MCP install wizard command

* fix(cli): address MCP install review feedback
2026-06-16 23:07:16 -07:00
Saoud Rizwan ba2f2284e8 feat(cli): add cline skill command aliasing the open skills CLI (#11611)
* feat(cli): add cline skill command aliasing the open skills CLI

Adds a 'cline skill' command that forwards to Vercel's open skills CLI
via 'npx -y skills@latest <args>', giving parity with 'cline plugin
install' and 'cline mcp' without reimplementing skill installation.

install/add/i default to '--agent cline' (unless the user passes their
own -a/--agent) so installs land in a directory Cline already scans;
use/list/remove pass through verbatim.

* fix(cli): scope skill update to cline
2026-06-16 22:59:35 -07:00
Saoud Rizwan b7f955c78b chore(cli): release v3.0.26 2026-06-16 21:07:22 -07:00
Saoud Rizwan 8433549327 chore(sdk): release v0.0.49 2026-06-16 20:43:23 -07:00
Saoud Rizwan 860f544ab8 Revert "Make sections expandable and add ClinePass models (#11582)" (#11608)
This reverts commit 2fd944cb84.
2026-06-16 20:33:44 -07:00
Saoud Rizwan 0761de984b chore(cli): release v3.0.25 2026-06-16 19:35:02 -07:00
Saoud Rizwan 940b43f72f chore(sdk): release v0.0.48 2026-06-16 19:20:04 -07:00
Saoud Rizwan a466060f79 fix(llms): avoid disabled reasoning for StepFun flash (#11597)
* fix(llms): avoid disabled reasoning for StepFun flash

* fix(llms): cover StepFun flash reasoning variants
2026-06-16 19:07:50 -07:00
Tomás Barreiro 2fd944cb84 Make sections expandable and add ClinePass models (#11582)
* Make sections expandable and add ClinePass

* fix label

* Map clinePass models to clinePass and the rest to cline

* gate the models behind the feature flag

* Do not allow custom model on cline pass

* Do not show the count on the mode list

* fix clinepass model list

* fix focus when expanding a section

* update the model data when the provider doesn't match

* Update sdk/packages/core/src/services/llms/cline-recommended-models.ts

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* style: format Cline recommended models fallback

* Build models

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-06-17 04:02:27 +02:00
Tomás Barreiro 7d1abb25a7 Update fixed model list (#11591) 2026-06-17 03:28:35 +02:00
Saoud Rizwan 628aaa0675 feat(cli): authorize plugin MCP OAuth during install (#11575) 2026-06-16 17:46:19 -07:00
Robin Newhouse af7fda87c0 ENG-2184 Add generic provider-request capture for SDK CLI (#11481)
* Add generic provider request capture

* Use per-request provider capture files
2026-06-16 16:49:46 -07:00
Tomás Barreiro 9af6ced896 Rename Cline Pass to ClinePass everywhere (#11584)
* Rename Cline Pass to ClinePass everywhere

* Update apps/vscode/src/utils/path.ts

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-06-16 21:50:40 +02:00
Ahmad Shahzad a3258dd79a feat: update Fireworks AI model registry with latest platform offerings (#11554)
The VS Code extension's Fireworks model list is out of date compared to the current active models available on the Fireworks platform. This commit updates the registry to match the current model lineup, ensuring users can select from the latest available models.

Changes:
- Add Kimi K2.7 Code (accounts/fireworks/models/kimi-k2p7-code)
- Add Kimi K2.7 Code Fast (accounts/fireworks/routers/kimi-k2p7-code-fast)
- Add Qwen 3.7 Plus (accounts/fireworks/models/qwen3p7-plus)
- Add MiniMax M3 (accounts/fireworks/models/minimax-m3)
- Remove deprecated Kimi K2.5 (accounts/fireworks/models/kimi-k2p5)
- Remove deprecated MiniMax M2.5 (accounts/fireworks/models/minimax-m2p5)
- Remove deprecated Qwen 3.6 Plus (accounts/fireworks/models/qwen3p6-plus)

The default model remains accounts/fireworks/models/kimi-k2p6.

Files:
- apps/vscode/src/shared/api.ts
2026-06-16 14:23:55 +02:00
Saoud Rizwan ebb05ed963 feat: add MCP support to plugins (#11516)
* feat: add MCP support to plugins

* fix: address plugin MCP review comments

* feat: sync plugin MCP servers to settings

* fix: tighten plugin MCP settings behavior

* fix: isolate plugin MCP sync failures

* fix: prune plugin MCP cleanup paths

* fix: surface plugin MCP re-enable failures

* fix: surface plugin MCP install sync failures

* fix: order plugin MCP state transitions
2026-06-15 19:44:46 -07:00
Tomás Barreiro 8d03d176f2 Fix WebView env replacing (#11574)
* Fix WebView env replacing

* fix node env resolution

* fix node env resolution

* fix node env resolution

* Fix platform reference

* Fix platform reference
2026-06-16 04:09:32 +02:00
Max 32b3cfc081 fix hugging face url (#11567)
hugging face inference url was incorrect

Co-authored-by: Max Paulus 🥪 <max@cline.bot>
2026-06-15 14:26:47 -07:00
Tomás Barreiro a6500a07b4 Enable feature flags on vscode (#11566)
* Enable feature flags on vscode

* Remove comment

* refactor

* fix
2026-06-15 22:52:21 +02:00
Max 81384089c4 allow dynamic models ids in huggingface provider (#11563)
Co-authored-by: Max Paulus 🥪 <max@cline.bot>
2026-06-15 10:22:31 -07:00
Robin Newhouse 6364792c47 fix(cli): isolate history resume renderer ENG-2190 (#11502)
* fix(cli): isolate history resume renderer

* fix(cli): avoid duplicate history resume signals
2026-06-15 10:04:23 -07:00
Tomás Barreiro 40fc8879f1 Fix Postprotos modifying unrelated files (#11557) 2026-06-15 08:59:29 -07:00
Ara e91cba4045 fix(sdk): search output cap + bash executor fixes (follow-up to #11480) (#11504)
* fix(sdk): search output cap + bash executor fixes (follow-up to #11480)

Slimmed from the original revision: the aggregate per-call output budget
is deferred to its own follow-up PR. What remains:

- cap search_codebase output at 48k chars per query with a middle-cut
  notice teaching the model to narrow the pattern (robinnewhouse's
  finding on #11480 — search was the last uncapped tool)
- rename bash executor maxOutputBytes -> maxOutputChars; the limit was
  always enforced in characters. Deprecated alias retained; stale
  @default annotation fixed
- flush the rolling collector's StringDecoder at end-of-stream so
  trailing incomplete multibyte sequences are not silently dropped
  (greptile's finding on #11480)
- decouple output-limits comments from MessageBuilder's specific
  backstop value; the durable invariant is that truncation notices live
  in the preserved head/tail of an entry

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

🔮 View transcript: https://agentlogs.beatrixwoo.workers.dev/s/ygz2qho62ub6o8v1zhjvdktt

* test(sdk): cover search output cap

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-06-14 19:22:39 -07:00
Robin Newhouse e5e1aa3455 Add bounded provider request media budget ENG-2191 (#11520)
* fix(sdk): bound provider request media payloads

* fix(sdk): address media review feedback

* fix(sdk): scrub media from error tool results

* chore(sdk): remove media budget docs noise

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-06-14 18:21:12 -07:00
Robin Newhouse d2893d2e93 fix(sdk): coalesce split heredoc run_commands (#11518)
* fix(sdk): coalesce split heredoc run_commands

* fix(sdk): address heredoc coalescing review

* test(sdk): cover split heredoc edge cases

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-06-14 16:47:16 -07:00
Saoud Rizwan 4fc366df5f fix(sdk): allow ranged reads on large files (#11511)
* fix(sdk): allow ranged reads on large files

* fix(sdk): bound ranged file reads

* fix(sdk): bound streamed file reads

* fix(sdk): simplify file read streaming bounds
2026-06-12 17:23:25 -07:00
Saoud Rizwan d8eb06318b fix(sdk): fail apply_patch when a hunk is skipped (#11509) 2026-06-12 16:46:48 -07:00
Tomás Barreiro fe4eb44c6b Unselect the org when selecting Cline Pass (#11501)
* Centralize OAuth management to the SDK

* Update mock

* Cleanup TUI cline-account logic

* clean save credentails

* Remove unused code

* Reduce mocks

* use normalizeStoredAccessToken

* Add Cline Pass

* Properly read storageProviderId

* Use the name for the model generation

* Use the model for the capabilities lookup

* Fix capability discovery

* Fix getLastUsedProviderSettings

* remove the provider id from the resolveWithSingleFlight return

* Fix tests

* Remove the entry.name check

* Execute model API calls separetely

* Hide Cline Pass pricing

* update model list

* Unselect the org when selecting Cline Pass

* deduplicate onProviderChange calls

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-06-13 01:44:54 +02:00
Saoud Rizwan 6c52bdc177 fix(sdk): return captured stdout on failing run_commands (#11508)
* fix(sdk): return captured stdout on failing run_commands

* fix(sdk): respect combineOutput on command failure
2026-06-12 16:42:08 -07:00
Saoud Rizwan 5260595472 fix(sdk): treat zero search results as success (#11510) 2026-06-12 16:35:00 -07:00
Tomás Barreiro a279388451 Add feature flag for cline pass (#11500)
* Centralize OAuth management to the SDK

* Update mock

* Cleanup TUI cline-account logic

* clean save credentails

* Remove unused code

* Reduce mocks

* use normalizeStoredAccessToken

* Add Cline Pass

* Properly read storageProviderId

* Use the name for the model generation

* Use the model for the capabilities lookup

* Fix capability discovery

* Fix getLastUsedProviderSettings

* remove the provider id from the resolveWithSingleFlight return

* Fix tests

* Remove the entry.name check

* Execute model API calls separetely

* Hide Cline Pass pricing

* Display cline-pass only if the feature flag is enabled

* unselect cline pass when the feature flag is off

* Store and read feature flag cache

* Add comment

* Do not return userId

* fix tests

* Revert unrelated changes
2026-06-13 01:29:29 +02:00
Saoud Rizwan 7810a81efe feat(sdk): encourage parallel tool calls (#11514)
* feat(sdk): encourage parallel tool calls

* fix(sdk): tighten tool execution return type

* test(sdk): remove prompt assertion test

* chore(sdk): restore existing prompt formatting

* chore(sdk): soften command batching wording

* chore(sdk): preserve tool execution default

* test(sdk): remove tool description assertions
2026-06-12 16:29:07 -07:00
Tomás Barreiro 2a54e2a76e Add cline pass (#11355)
* Centralize OAuth management to the SDK

* Update mock

* Cleanup TUI cline-account logic

* clean save credentails

* Remove unused code

* Reduce mocks

* use normalizeStoredAccessToken

* Add Cline Pass

* Properly read storageProviderId

* Use the name for the model generation

* Use the model for the capabilities lookup

* Fix capability discovery

* Fix getLastUsedProviderSettings

* remove the provider id from the resolveWithSingleFlight return

* Fix tests

* Remove the entry.name check

* Execute model API calls separetely

* Hide Cline Pass pricing

* update model list

* Address PR feedback

* revert unrelated changes

* Update comment

* Update check
2026-06-13 00:49:52 +02:00
Tomás Barreiro b7c38f76c9 Add buildtime variables for posthog (#11503)
* Add buildtime variables for posthog

* Apply changes
2026-06-13 00:25:22 +02:00
Saoud Rizwan d20e517831 feat(sdk): cap tool output ingestion for bash and file reads (#11480)
Tool outputs previously entered conversation history nearly unbounded
(1MB command output, whole-file reads up to 10MB) and were re-sent on
every subsequent request. Evals showed single observations of 350KB-3.2MB
dominating token spend versus opencode's 50KB-bounded observations.

- run_commands: combined stdout/stderr capped at 48,000 chars with
  head+tail sampling (middle elided with a notice reporting total size),
  since failures usually live at the end of build/test output. Failing
  commands carry the notice in stderr errors too. Streams decode through
  StringDecoder so multibyte chars split across chunks stay intact.
- read_files: whole-file and oversized-range reads windowed to 2,000
  lines / 48,000 chars with a notice reporting total line count and how
  to paginate via start_line/end_line. Per-line cap of 2,000 chars
  defangs minified files. In-window ranged reads are byte-for-byte
  unchanged; the 10MB stat guard stays.
- Shared constants live in executors/output-limits.ts, sized below
  MessageBuilder's 50,000 per-string backstop so source notices survive
  provider-request truncation intact. Tool descriptions document the
  windowing so the model pages or filters instead of retrying.

Companion to #11463/#11465: those bound provider requests at build time;
this bounds what enters history at the source and gives the model a
recovery path.
2026-06-12 11:51:48 -07:00
Tomás Barreiro fa3630da47 Add posthog for feature flags on the cli (#11491)
* Introduce PostHog as a Feature Flag provider

* Set-up auth after login

* Update the context when something changes in the CLI

* Make the distinctId not be optional

* Dispose of the feature flag service

* Remove the distinctId from the options

* get rid of isSharedClient

* Remove timeoutMs from the posthog options

* Rename functions to not refer cli

* Change the PostHogFeatureFlagsProvider API
2026-06-12 18:02:50 +02:00
Saoud Rizwan 8229d0c9be fix: format Cline OAuth tokens in provider config (#11489) 2026-06-11 20:03:46 -07:00
Saoud Rizwan efa14b6cab chore(cli): release v3.0.24 2026-06-11 14:27:27 -07:00
Saoud Rizwan c10b417b78 chore(sdk): release v0.0.47 2026-06-11 14:02:45 -07:00
Saoud Rizwan 9958e3f354 feat(cli): allow plugin commands to submit prompts (#11479)
* feat(cli): allow plugin commands to submit prompts

* fix(cli): preserve plugin command output on abort

* revert(cli): drop ineffective clear view tweak
2026-06-11 13:49:50 -07:00
Tomás Barreiro ec75291d5b Allow overriding the API base url (#11440)
* Allow overriding the base url

* Override the mcpbaseurl

* update the api base url

* Fix tests
2026-06-11 22:29:17 +02:00
Tomás Barreiro a3a31da37d Add the FeatureFlagService to the SDK [NOOP] (#11444)
* Add the FeatureFlagService to the SDK

* Fix comments

* Dispose of the telemetry service

* Dispose of the feature flag service

* Address PR feedback

* Stop the polling early if a new one is triggered with another user id

* Address PR feedback

* Dispose of the feature flag service

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-06-11 22:11:35 +02:00
Tomás Barreiro a69d650838 Open URLs when starting device auth (#11393)
Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-06-11 21:27:44 +02:00
Ara 7934d367a9 fix(sdk): truncate structured ToolOperationResult strings in MessageBuilder (#11465)
* test(sdk): add regression tests for structured ToolOperationResult truncation

MessageBuilder tests only covered string and {type:

🔮 View transcript: https://agentlogs.beatrixwoo.workers.dev/s/mrbt2b39jfn370scr4g0ivzo"text"} tool-result
content, not the structured ToolOperationResult[] shape the default tools
(run_commands, read_files, search_codebase) actually emit. Those entries
are plain {query, result, success} objects with no type discriminator, so
the token-bloat path they create was unprotected by tests.

Adds regression tests using the real structured shape: huge result, huge
query, huge read_files payload, aggregate budget across multiple results,
mutation safety, and provider-formatted AI SDK payload size. Assertions
are on actual serialized payload sizes, not transcript shape.

The new tests fail at this commit by design; the following commit makes
them pass.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(sdk): truncate structured ToolOperationResult strings in MessageBuilder

The runtime stores structured tool outputs (ToolOperationResult[] from
run_commands/read_files/search_codebase) directly as the tool_result
content array (agentPartToContentBlock casts the array straight through).
Those entries have no type discriminator, so MessageBuilder's per-result
truncation, aggregate byte counting, and budget truncation all skipped
them — multi-megabyte command outputs and file reads were JSON-serialized
in full into every subsequent provider request.

MessageBuilder now deep-truncates nested strings inside structured
entries (middle truncation, preserving head and tail), counts them
against the aggregate text budget, collects them as budget-truncation
candidates, and deep-clones them before mutation so the original
conversation history stays untouched. Image blocks are skipped so base64
payloads survive intact.

Real-inference A/B on openrouter:minimax/minimax-m2.7 with realistic
structured payloads: 58.7% overall input-token reduction (82.6% on a
single huge command output) with identical answer correctness.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

🔮 View transcript: https://agentlogs.beatrixwoo.workers.dev/s/mrbt2b39jfn370scr4g0ivzo

* fix(sdk): include fetch_web_content in MessageBuilder truncation targets

Review feedback: fetch_web_content also returns ToolOperationResult[] and
its executor allows responses up to 5MB, but the tool was missing from
TARGET_TOOL_NAMES, so a single web fetch could still bloat every
subsequent provider request. Adds the tool to the truncation target set
with a regression test.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

🔮 View transcript: https://agentlogs.beatrixwoo.workers.dev/s/mrbt2b39jfn370scr4g0ivzo

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 11:34:43 -07:00
Ara 7f9d5461f1 fix(sdk): stop echoing full command text in run_commands tool results (#11463)
The run_commands tool result's query field repeated the entire executed
command, which already exists verbatim in the assistant tool-call input.
For large generated-file commands (e.g. cat <<EOF heredocs) this
duplicated thousands of chars of source text into every subsequent
provider request.

Bound the provider-facing echo to a 200-char preview plus a truncation
note pointing at the tool call input. Short commands pass through
unchanged. Applies to both createBashTool and createWindowsShellTool,
on success and error paths.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

🔮 View transcript: https://agentlogs.beatrixwoo.workers.dev/s/og840mbjqaog4zita8m0262m
2026-06-11 11:22:20 -07:00
Saoud Rizwan e1bdeeff68 docs(changelog): note Vertex SDK companion bump in 3.89.2 (#11459) 2026-06-11 04:10:30 -07:00
Saoud Rizwan 49897830bb fix(vscode): align Anthropic Vertex SDK with runtime SDK (#11458) 2026-06-11 04:07:51 -07:00
Saoud Rizwan 1f316a2734 fix(vscode): remove unused ClineStorageMessage import in openai-format (#11457)
The SDK 0.50.1 upgrade widened convertToOpenAiMessages to take
Anthropic.Messages.MessageParam[], which left the ClineStorageMessage
import referenced only in comments. tsc does not flag unused imports in
this config, but biome lint does, and it blocked the 3.89.2 publish.
2026-06-11 03:52:55 -07:00
Saoud Rizwan 9c1f9133c7 v3.89.2 Release Notes (#11455) 2026-06-11 03:47:24 -07:00
Saoud Rizwan 2faef2b40d fix(vscode): upgrade @anthropic-ai/sdk to 0.50.1 for Node 24 compatibility (#11454)
VS Code 1.123.0 bumped its bundled runtime from Node 22 to Node 24
(Electron 39 to 42). The Anthropic provider broke on the updated editor
because the old SDK (<=0.41.x) shipped a legacy runtime built on
node-fetch and an internal _shims layer that does not work under Node 24.

0.50.1 is the first SDK release rewritten on top of the platform's native
fetch: it has zero runtime dependencies (no node-fetch, no _shims), which
removes the incompatibility. This is the actual fix; the earlier 0.40.1
bump did not change the runtime architecture.

The 0.50.1 type changes are minimal:
- Usage gained a required server_tool_use field, so fabricated Usage
  objects in the gemini/o1/openai/vscode-lm transforms set it to null.
- ContentBlockParam widened, so Anthropic.MessageParam is no longer
  structurally assignable to ClineStorageMessage. Handled by narrowing
  the two transform helpers that only ever receive Cline history
  (sanitizeAnthropicMessages, convertAnthropicMessageToGemini), typing
  getSavedApiConversationHistory as the Cline history it reads, and
  narrowing ContextManager's loosely-typed truncated output back to
  ClineStorageMessage at the two provider/hook boundaries.
2026-06-11 03:43:33 -07:00
Saoud Rizwan 64829bca8c chore(vscode): release v3.89.1 (#11451) 2026-06-11 02:49:39 -07:00
Saoud Rizwan 4c9ba6b091 fix(vscode): restore Anthropic provider on Node 24 by bumping SDK (#11449)
VS Code 1.123.0 bumped its bundled runtime from Node 22 to Node 24
(Electron 39 to 42). The extension passes VS Code's globalThis.fetch to
every provider SDK, but @anthropic-ai/sdk was pinned at 0.37.0, which
predates the SDK's native-fetch rewrite and relies on legacy _shims
runtime detection that breaks under Node 24. The modern OpenAI and Gemini
SDKs are unaffected, which is why only the Anthropic provider broke after
users updated VS Code.

Bump to ^0.40.1, the first release with the native-fetch rewrite that
restores Node 24 compatibility, while staying short of the latest line's
larger breaking surface.

The only code change the bump requires is narrowing the image source type:
ImageBlockParam.source widened from a base64-only type to
Base64ImageSource | URLImageSource. Add a getBase64ImageSource/
getImageDataUrl helper in shared/messages/content.ts and route the
provider transforms through it. Cline only ever produces base64 image
sources, so behavior is unchanged; the helper emits the same data URL the
inline code did.
2026-06-11 02:44:25 -07:00
Bee 6138bdfe40 feat: Enforce a production singleton Cline Hub (#11372)
* feat: Enforce a production singleton Cline Hub

This PR changes local Hub startup/discovery so production uses one stable daemon per user machine instead of silently creating additional hubs on random ports.

Replace resolveSharedHubOwnerContext with resolveProductionHubOwnerContext
across doctor and hub server lifecycle management to scope hub discovery
to the production owner.

Additionally:
- Preserve and propagate auth tokens when retiring incompatible hubs
- Throw a clear error when a compatible hub is already running but its
  discovery record is missing, guiding users to run 'cline doctor fix'
- Gate port fallback behind an explicit allowPortFallback override
- Update tests to mock the new production hub owner context

* patches

* fix

* hasExplicitPort

* Restored daemon cron startup, made discovery auth tokens required again, and fixed graceful hub stop/restart paths to use the selected production/shared owner context.

* clean up

* patches

* fix Polynomial regular expression

* test

* fix: require explicit hub port fallback in production

* fix(cli): stop pgrep from parsing the hub daemon marker as an option

pgrep treats the "--cline-hub-daemon" pattern as an unknown long option
and exits 2, so doctor never found stale daemons from compiled-binary
installs, which are exactly the processes 'cline doctor fix' is told to
clean up. Pass "--" before the pattern to end option parsing.

* fix(hub): retire legacy shared-owner hubs on production startup

Pre-singleton production builds tracked the local hub under the shared
owner discovery path and spawned daemons on random fallback ports. The
production owner context never reads that path, so upgrades would leave
those daemons running indefinitely with no way to reuse or stop them.
Retire the recorded legacy hub (its record carries the auth token and
pid needed for a graceful stop) and clear the legacy record before
resolving the production hub.

* refactor(hub): simplify stale discovery clearing, share capability list

shouldClearStaleHubDiscovery was only ever called with
discoveredVerified=false (the true assignment sits on a return path),
so the expected-hub probe and compatibility check had no effect and the
condition reduced to "a discovery record exists and was not reused".
Replace it with a plain conditional and drop the tests that exercised
unreachable states.

Also move the hub capability list into a typed HUB_CAPABILITIES
constant in @cline/shared next to HubCapabilityName so the server
cannot drift from the type.

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-06-10 16:59:13 -07:00
Dominic Cooney 7d119351b1 fix(cli): suppress flickering console windows on Windows (#11408)
* fix(cli): suppress flickering console windows on Windows by setting windowsHide on child processes

On Windows, child_process.spawn/execFile default to windowsHide: false,
so console-subsystem children (powershell, rg, git, node, npm) can
allocate a new visible console window - guaranteed when detached: true
is used. In the CLI this caused constant short-lived window flashes
from run_commands, the git status bar polling, ripgrep searches and
indexing, clipboard helpers, and hook/plugin node subprocesses.

Set windowsHide: true (CREATE_NO_WINDOW; a no-op on non-Windows) on all
remaining spawn/spawnSync/execFile call sites in the SDK core, CLI,
Cline Hub, and example plugins, matching the pattern already used by
the MCP client, checkpoint-hooks, and StandaloneTerminalProcess.

* Update apps/cli/src/commands/kanban.ts

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-06-10 22:51:59 +09:00
Saoud Rizwan de987a5246 chore(cli): release v3.0.23 2026-06-09 17:43:24 -07:00
Saoud Rizwan 90050426df chore(sdk): release v0.0.46 2026-06-09 17:30:20 -07:00
Saoud Rizwan 205c5676ff fix(llms): fix disabled reasoning for Fable 5 error (#11397)
* fix(llms): avoid disabled reasoning for fable 5

* fix(llms): route fable reasoning by family

* Revert "fix(llms): route fable reasoning by family"

This reverts commit 6dd4e5dcf5.

* fix(llms): match claude fable reasoning workaround broadly
2026-06-09 17:24:34 -07:00
Bee 1c13edd395 fix(core): configured agent support as subagent tools (#11368)
* fix(core):  configured agent support as subagent tools

Introduce configured agent config parsing and tool creation for
subagents. Agent configs are defined via YAML frontmatter files
specifying name, description, tools, skills, model, and system prompt.

- Add `configured-agent-config` for loading and parsing agent
  definitions from search paths
- Add configured agent tool factory that wraps delegated agents as
  named subagent tools with policy and approval support

* patch

* patches

* fixes

* Infinite loop when YAML block is a non-object fix

* apply feedback

Forwarded host requestToolApproval into configured subagents.
Used the resolved workspace config root for configured-agent skills discovery.
Split configured-agent skill loading from root-session skills enablement.
Added host lifecycle/event plumbing for configured subagents via shared subagent callbacks.
Made UserInstructionConfigService.createSkillsExecutor optional and guarded its use.

* threaded

* test(core): cover configured subagent skill isolation (#11396)

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-06-09 17:23:00 -07:00
Ara a2a1936709 Fix Azure Foundry API version for CLI (#11359)
* Fix Azure Foundry API version for CLI

* Fix Azure API version setup
2026-06-09 16:28:37 -07:00
Max 35ce6a3f26 fix(cli): configure Vertex GCP settings (#11390)
Co-authored-by: Max Paulus 🥪 <max@cline.bot>
2026-06-09 16:05:09 -07:00
Saoud Rizwan 2c4aeae4f3 fix(vscode): handle DeepSeek V4 reasoning format (#11392) 2026-06-09 15:10:38 -07:00
Tomás Barreiro 0c027d2731 Centralize OAuth management to the SDK (#11260)
* Centralize OAuth management to the SDK

* Update mock

* Cleanup TUI cline-account logic

* clean save credentails

* Remove unused code

* Reduce mocks

* use normalizeStoredAccessToken
2026-06-09 23:56:26 +02:00
Tomás Barreiro 6cc93c124e Format vscode using biome higher order rules (#11389) 2026-06-09 22:14:19 +02:00
Saoud Rizwan 7e5b8be28c chore(cli): release v3.0.22 2026-06-09 12:08:17 -07:00
Saoud Rizwan 764e901693 test(core): update legacy migration default to claude-fable-5
The Fable 5 PR (#11385) made claude-fable-5 the newest anthropic model,
which sorts first in the generated catalog. Legacy provider migration
defaults to the first catalog model, so the migrated default changed from
claude-opus-4-8 to claude-fable-5. Update the test expectation to match.
2026-06-09 11:55:08 -07:00
Saoud Rizwan 2cabb2ddf6 chore(sdk): release v0.0.45 2026-06-09 11:43:56 -07:00
Saoud Rizwan c32789f697 chore: bump version and update changelog (v3.89.0) (#11386) 2026-06-09 11:36:08 -07:00
Saoud Rizwan 349a8da750 feat(sdk): add Claude Fable 5 model support (#11385) 2026-06-09 11:31:50 -07:00
Saoud Rizwan f09dab7a0b feat(vscode): add Claude Fable 5 support to VS Code extension (#11384) 2026-06-09 11:19:29 -07:00
Robin Newhouse 3a3ea6ee96 Fix MiniMax M3 thinking controls across gateways [ENG-2163] (#11371)
* fix(llms): route MiniMax M3 thinking controls

* test(llms): tighten MiniMax M3 routing scope

* fix(llms): preserve fetch preconnect in MiniMax shim
2026-06-09 10:53:59 -07:00
dependabot[bot] 1c1ea0bd53 chore(deps): bump shell-quote from 1.8.3 to 1.8.4 in /apps/vscode (#11383)
Bumps [shell-quote](https://github.com/ljharb/shell-quote) from 1.8.3 to 1.8.4.
- [Changelog](https://github.com/ljharb/shell-quote/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ljharb/shell-quote/compare/v1.8.3...v1.8.4)

---
updated-dependencies:
- dependency-name: shell-quote
  dependency-version: 1.8.4
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-09 19:11:24 +02:00
Mikołaj Kondratek 70303d8541 Improve bug report form: rename surface dropdown, add IDE/CLI diagnostics (#11381)
* Improve bug report form: rename surface dropdown, add IDE/CLI diagnostics

Rename the 'Plugin Type' dropdown to 'Cline Surface' since CLI is not a plugin; the option values keep each choice unambiguous.

Add an 'IDE / CLI Diagnostics' field with per-surface copy-paste steps for About info (VSCode Help/About, JetBrains Help/About Copy button) and a CLI exception using 'cline --version'. System Information is left as-is; minor overlap is acceptable.

* Update repo-label-issues workflow for renamed Cline Surface field

The auto-labeler matches the rendered '### Plugin Type' heading. Since the form label was renamed to 'Cline Surface', update the three regexes so JetBrains/VS Code/CLI labels keep applying.
2026-06-09 18:38:53 +02:00
Saoud Rizwan 8ba15dfca6 chore(cli): release v3.0.21 2026-06-08 21:44:14 -07:00
Saoud Rizwan 6ab6a1eabc chore(sdk): release v0.0.44 2026-06-08 21:27:22 -07:00
Bee 2ad4146de1 doc(sdk): add host logger support in plugin examples (#11363)
* doc(sdk): add host logger support in plugin examples

Add examples to use the exposed `ctx.logger` to plugins via the `setup` second argument for
diagnostics. Wire logging into the agents-squad example to record setup,
subagent starts, follow-ups, and async failures, with a `logPluginError`
helper that falls back to severity-tagged logs. Update README with
logger usage guidance and examples.

* patches
2026-06-08 16:29:38 -07:00
Ara cfc2250717 fix(sdk): support Vertex ADC tool-use inference (#10773)
* fix(sdk): replay Vertex thought signatures

* fix(sdk): route Gemini 2.5 thinking config

* fix(sdk): tighten Vertex thinking replay routing

* chore(sdk): keep Vertex PR scoped to signatures

* chore(sdk): remove defensive thought signature fallback

* test(sdk): cover legacy Google thought signatures

* refactor(sdk): move Gemini model facts
2026-06-08 15:38:54 -07:00
Bee 730bac7f59 fix: empty SDK message content replay for Bedrock CLINE-2373 (#11320)
* fix: empty SDK message content replay for Bedrock CLINE-2373

This fixes SDK message formatting when persisted conversation history contains an empty user or assistant message, such as after an interrupted task is resumed.

Instead of dropping the message turn, the SDK now preserves it and inserts a text content block:

ERROR: EMPTY CONTENT

This prevents providers like Amazon Bedrock from rejecting replayed history with empty content arrays while avoiding message removal that could affect provider turn ordering.

* Exported EMPTY_CONTENT_TEXT from @cline/shared so core/shared use one constant
2026-06-08 14:47:53 -07:00
Bee 797ea1f607 feat: global auto-update setting for CLI startup updates (#11326)
* feat: global auto-update setting for CLI startup updates

* patches

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-06-08 14:22:41 -07:00
Saoud Rizwan 9d59de4a4c test(llms): align ChatGPT subscription model expectations (#11348) 2026-06-07 23:44:51 -07:00
Saoud Rizwan ae67ca7a13 fix(cli): show Cline credits refill link (#11345)
* fix(cli): show Cline credits refill link

* fix(cli): simplify Cline credits error matcher

* fix(cli): keep credits handling in TUI

* fix(cli): rename credits error matcher

* fix(cli): render credits dashboard as link

* fix(cli): remove credits redirect param

* fix(cli): document temporary credits matcher
2026-06-07 19:58:34 -07:00
Tomás Barreiro 7e2583f40c Fix broken tests (#11344) 2026-06-07 18:22:43 -07:00
Tomás Barreiro ecca88bb98 Clean-up the Codex model list (#11342) 2026-06-07 17:16:12 -07:00
Saoud Rizwan 4bb93ee5b9 chore: bump version and update changelog (v3.88.1) (#11334) 2026-06-06 18:26:28 -07:00
Saoud Rizwan 4f2d7398ed fix(vscode): include walkthrough files in extension package (#11333) 2026-06-06 17:56:55 -07:00
Saoud Rizwan bc184f346d fix(cli): scroll inline ask question responses (#11293)
* fix(cli): scroll inline ask question responses

* fix(cli): address ask question review feedback
2026-06-06 17:31:16 -07:00
Bee 96aea0d34b fix(cli): connector thread session routing & stale hub session (#11325)
* fix(cli): connector thread session routing & stale hub session

Fix connector thread session routing and stale hub session recovery

**PR Description**

This fixes connector messages from separate chat threads being routed into the wrong active runtime session.

**Issue**

In Slack, if a user sent a message in a different thread while another thread was still processing, the new message could be treated as a steer message for the active task. Users could also see errors like:

```text
Slack bridge error: session not found: 1780596180501_ms45m
```

when a connector thread had a persisted session id that no longer existed in the hub, such as after a hub restart.

**Cause**

Connector conversation bindings and active turn queues were using participant identity as the primary key in several paths. That allowed messages from the same user in different chat threads to resolve to the same connector session/active turn.

Separately, persisted connector `sessionId` values were trusted without checking whether the hub still had that runtime session. After a hub restart, the connector could try to send input to a stale session id.

**Fix**

- Store connector conversation bindings by thread id instead of participant key.
- Key connector active turn queues by thread id across Slack, Discord, Telegram, Google Chat, Linear, and WhatsApp adapters.
- Only treat a follow-up as a steer message when the active turn belongs to the same thread.
- Keep participant key/label as metadata instead of using it as the conversation binding key.
- Validate a persisted session id with the hub before reusing it.
- If the persisted session is missing, clear it from thread state and start a fresh runtime session.
- Update schedule delivery metadata to target thread ids while preserving participant metadata.
- Add regression coverage for cross-thread active sessions and stale persisted session ids.

**Verification**

```bash
bun -F @cline/cli typecheck
bunx vitest run apps/cli/src/connectors/connector-host.test.ts apps/cli/src/connectors/thread-bindings.test.ts apps/cli/src/connectors/adapters/slack.test.ts apps/cli/src/connectors/adapters/telegram.test.ts apps/cli/src/connectors/adapters/discord.test.ts apps/cli/src/connectors/adapters/gchat.test.ts apps/cli/src/connectors/adapters/linear.test.ts apps/cli/src/connectors/adapters/whatsapp.test.ts
```

* patches
2026-06-06 10:17:01 -07:00
Tomás Barreiro 676b446d47 Add debug section for Cline testers (#11318) 2026-06-05 12:09:01 -07:00
Ara a8835425bf chore: bump version and update changelog (v3.88.0) (#11316) 2026-06-05 09:58:50 -07:00
Tomás Barreiro e152741e1d Remove the recommended models feature flag (#11315)
* Remove the recommended models feature flag

* Fix recommended model flag CI

---------

Co-authored-by: Arafatkatze <arafat.da.khan@gmail.com>
2026-06-05 08:36:18 -07:00
Mikołaj Kondratek 6fec41ea70 fix(mcp): guard settings writes in delete/add so the watcher can't empty the list (#11240)
deleteServerRPC and addRemoteServer wrote cline_mcp_settings.json without
setting isUpdatingClineSettings, so the chokidar settings watcher was not
suppressed during the plugin's own write. The watcher is configured with
atomic: true and awaitWriteFinish (stabilityThreshold 100ms); on Windows it
races the non-atomic writeFile, reads a transient/empty file, and
readAndValidateMcpSettingsFile() returns { mcpServers: {} }. updateServerConnections({})
then tears down every in-memory connection, so deleting one MCP server emptied
the whole list in the UI after navigating away and back (CLINE-2097).

Wrap both methods in the same guard the sibling RPCs already use
(toggleServerDisabledRPC, toggleToolAutoApproveRPC, updateServerTimeoutRPC):
set isUpdatingClineSettings = true before the write and clear it on a 300ms
timer in finally, so the delayed watcher "change" event is skipped. addRemoteServer
had the same latent omission and is fixed in the same change.

Known tradeoff (pre-existing, unchanged by this fix): the guard is a single
shared boolean cleared by uncoordinated 300ms timers, so two settings writes
within 300ms can clear the flag early. Because awaitWriteFinish only emits once
the file is stable, the worst case there is a redundant reconnect, not the
empty-list data loss this fixes. A deterministic guard (per-op token or
content-compare-and-skip in the watcher) is out of scope for this targeted fix.

Adds McpHub.deleteServerRPC.test.ts covering the user-visible symptom (delete
one of two servers -> remaining server still returned/persisted, list not empty)
and the guard contract (flag set during write, cleared after 300ms, cleared on
the not-found error path).
2026-06-05 08:36:04 -07:00
Saoud Rizwan 5de1a45d1d chore(cli): release v3.0.20 2026-06-04 17:19:31 -07:00
Saoud Rizwan 717a2e643a fix(cli): name installed plugin wrappers from source (#11291) 2026-06-04 16:54:47 -07:00
Saoud Rizwan e50184d316 chore(cli): release v3.0.19 2026-06-04 14:48:01 -07:00
Saoud Rizwan 08c6f7ecbe fix(cli): use npm update for auto updates (#11285)
* fix(cli): use npm update for auto updates

* fix(cli): preserve npm update dist tag
2026-06-04 14:45:24 -07:00
Saoud Rizwan 15e6a685d6 chore(cli): release v3.0.18 2026-06-04 13:49:47 -07:00
Saoud Rizwan bc0eed950b test(core): skip chmod-based plugin uninstall failure test on Windows (#11284) 2026-06-04 13:34:56 -07:00
Saoud Rizwan 48316027a7 chore(sdk): release v0.0.43 2026-06-04 13:19:13 -07:00
Saoud Rizwan 4d15d16109 docs(cli): release the SDK before the CLI in the publish-cli skill (#11280)
* docs(cli): release the SDK before the CLI in the publish-cli skill

Add a Step 0 to the publish-cli skill that gates a CLI release on an SDK
release when the SDK changed since its last release, and relocate the
publish-cli skill to the repo root.

Why release the SDK alongside the CLI: the CLI bundles the SDK source via
workspace:*, so the CLI always ships the latest SDK code, but the hub
daemon stamps a buildId that defaults to the @cline/core version and a
running hub is only respawned when that buildId changes. Bumping the SDK
version forces a stale hub to be retired and respawned with the new code.
It also keeps SDK releases on a regular cadence in step with the CLI.

Step 0 covers detecting unreleased sdk/packages changes, bumping the
shared SDK version + llms CHANGELOG, committing to main, kicking off
sdk-publish.yml on the latest channel, and waiting for it before cutting
the CLI release. Also fixes the now-stale working-directory note (commands
run from the repo root, not sdk/) and updates the DEVELOPMENT.md path.

* chore: move opentui skill to repo root

Relocate the opentui TUI skill from apps/cli to the repo root, matching
the real-dir + symlink convention used by the other root skills (real dir
in .agents/skills, symlink from .claude/skills).

* docs(sdk): reformat the SDK changelog and move it to sdk/CHANGELOG.md

The changelog covers all SDK packages (they share one version and release
together), so move it from sdk/packages/llms/ to the SDK root, parallel to
apps/cli/CHANGELOG.md. Reformat to match the CLI changelog: a titled
header with flat, version-only sections, newest on top, no dates, and no
Next Release bucket. The unreleased entry that bucket held is captured
from commits when the next SDK release is drafted. Update the publish-cli
skill to draft SDK notes from commits and prepend a ## <version> section
at sdk/CHANGELOG.md.
2026-06-04 12:48:17 -07:00
Bee d2339f57f1 fix(slack): normalize channel mentions to original post thread (#11273)
* fix(slack): normalize channel mentions to original post thread

Route top-level Slack channel mentions to the originating post thread so
replies land in the correct conversation. Add `resolveSlackChannelMentionThread`
to rewrite non-DM mention threads using the message's `thread_ts`/`ts` and
channel, while preserving DM threads and already-correct threads.

Includes unit tests covering normalization, no-op, and DM cases.

* thread id
2026-06-04 11:49:56 -07:00
Ahmad Shahzad a209825116 Sync Fireworks AI model registry with current platform offerings (#11173)
* feat: update Fireworks model registry to improve Cline UX for Fireworks API users

The VS Code extension's Fireworks model list was significantly out of
date compared to the current active models available on the Fireworks
platform. This commit updates the registry to match the current model
lineup, ensuring users can select from the latest available models.

Changes:
- Default model: accounts/fireworks/models/kimi-k2p6 (was kimi-k2p5)
- SDK default: accounts/fireworks/models/kimi-k2p6 (was minimax-m2p5)

Removed 6 stale/phantom models no longer available:
  - qwen3-vl-30b-a3b-thinking
  - qwen3-vl-30b-a3b-instruct
  - deepseek-v3p2
  - glm-4p7
  - glm-5
  - minimax-m2p1

Added 9 missing models:
  - accounts/fireworks/models/kimi-k2p6
  - accounts/fireworks/routers/kimi-k2p6-turbo
  - accounts/fireworks/models/deepseek-v4-flash
  - accounts/fireworks/models/deepseek-v4-pro
  - accounts/fireworks/models/glm-5p1
  - accounts/fireworks/routers/glm-5p1-fast
  - accounts/fireworks/models/minimax-m2p7
  - accounts/fireworks/models/qwen3p6-plus
  - accounts/fireworks/models/gpt-oss-20b

Fixed metadata for 3 overlapping models:
  - kimi-k2p5: contextWindow 262144 → 256000, maxTokens 16384 → 256000
  - minimax-m2p5: maxTokens 16384 → 196608
  - gpt-oss-120b: maxTokens 16384 → 32768, cacheReadsPrice 0.01 → 0.015

All models now have cacheWritesPrice: 0 because Fireworks does not
charge a separate rate for prompt cache writes (cache writes are
billed at the standard input rate, matching the SDK catalog).

Three models remain in the UI but are scheduled for deprecation on
June 17 and will be removed then:
  - accounts/fireworks/models/kimi-k2p5
  - accounts/fireworks/models/minimax-m2p5
  - accounts/fireworks/models/qwen3p6-plus

Files:
- apps/vscode/src/shared/api.ts
- apps/vscode/webview-ui/src/components/settings/__tests__/APIOptions.spec.tsx
- sdk/packages/llms/src/providers/builtins.ts

* fix: update qwen3p6-plus context window and maxTokens
2026-06-04 19:19:26 +02:00
Mahesh Narayan acc1a25e51 Update agent storage guidance (#11025) 2026-06-04 09:53:18 -07:00
Saoud Rizwan 5f17a6963f fix(cli): clear abort indicator immediately (#11265) 2026-06-04 00:02:16 -07:00
Saoud Rizwan c7ddb96ddb chore(cli): release v3.0.17 2026-06-03 21:07:30 -07:00
Bee 107bce75b2 fix(cli): recover stale interactive sessions and suppress shutdown races (#11259)
* fix(cli): recover stale interactive sessions and suppress shutdown hook races

This fixes the CLI/TUI regression introduced between `3.0.14` and `3.0.15` where the interactive CLI could enter a broken state after stopping and restarting Cline Hub, then attempting to cancel a request with Escape.

The affected release window was:

- `49e8c1b32` / `v3.0.14`: known-good baseline
- `c33c3176e` / `v3.0.15`: release containing the regression
- `fad8271f4 feat: Cline Hub web app (#10969)`: relevant behavior change in the window

The Hub web app change introduced new Hub-backed runtime/session lifecycle behavior. After Ctrl+C or Hub shutdown, the CLI could still retain an `activeSessionId` that no longer existed in the Hub/runtime process. On the next interactive send, the CLI attempted to reuse that stale session and received `session not found`. Because cancellation also targeted the stale session, Escape stopped working and OpenTUI ended up receiving failures during input handling, which made the TUI look corrupted.

The same lifecycle issue also explains the Ctrl+C errors:

```text
error: hook dispatch failed: Hub connection closed (code=1006, reason=Connection ended)
error: WebSocket connection to 'ws://127.0.0.1:50168/hub' failed: Failed to connect
```

Those were caused by late hook dispatches racing against Hub shutdown. The CLI was still trying to send hook events over a Hub WebSocket that had already closed.

**What changed**

- Added missing-session recovery in the interactive runtime.
  - Detects `session not found` / stale session errors.
  - Reads any recoverable messages from the missing session.
  - Clears the stale active session state.
  - Starts a new interactive runtime session.
  - Retries the current turn once against the fresh session.

- Made hook dispatch shutdown-aware.
  - Runtime hooks now mark themselves as shutting down before session disposal.
  - Hook dispatches are skipped once shutdown begins.
  - Dispatch failures during shutdown are suppressed, since the Hub transport closing is expected at that point.

- Reordered CLI cleanup.
  - Hooks are shut down before stopping/disposing runtime sessions.
  - This prevents abort/stop lifecycle events from trying to dispatch over a closing Hub connection.

**Regression coverage**

Added tests for:

- Recovering from a disappeared active interactive session and retrying against a new session.
- Ensuring hook events are not dispatched after shutdown begins.

**Verification**

Passed:

```text
bunx vitest run apps/cli/src/utils/hooks.test.ts apps/cli/src/runtime/interactive/session-runtime.test.ts
bun -F @cline/cli typecheck
bun -F @cline/cli test:unit
bun -F @cline/cli test:e2e:cli:tui
git diff --check
```

* SessionNotFoundError

* fix(core): preserve stale session errors in hub runs

* clean up

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-06-03 21:00:19 -07:00
Saoud Rizwan 81792d20c6 fix(sdk): keep hub daemon alive on runtime abort (#11258) 2026-06-03 20:22:55 -07:00
Saoud Rizwan e8e2af705d feat(cli): improve Telegram connector with --allowed-user-id flag (#11256)
* feat(cli): add Telegram allowed user id flag

* fix(cli): tighten connector authorization hooks
2026-06-03 17:50:07 -07:00
Bee 8f00fcf3ed chore: bun run fix (#11252) 2026-06-03 13:45:06 -07:00
Saoud Rizwan a64d17734d chore(cli): release v3.0.16 2026-06-03 13:29:33 -07:00
Saoud Rizwan 8e621817c5 feat: add plugin uninstall ability (#11247)
* feat: add plugin uninstall flow

* fix: preserve disabled plugin settings on uninstall failure
2026-06-03 13:23:35 -07:00
Bee 423fde4828 feat(cli): add Slack socket mode support (#11245)
* feat(cli): add Slack socket mode support

Add socket mode as an alternative to webhook mode for Slack
connector, allowing connections without a public URL.

- Introduce `--connection` flag to select webhook or socket mode
- Add `--app-token` option for socket mode authentication
- Make signing secret and base URL conditional on webhook mode
- Add `parseSlackConnectionMode` with validation and tests
- Update CLI platform definition to support hybrid connection type
- Update README docs with socket mode usage examples

* use base-url and remove connection flag

* isSocketMode
2026-06-03 12:00:57 -07:00
Saoud Rizwan 76af785fa6 fix: allow skills plugin capability (#11244) 2026-06-03 10:42:12 -07:00
Bee dd7042fc7b fix(core): use union schema for read files tool input validation (#11225)
* fix(core): use union schema for read files tool input validation

Move the normalizeReadFileRequests helper logic directly into the
read_files tool executor, replacing the legacy helper with inline
validation against ReadFilesInputUnionSchema. This ensures invalid
union inputs are rejected before reaching the executor.

Update tests to reflect validation behavior and add coverage for
rejecting invalid union inputs.

* add new schema support
2026-06-03 10:20:54 -07:00
Tomás Barreiro 38e5b7c26b Fix VSCode CI (#11236)
* Fix VSCode CI

* fix nightly publish

* Fix install

* Fix biome

* fix tsc

* Fix windows set-up

* Ignore .vscode-test

* Add a LICENSE to the vscode extension

* Add type roots

* Remove workspaces

* remove vscode as a bun workspace
2026-06-03 18:58:27 +02:00
Dominic Cooney 8ae99cd69f feat(llms,core): route custom registered handlers through the agent runtime (#11235)
* feat(llms,core): route custom registered handlers through the agent runtime

Expose the handler-registry helpers (hasRegisteredHandler, getRegisteredHandler,
getRegisteredHandlerAsync, isRegisteredHandlerAsync) from @cline/llms, and have
core's createAgentModelFromConfig consult the registry: when a handler is
registered for a provider, build it via createHandler and adapt its ApiHandler
surface onto the AgentModel contract (the inverse of the gateway's
toApiStreamChunk).

This lets hosts register provider handlers that need host-only dependencies
(e.g. a vscode.lm-backed handler) and have them used by the main agent loop,
not just standalone createHandler callers.

* fix(core): resolve registered handlers lazily and avoid double finish

Address review feedback:
- createAgentModelFromConfig built the handler eagerly with the sync
  createHandler, which throws for providers registered via registerAsyncHandler.
  The adapter now accepts a handler factory and resolves it on the first stream
  via createHandlerAsync, supporting both sync- and async-registered handlers.
- Guard the adapter's catch-block finish with sawFinish so a handler that emits
  an explicit done chunk and then throws does not produce two finish events.

* fix(core): preserve thought signatures and finish-reason semantics in adapter

Further review feedback on the ApiHandler -> AgentModel adapter:
- Reasoning and tool-call thought signatures are now surfaced under
  metadata.thoughtSignature (the key downstream adapters read), instead of being
  stored as metadata.signature / dropped.
- A done chunk whose incompleteReason indicates max output tokens now maps to
  finish{reason:"max-tokens"} rather than "stop".
- A turn that ends with tool calls (no explicit done) now terminates as
  finish{reason:"tool-calls"}, matching the gateway/AI-SDK adapters.

* Apply remaining changes

* fix(core): report lazy handler-factory rejection as a finish(error) event

The lazy handler resolution (await source()) ran outside the adapter's
try/catch, so a rejecting factory (e.g. when the host API is unavailable at
stream time) escaped as a raw generator exception instead of a terminal
finish{reason:"error"} event. Move the resolution inside the try block so all
failure paths converge on the same terminal finish.

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
2026-06-04 01:40:59 +09:00
Max 845970ba7d improve cline provider migration (#11242)
- user's who are signed in with oauth in old extension were not properly
migrating their token. this commit handles that

Co-authored-by: Max Paulus 🥪 <max@cline.bot>
2026-06-03 09:38:18 -07:00
Max 444a9be6ec allow baseUrl field for anthropic vendor-type providers (#11227)
Co-authored-by: Max Paulus 🥪 <max@cline.bot>
2026-06-02 20:01:49 -07:00
Saoud Rizwan ade7775337 feat(cli): install official plugins by slug (#11230)
* feat(cli): install official plugins by keyword

* fix(cli): harden official plugin clone

* fix(cli): remove official plugin repo env override
2026-06-02 18:24:37 -07:00
Tomás Barreiro e147682945 Set-up global greptile rules, kanban and other SDK files (#11233)
* Set-up global greptile rules, kanban and other SDK files

* Fix path

* Fix stale path

* Update vitest workspace config
2026-06-03 03:24:07 +02:00
Saoud Rizwan ae78fb422c docs(sdk): add custom model provider plugin example (#11234)
* docs(sdk): add custom model provider plugin example

Add an OpenRouter-backed example plugin demonstrating the providers
capability and registerProvider. It registers an OpenAI-compatible
provider plus its model catalog with the gateway so the agent can run
inference against an endpoint Cline does not bundle.

Registers under a distinct id (openrouter-plugin) to avoid colliding
with the built-in openrouter provider.

* docs(sdk): drop redundant provider section from plugin examples readme

* docs(sdk): drop provider demo line from plugin examples readme

* fix: support plugin model providers

* docs: remove provider plugin demo

* docs: address provider example review
2026-06-02 18:22:06 -07:00
Tomás Barreiro 220a21bdcf Move sdk/apps/ to apps/ (#11200)
* Move the apps to the root dir

* Update all references from sdk/apps/ to apps/

* Update dependencies

* Install bun types

* Fix types

* Fix types

* Fix linter

* Ingore apps from vscode

* Fix security warning

* Fix windows install

* Enable windows dev mode

* Revert "Enable windows dev mode"

This reverts commit a46c99282e.

* Revert "Ingore apps from vscode"

This reverts commit 47f7b265d2.

* Revert "Fix windows install"

This reverts commit 1dabba1556.

* update the repo root

* fix root dir

* fix path

* fix other path

* Fix unrelated changes

* fix: address apps move follow-up blockers (#11228)

* fix: update root app command paths

* fix: include moved apps in root checks

* fix: clean up moved app path references

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-06-03 01:49:57 +02:00
Saoud Rizwan 6aef7f5280 docs(cli): refine supply-chain scan alerts sample (#11224)
- Fold provider/model setup into a single `cline` run; drop the auth command
- Remove the /yolo on step from the Telegram setup
- Present scheduling as two clear options (Telegram chat vs terminal with
  delivery flags)
- Clarify how to find the schedule id before triggering a test run
2026-06-02 14:57:48 -07:00
Saoud Rizwan 4e56ed6922 docs(cli): add supply-chain scan alerts sample (#11222)
* docs(cli): add supply-chain scan alerts sample

Walkthrough for scheduling the Cline CLI to run Perplexity's Bumblebee
scanner and deliver compromise alerts to Telegram. Covers installing the
CLI, cloning/building Bumblebee and how it stays read-only, the Telegram
connector, and creating a scheduled scan that texts a clean/alert verdict.

* docs(cli): drop unsupported --delivery-thread from supply-chain sample
2026-06-02 14:04:33 -07:00
Saoud Rizwan 3a0f182408 fix(cli): show skills in slash autocomplete (#11220) 2026-06-02 13:18:30 -07:00
Saoud Rizwan 1f7adbd87e feat(cli): group plugin skills in settings (#11219) 2026-06-02 13:17:05 -07:00
Saoud Rizwan b0590554da feat: add skills bundled with plugins (#11161)
* feat: discover skills bundled with plugins

* fix: scope plugin bundled skills to active plugins

* fix: prevent ancestor skill discovery for plugins
2026-06-02 12:50:15 -07:00
Ara af2454f8d9 chore: bump version and update changelog (v3.87.0) (#11211) 2026-06-02 10:19:08 -07:00
Shantanu Gontia 1a4bf98e31 Update Sambanova Models (#11008)
* Update Sambanova Models

* moved to vscode/

* fix context windows

* Update Sambanova Models

* fix context windows

* Update api.ts

* Update sambanova prices
2026-06-02 18:52:00 +02:00
Ara 4139db4127 feat: add MiniMax M3 model (#11210) 2026-06-02 09:16:38 -07:00
Saoud Rizwan d55916e3ab fix(cli): show MCP OAuth errors in TUI (#11196)
* fix(cli): surface MCP OAuth errors in TUI

* chore(cli): reuse MCP status label helper
2026-06-01 20:08:23 -07:00
Bee 386ded5126 feat(cli): bundle and serve Cline Hub dashboard with cline dashboard (#11195)
* feat(cli): bundle and serve Cline Hub dashboard with cline dashboard

Add the @cline/cline-hub workspace dependency to the CLI and build the
Hub webview as part of CLI packaging. Copy the generated dashboard assets
into platform-specific CLI distributions so the dashboard is available in
built artifacts.

Refactor the Cline Hub server startup into an exported function so the CLI
can start and stop the dashboard server programmatically.

* fix(cli): resolve dashboard webview in wrapper installs

Detect the platform-specific CLI package from the published wrapper layout
and use its bundled cline-hub webview assets when no explicit dist path is
set. Add test coverage for resolving assets via CLINE_WRAPPER_PATH.

* patches

* patch

* fix server detachHub on stop

Imported detachHub.
Changed ClineHubDashboardServer.stop to () => Promise<void>.
Made stop() idempotent with a stopped guard.
Clears the health interval.
Calls server.stop(true).
Always calls await detachHub(ctx) in a finally, so hub client teardown still happens if the HTTP server stop throws.

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-06-01 17:35:24 -07:00
WaylandYang 44e15319e4 fix(plugin-sandbox): expose CLINE_PLUGIN_IMPORT_TIMEOUT_MS env override (#11065) (#11084)
* fix(plugin-sandbox): expose CLINE_PLUGIN_IMPORT_TIMEOUT_MS env override

The hardcoded 4000 ms importTimeoutMs default is too tight on Windows
cold-start; the plugin-sandbox tests already use 30_000 ms for the same
reason. This patch lets hosts raise the ceiling via env var without
touching code or adding a CLI flag, with explicit options.importTimeoutMs
still taking precedence.

Precedence: options.importTimeoutMs > env var > 4000.

Refs: #11065

* fix(plugin-sandbox): tighten env parsing + use vi.stubEnv (PR feedback)

- Number.parseInt accepts trailing garbage ("4000ms" -> 4000); switch
  to Number() + Number.isInteger() so malformed env values fall back
  to the default instead of silently consuming the numeric prefix.
- Replace manual process.env save/restore in the regression test with
  the idiomatic vi.stubEnv() / vi.unstubAllEnvs() pattern.

Per Greptile review on #11084.
2026-06-01 17:34:13 -07:00
Saoud Rizwan e424b28702 feat(cli): add plugins slash command (#11193)
* feat(cli): add plugins slash command

* fix(cli): address plugins command review feedback
2026-06-01 16:59:38 -07:00
Robin Newhouse db9971890e Add SDK telemetry for run_commands timeouts (#11149)
* feat(sdk): add run_commands timeout telemetry

* docs(sdk): document timeout telemetry event

* docs(sdk): move telemetry catalog to core docs

* fix(sdk): omit undefined timeout telemetry fields

* fix(sdk): mark timed out run_commands unsuccessful

* docs(sdk): defer telemetry catalog entry

* fix(sdk): limit run_commands timeout success override

* fix(sdk): tighten timeout telemetry plumbing
2026-06-01 16:22:04 -07:00
Tomás Barreiro d7cc9b6155 Move bun from the sdk/ to root (#11104)
* Move bun to root

* Fix scripts and pre-commit

* Update scripts

* Update workflows

* Fix cd

* fix pre-commit

* fix cli publish
2026-06-01 22:29:41 +02:00
Saoud Rizwan 05042d3ff7 docs(sdk): add env-blocker plugin example (#11192)
* docs(sdk): add env-blocker plugin example

Adds a beforeTool hook plugin that deterministically blocks the agent
from reading .env secret files via read_files, editor, or run_commands
(e.g. cat .env), while leaving .env.example/.sample/.template readable.
Demonstrates moving a security policy out of an AGENTS.md rule (a
suggestion the model can ignore) and into the execution path.

* docs(sdk): install env-blocker globally in usage examples

A secret-protection guard is most useful applied to every project, so
drop the --cwd . project-scoped install in favor of the global default.

* docs(sdk): trim env-blocker usage docs

* docs(sdk): limit env-blocker to read paths only

It is a read blocker, so only guard read_files and run_commands.
Drop the editor case (and with it the symmetric apply_patch concern),
keeping the example focused and simple.

* docs(sdk): rename env-blocker helpers for readability

collectPaths -> extractFilePaths, collectCommands -> extractShellCommands
so the beforeTool call sites read clearly at a glance.

* docs(sdk): rename commandTouchesEnv to commandReadsEnv

* docs(sdk): drop console.error from env-blocker hook
2026-06-01 12:12:16 -07:00
aikido-autofix[bot] dc2c662de6 [Aikido] Fix 53 security issues in @xmldom/xmldom, basic-ftp, axios and 14 more (#11145)
* fix(security): update dependencies

* fix: set unbounded axios fetch adapter limits for 1.16.0

---------

Co-authored-by: aikido-autofix[bot] <119856028+aikido-autofix[bot]@users.noreply.github.com>
Co-authored-by: TheRealSpencer <spencer@cline.bot>
2026-06-01 10:15:40 -07:00
Max 4b68826cb5 update changelog and bump version (#11184)
Co-authored-by: Max Paulus 🥪 <max@cline.bot>
2026-06-01 09:26:14 -07:00
Mikołaj Kondratek 85be70bb25 fix(vscode): probe @vscode/ripgrep-universal layout for VS Code ≥1.122.0 (#11178)
VS Code 1.122.0 migrated its bundled ripgrep from `@vscode/ripgrep` to
`@vscode/ripgrep-universal`, which ships per-platform/arch subdirectories
(`node_modules/@vscode/ripgrep-universal/bin/<platform>-<arch>/{rg|rg.exe}`)
instead of the previous flat `node_modules/@vscode/ripgrep/bin/{rg|rg.exe}`.
The migration commit (microsoft/vscode@bf19e5ca / @c4471e24) landed on
`release/1.122` and ships in stable 1.122.0+. Microsoft hit the identical
bug in their own sandbox engines and patched it in microsoft/vscode#317978;
our `getBinaryLocation` was still on the retired layout.

Symptom: on VS Code 1.122.x, all four `checkPath` probes in extension.ts
miss, `getBinaryLocation("rg")` throws `Could not find ripgrep binary`,
and `searchFiles` returns `{results: [], errorReason: "unknown"}`. The
@-mention picker shows "No results found" immediately, regardless of
workspace or query.

Telemetry confirmed the regression bisects cleanly to the VS Code
version boundary, not any Cline release — `mention_failed` events with
`errorType=unknown` jumped from ~300/week on 1.121.0 to ~100k/week on
1.122.0/1, while JetBrains versions (which don't use this code path)
stayed flat. Every historical Cline version is affected when the user
is on 1.122+.

Fix: probe the new `@vscode/ripgrep-universal/bin/<platform>-<arch>/`
layout first (both regular and `.asar.unpacked` variants), then fall
through to the four legacy probes so users on ≤1.121.x keep working.
`<platform>-<arch>` is `${process.platform}-${process.arch}`, matching
the directory naming Microsoft documented in #317978
(darwin-arm64, darwin-x64, linux-x64, linux-arm64, linux-arm,
linux-ia32, win32-x64, win32-arm64, win32-ia32, etc.).

This also closes the residual #11105 reports that survived #11166:
Yufeng's PR moved the failure mode from `unknown` to
`ripgrep_spawn_failed` (bare `rg`/`rg.exe` on PATH fallback) but didn't
restore actual functionality for users on 1.122.x — they got
spawn-ENOENT instead of file-not-found. With this patch the bundled
binary resolves correctly and ripgrep runs as before.

Refs: https://github.com/cline/cline/issues/11105
Refs: https://github.com/cline/cline/issues/11142
Refs: https://github.com/microsoft/vscode/pull/317978
2026-06-02 00:11:02 +09:00
Saoud Rizwan c824d8380a bump version and update changelog (#11172) 2026-05-31 23:33:45 -07:00
Yufeng He fb80840086 fix: keep file search fallback alive (#11166)
Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-05-31 23:28:28 -07:00
morning-verlu 42e4ea60db Fix marketplace getting started link (#11170)
Co-authored-by: morning-verlu <258725120+morning-verlu@users.noreply.github.com>
2026-05-31 23:12:08 -07:00
Saoud Rizwan 31a118fc0c test(core): expect Opus 4.8 default in legacy provider migration 2026-05-29 12:15:37 -07:00
Saoud Rizwan c33c3176ef chore(cli): release v3.0.15 2026-05-29 12:06:12 -07:00
Bee f5a3c591c8 chore(sdk): Model Catalog v1780081026557 (#11140)
Updated model catalog to v1780081026557 with `bun run build:models`

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-05-29 12:00:26 -07:00
Bee be930a69a8 feat(plugin): support rule contributions in sandbox (#11127)
* feat(plugin): support rule contributions in sandbox

Add plugin rule registration to the sandbox descriptor and handler state so
plugins can contribute static or dynamic rule content.

Update plugin installation to omit peer dependencies and use
legacy-peer-deps to avoid peer resolution failures during isolated installs.

* feat(cli): support participant mute targets in Discord

Resolve /mute and /unmute targets from Discord user mentions and raw
user IDs so a specific participant can be muted within a thread.

Update Discord system rules to guide agents toward thread-level and
participant-level mute commands, and add tests for target parsing.

* Update sdk/apps/cli/src/utils/chat-commands.ts

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* fix(cli): normalize addressed bot command suffixes

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-05-29 11:59:58 -07:00
Ara 3651fe9a55 fix: stop Discord connector after repeated errors (#11121)
* fix(cli): clear connector sessions on hub shutdown

* fix: stop Discord connector after repeated errors

Added error tracking to Discord connector to prevent spam:
- Tracks errors by message within a 1-minute window
- After 3 identical errors, shuts down connector instead of posting
- Prevents repeated error messages flooding Discord channels
- Logs shutdown reason for debugging

* fix: address Greptile feedback

1. Error tracker now per-thread (includes thread.id in key)

   - Prevents cross-thread error aggregation

   - One thread having errors will not kill the whole connector

2. Use fixed time window instead of sliding window

   - Track firstSeen timestamp, not just lastSeen

   - Prevents indefinite spam from errors every 61s

   - Window properly resets after ERROR_WINDOW_MS from first error

3. Remove redundant delete in clearBindingSessionIds

   - binding.state.sessionId already deleted in earlier block

   - Cleanup was misleading/unnecessary
2026-05-29 11:52:17 -07:00
Saoud Rizwan 526d8e9c93 fix(cli): make oauth urls clickable in tui (#11139) 2026-05-29 11:50:12 -07:00
Bee fad8271f41 feat: Cline Hub web app (#10969)
* feat: Cline Hub web app

Add a Cline Hub app for monitoring connected clients, viewing and driving sessions, streaming assistant output, and restarting the local hub. Document local, LAN, and tunnel usage with room-secret gating, ignore generated Cline cache/config data, and update lockfile entries.

* feat: Cline Hub UI

* feat: provider config schema

* run command update

* Use Workspace versions

* fix: rename routines to schedules

* feat(schedule): add routine summary and update support

Include last execution data in routine schedule overviews and cache the
summary state in the UI to reduce unnecessary reloads.

Add support for updating routine schedules from the hub server, validate
required fields, and trigger schedules asynchronously after confirming they
exist.

* UI for Connectors

* Fix UI switch for telemetryOptOut

* Expands Recent Sessions UI - allow title update

* feat: Cline Hub routes

* Add "health" and "version" routes

* Refactor server.ts nto 17 focused modules

* Provider Model list search box

* Extensions -> Customizations

* fix: restore discord connector catalog

* fix

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-05-29 11:25:50 -07:00
Bee d6837edda8 feat(cli): support participant mute targets in Discord (#11126)
* feat(cli): support participant mute targets in Discord

Resolve /mute and /unmute targets from Discord user mentions and raw
user IDs so a specific participant can be muted within a thread.

Update Discord system rules to guide agents toward thread-level and
participant-level mute commands, and add tests for target parsing.

* patches

* update prompt
2026-05-29 11:16:10 -07:00
Ara d0f20ca135 fix(sdk): stabilize Windows test suite (#11128) 2026-05-29 10:57:09 -07:00
Robin Newhouse b2a113a2a5 test(sdk): fix Windows CI test failures (#11132) 2026-05-29 10:46:21 -07:00
Robin Newhouse 5efa8cfd3f fix: discover symlinked SDK skill directories (#11113)
* fix: discover symlinked SDK skill directories

* test: cover circular symlink skill discovery

* test: avoid native config watchers in snapshot tests
2026-05-28 17:57:21 -07:00
Ara 9f42aea85d feat(sdk): support global AGENTS rules (#11103)
* feat(sdk): support global AGENTS rules

* fix: address global AGENTS review feedback

* fix: classify global AGENTS by exact path
2026-05-28 16:56:55 -07:00
Bee b87f61f9e4 fix(cli): stabilize core tests on Windows (#11125)
* fix(cli): stabilize core tests on Windows

Avoid several Windows-specific failure modes in the core CLI test suite.

Vitest already runs test files inside worker pools. The workspace file indexer was lazily spawning a nested worker from a transformed TypeScript module via import.meta.url, which is fragile on Windows and can cause Vitest to report only a generic worker fork crash. Disable that worker path under VITEST and use the deterministic fallback indexer for tests.

Quote process.execPath in bash executor shell-string tests. Windows Node/Bun paths commonly contain spaces, so unquoted command strings can fail under PowerShell or cmd even though they work on Unix paths.

Make detached hub probing defensive by routing probeHubServer calls through a safe wrapper, so rejected or malformed probe results are treated as unreachable instead of destabilizing startup/prewarm flows.

Also clear CLINE_RUN_AS_HUB_DAEMON in daemon test setup so tests do not inherit daemon-mode state from the surrounding CLI environment except where explicitly set.

Validation: bunx vitest run --config vitest.config.ts src/hub/daemon/index.test.ts src/services/workspace/file-indexer.test.ts src/services/workspace/mention-enricher.test.ts src/extensions/tools/executors/bash.test.ts --reporter=dot

Validation: bun run typecheck

Validation: bun run test:unit

* patch
2026-05-28 16:41:49 -07:00
Bee 38f1c7eb14 fix(cli): steer active connector sessions across turn keys (#11115)
* fix(cli): bind discord sessions to individual message authors

Resolve Discord participants from normalized message author data and persist
participant-specific thread state in bindings. Restore or create sessions per
participant so different Discord users do not accidentally share chat state.

Also add coverage for bot author handling and owner user configuration.

* patches

* fix(cli): steer active connector sessions across turn keys

Detect active connector turns by session ID when the current turn key
does not match, so replies steer the existing runtime session instead of
starting a duplicate session.

Also treat queued runtime turns as a non-error completion and log the
queued state for connector transports.

* fix(cli): steer active connector sessions across turn keys

Detect active connector turns by session ID when the current turn key
does not match, so replies steer the existing runtime session instead of
starting a duplicate session.

Also treat queued runtime turns as a non-error completion and log the
queued state for connector transports.

* add /idel
2026-05-28 13:21:09 -07:00
Robin Newhouse 81121663a4 fix(sdk): pin SAP AI provider for smoke install (#11116) 2026-05-28 13:02:26 -07:00
Bee 854ac75fe0 feat(cli): bind discord sessions to individual message authors (#11114)
* fix(cli): bind discord sessions to individual message authors

Resolve Discord participants from normalized message author data and persist
participant-specific thread state in bindings. Restore or create sessions per
participant so different Discord users do not accidentally share chat state.

Also add coverage for bot author handling and owner user configuration.

* patches
2026-05-28 12:53:37 -07:00
Ara 107f0f8337 bump version and update changelog (#11112) 2026-05-28 10:58:07 -07:00
Dominic Cooney e330695cb5 chore(codeowners): replace @candieduniverse with @dominiccooney (#11111)
Eve Killaby (@candieduniverse) has left Cline; transfer her /.github/ codeowner slot to @dominiccooney so .github changes still have four code owners able to approve.
2026-05-28 10:38:42 -07:00
Saoud Rizwan c2879dba43 feat(models): add Claude Opus 4.8 provider support (#11110)
Add claude-opus-4-8 (200k) and claude-opus-4-8:1m model variants across the
Anthropic, Claude Code, Bedrock, and Vertex catalogs, mirroring the Opus 4.7
setup (same pricing, 1M tiers, global endpoint, adaptive thinking).

- Wire the OpenRouter/Vercel AI Gateway 1m suffix handling and Cline/OpenRouter
  model refresh derivation for anthropic/claude-opus-4.8
- Register 4.8 in adaptive thinking detection so it uses the reasoning-effort
  selector path
- Bump the Claude Code "opus" alias to 4.8
- Add context window switchers in the Cline and OpenRouter model pickers
- Add provider tests for the new model ids
2026-05-28 10:37:44 -07:00
Mikołaj Kondratek f2d692cfc2 ci: gate ext-jb-test-integration auto-trigger on PR author association (#11108)
* ci: gate ext-jb-test-integration auto-trigger on PR author association

Extend the existing MEMBER/OWNER/COLLABORATOR allow-list (already used
for the /test-jetbrains comment path) to pull_request_target [opened,
reopened] as well, so the same trust model applies regardless of how
the workflow is triggered. PRs from non-trusted authors no longer
auto-trigger; a maintainer can still opt them in via /test-jetbrains.

* ci: replace hardcoded app-id with CLINE_JETBRAINS_WORKFLOW_ID var

Matches the convention already in use in cline/intellij-plugin and lets us change the App ID without touching workflow code.

* Update .github/workflows/ext-jb-test-integration.yml

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* ci: rename CLINE_JETBRAINS_WORKFLOW_KEY to CLINE_JETBRAINS_APP_KEY

The secret holds a GitHub App private key. Matches the rename of the matching app-id var in the previous commit.

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-05-28 10:36:38 -07:00
Ara 0c90bd9bcf feat: add Moonshot Kimi K2.6 model (#11109) 2026-05-28 10:17:50 -07:00
Bee 33e521e551 fix: SAP AI Core uses AI SDK community provider (CLINE-2307) (#11075) 2026-05-27 20:12:01 -07:00
Ara 9e942fbb6b Fix Discord connector registration (#11077)
* fix(cli): register discord connector

* fix(cli): scope Discord reply fallback

* docs(cli): expand Discord connector setup

* fix(cli): move Discord empty reply fallback to adapter
2026-05-27 18:47:20 -07:00
Bee 6f609e8945 fix: writeDiagnostic for logging ACP output (#11091)
Replace writeErr with writeDiagnostic for logging ACP output so that they don't show up as error.
2026-05-27 14:14:49 -07:00
Ara 762e3c42ab fix(vscode): show Qwen 3.7 Max cache support (#11079)
* fix(vscode): route Qwen cache requests

* fix(vscode): keep qwen cache alias request-scoped

* fix(vscode): mark Vercel prompt-cache models

* fix(vscode): show Qwen 3.7 Max cache support
2026-05-27 13:39:28 -07:00
Tomás Barreiro 49e8c1b324 Update CLI to 3.0.14 (#11094) 2026-05-27 12:06:31 -07:00
Tomás Barreiro 71b8f43a7a Fix OTEL variable bundling (#11092) 2026-05-27 20:58:48 +02:00
Saoud Rizwan 3068fcfedf docs(sdk): note single-file plugin dep limit and pluginPaths dir form (#11076)
Single-file plugins can only import Node builtins and @cline/*. As soon
as a plugin needs an npm dep it has to ship as a package. Adds one
sentence each to the writing-plugins guide (with dependencies in the
example package.json) and plugin-install (noting pluginPaths accepts a
package directory for fast iteration).
2026-05-27 11:21:08 -07:00
Ara 7530900166 fix: repair vscode nightly publish workflows (#11072) 2026-05-26 12:14:25 -07:00
Dominic Cooney 2b45b7b7aa Remove the VSCode Nightly (SDK) publish workflow; we are just running the regular publish workflow from the SDK branch now. (#11074) 2026-05-26 11:34:25 -07:00
Tomás Barreiro 791d238996 Move vscode to apps (#10961)
* Move all vscode related files to /apps/vscode

* Fix launch and biome

* Ignore generated files

* Remove unused icons

* fix tsconfig

* Update workflows (#10962)

* Add default branch

* Move files to the right dir

* fix: add vscode publish README placeholder

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-05-25 21:35:59 +02:00
Ara 9ee618111d bump version and update changelog (#11036) 2026-05-25 10:47:32 -07:00
tjandy98 4bec5931f6 add gpt-5.5 (#11032) 2026-05-25 06:30:21 -07:00
Saoud Rizwan 3115679031 feat: add DeepSeek V4 models (#11027)
* feat: add DeepSeek V4 models

* fix: align DeepSeek V4 cache pricing
2026-05-25 06:29:49 -07:00
Saoud Rizwan 8a6441fddd chore(cli): release v3.0.13 2026-05-22 17:41:54 -07:00
Saoud Rizwan 7ef5b1e7af test(llms): add provider VCR smoke tests to prevent e.g. ChatGPT regressions (#11012)
* test(llms): add provider vcr smoke tests

* fix(llms): harden provider vcr recording cleanup
2026-05-22 17:37:20 -07:00
Saoud Rizwan 3e58e7b024 fix(cli): show loading dialog for history resume (#11013) 2026-05-22 17:20:59 -07:00
Willis d16667f0b8 fix: use correct base URL for Vertex AI global endpoint with Claude models (#10288)
* fix: use correct base URL for Vertex AI global endpoint with Claude models

The AnthropicVertex SDK constructs the API hostname as
`${region}-aiplatform.googleapis.com`, which produces
`global-aiplatform.googleapis.com` when region is "global".
This hostname does not exist and returns 404.

Per Google Cloud docs, the correct global endpoint hostname is
`aiplatform.googleapis.com` (no region prefix). This fix overrides
the baseURL when region is "global" to use the correct hostname.

Fixes #10287

* chore: add changeset for vertex global endpoint fix

* fix: rebase on main and re-apply global endpoint baseURL override
2026-05-22 17:11:15 -07:00
Saoud Rizwan a740c49524 fix(cli): defer empty session creation after reset (#11000)
* fix(cli): skip empty clear session restart

* fix(cli): defer empty session creation after reset

* fix(cli): clarify session start race guard

* fix(cli): avoid re-resuming after new session reset
2026-05-22 17:02:47 -07:00
Saoud Rizwan 3bc1ee8382 feat(shared): add VCR request body contracts (#10997)
* feat(shared): add VCR request body contracts

* fix(shared): tighten VCR request body contracts

* test(shared): cover legacy VCR cassette playback
2026-05-22 16:00:36 -07:00
Saoud Rizwan ecf354c753 chore(cli): release v3.0.12 2026-05-22 15:14:29 -07:00
TheRealSpencer a66c5ee973 chore(deps): pin protobuf to 7.5.8 via overrides (#10998) 2026-05-22 15:11:06 -07:00
Saoud Rizwan 46659eebeb fix(cli): show loading dialog during model settings transitions (#10999)
* fix(cli): show loading dialog during model settings transitions

* docs(cli): explain loading dialog render yield
2026-05-22 14:54:16 -07:00
Saoud Rizwan 899ee0bfea fix(cli): add inline ask question tool prompt (#10989)
* fix(cli): inline runtime tool prompts

* fix(cli): address inline prompt review
2026-05-22 13:57:01 -07:00
Bee db4598e310 Cline SDK 0.0.42 (#10994)
Version bump
2026-05-22 13:31:48 -07:00
Saoud Rizwan 627e539266 fix(cli): bypass release age gate for manual updates (#10987)
* fix(cli): bypass release age gate for manual updates

* fix(cli): use yarn env override for update age gate
2026-05-22 13:29:25 -07:00
Saoud Rizwan 87ff61915f chore(cli): release v3.0.11 2026-05-22 13:03:25 -07:00
Saoud Rizwan a305556452 fix(llms): revert implicit output cap regression (#10990)
* fix(llms): avoid implicit output token caps

* test(llms): cover ChatGPT OAuth output token regression
2026-05-22 12:59:43 -07:00
Robin Newhouse ca12e97288 fix(cli): make config footer toggle hint contextual (#10976) 2026-05-22 12:34:48 -07:00
Bee 0c89716849 feat(core): includes tool names in tool results across messages (#10975)
* fix(llms): Use Google auth for Vertex Gemini

- Pass `providerConfig.gcp.projectId` through to Vertex Gemini as `googleAuthOptions.projectId`
- Disable Vertex API-key express mode when GCP project config is present so `google-auth-library` handles auth
- Add coverage for `AgentConfig.providerConfig.gcp` forwarding and Vertex Gemini provider creation
- Fix vertex model list only contains Claude models issue

* feat(core): includes tool names in tool results across messages

Updated `tool_result` content blocks to consistently include the `name` of the tool being executed. This change propagates through provider helpers and applies the new schema across all associated unit and live tests, ensuring proper tracking and logging of tool interactions within messages.
2026-05-22 11:36:32 -07:00
Robin Newhouse 1c401dbe1a Fix SDK LLM live provider configs (#10977) 2026-05-22 11:04:33 -07:00
Bee ed78404e4b fix(llms): Use Google auth for Vertex Gemini (#10974)
- Pass `providerConfig.gcp.projectId` through to Vertex Gemini as `googleAuthOptions.projectId`
- Disable Vertex API-key express mode when GCP project config is present so `google-auth-library` handles auth
- Add coverage for `AgentConfig.providerConfig.gcp` forwarding and Vertex Gemini provider creation
- Fix vertex model list only contains Claude models issue
2026-05-22 11:02:35 -07:00
Saoud Rizwan 0157ed9efb chore(cli): release v3.0.10 2026-05-21 20:23:07 -07:00
Saoud Rizwan 5574c95ff2 docs(cli): note ignore-scripts local pack guard quirk in publish-cli skill 2026-05-21 20:22:44 -07:00
Saoud Rizwan 31ee8eb744 feat(cli): install plugins from file URLs (#10945)
* feat(cli): install plugins from file URLs

* fix(cli): harden remote plugin installs

* docs: document plugin file URL installs

* docs: simplify plugin file URL wording

* docs: trim CLI plugin example
2026-05-21 19:58:20 -07:00
Renee Huang 7952e230ae Add Ollama API key note in TUI settings (#10947)
* Add Ollama API key note in TUI settings

* refactor: centralize provider config field metadata

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-05-21 16:27:31 -07:00
Bee 6bcfa34ba9 feat(sdk): track idle and pending session states (#10959)
* feat(runtime): track idle and pending session states

Propagate idle and pending as non-terminal session statuses across the CLI, hub records, and active-session checks. Update runtime handling so interactive sessions remain active while idle or awaiting approval instead of being treated as ended.

Removed `status` from showing up in `cline history` for now.

* hydrate: false no longer reads message files for every session.
2026-05-21 16:09:45 -07:00
Ara cf18793434 feat(sdk): add Poolside provider (#10956) 2026-05-21 16:04:38 -07:00
Saoud Rizwan a7ba25d6e1 feat(uri): add /lg-task webhook integration for LG dashboard flow (#10194)
* feat(uri): add LG task deeplink webhook integration

* feat(uri): include prompt-file path in LG task prompt

* refactor(uri): move LG webhook setup into integration service
2026-05-21 16:03:21 -07:00
Ara 2af2028861 feat: add Gemini 3.5 Flash to Gemini providers (#10955) 2026-05-21 15:43:44 -07:00
Ara 932c8e68e1 Infer Telegram bot username from token (#10954)
* fix(cli): infer Telegram bot username from token

* fix(cli): address Telegram connector review feedback

* test(cli): confirm Telegram schedule delivery bot metadata

* fix(core): publish schedule completion events to connectors

* Revert "fix(core): publish schedule completion events to connectors"

This reverts commit 0d64f037b9.
2026-05-21 15:03:36 -07:00
Bee 3cd13352eb fix(cron): publish failed schedule execution events (#10937)
* fix(cron): publish failed schedule execution events

Add cron runner execution event publishing for completed and failed runs.
Update connector adapters to react to failed schedule executions so clients are notified when scheduled work does not complete successfully.

* add unit tests
2026-05-21 14:57:26 -07:00
Tomás Barreiro 2c842328a8 Inject OTEL variables into the cli at buildtime (#10958)
* Inject OTEL variables

* Add telemetry to the nightly

* Add variables to the build script
2026-05-21 23:43:30 +02:00
Saoud Rizwan d3b3ff1c33 fix(cli): satisfy config item hook lint (#10968) 2026-05-21 14:38:36 -07:00
Robin Newhouse 2508e76af8 Fix SDK model catalog token-limit semantics ENG-2100 (#10946)
* fix(llms): preserve catalog output limits

* chore(llms): regenerate model catalog
2026-05-21 14:19:35 -07:00
Ara a702bf9b38 fix(cli): soften rejected tool call display (#10871) 2026-05-21 14:00:55 -07:00
Robin Newhouse a8cea1dca3 Fix disabled skill availability in SDK CLI ENG-2058 (#10876)
* fix(sdk): hide skills tool when skills are disabled

* fix(cli): refresh skill slash commands after toggle
2026-05-21 11:23:14 -07:00
Bee 2a351ffdd5 fix: Bedrock legacy migration for awsProfile (#10943)
The new Bedrock path is not failing because credential_process is unsupported. Both old and new code use AWS SDK v3’s fromNodeProviderChain, which can load credential_process.

The practical difference is that the migrated config does not include the AWS profile name:

```
"aws": {
  "region": "us-east-1",
  "authentication": "profile"
}
```

There is no `"profile": "bedrock"` So the new llms provider never targets [profile bedrock]. It calls fromNodeProviderChain({ ignoreCache: true, clientConfig: { region } }), which means AWS SDK will use AWS_PROFILE if present, otherwise default.

## Cause

The migration code only migrates awsProfile when legacy awsUseProfile is true. But the old extension treats profile auth as active when awsAuthentication === "profile" too:

```
profile:
  legacyGlobalState.awsAuthentication === "profile" || legacyGlobalState.awsUseProfile
    ? trimNonEmpty(legacyGlobalState.awsProfile)
    : undefined
    ```

## Fixes

The migration now preserves awsProfile when awsAuthentication === "profile", even if the old awsUseProfile flag is missing. It also treats profile-based Bedrock settings as enough to migrate Bedrock without static AWS keys.
2026-05-20 16:46:38 -07:00
Bee 69f148bad9 refactor: cache global settings reads by file metadata (#10933)
* refactor: cache global settings reads by file metadata

Avoid repeated global settings file reads by caching parsed settings and
validating the cache with path, mtime, and size. Invalidate the cache after
writes so updates remain visible, and clarify the legacy skills config name.

* refactor for performance

mtime-keyed cache of the parsed GlobalSettings — repeated reads do statSync + 4 comparisons instead of readFile + JSON.parse + zod (~30-100× speedup on hot path).
statSync(filePath, { throwIfNoEntry: false }) — avoids exception construction on missing-file path.
Cache invalidated on write — doesn't rely on filesystem mtime resolution.
loadSettingsFromDisk helper — pulls the read/parse/validate flow into one place, eliminates the previous three duplicated settingsCache = {...} assignments.
toggleDisabledTool cleaned up — single set construction + single write call, no branched copy of writeGlobalSettings.

* add unit tests for caching logic

* object freeze
2026-05-20 12:11:46 -07:00
Bee 6ca92794e1 chore: model catalog updated 1779302019893 (#10934)
All files automatically changed and formatted by `cd sdk && bun run build:models`

Generated model catalog version updated to 1779251127504

This includes the new X AI build
2026-05-20 11:52:25 -07:00
Bee 27bd4c6c65 chore: generated model catalog update (#10921)
version 1779251127504
2026-05-20 09:03:39 -07:00
Saoud Rizwan 015b61924f chore(cli): release v3.0.9 2026-05-19 19:48:23 -07:00
Saoud Rizwan d238239a0f fix(core): handle hub abort cleanup failures (#10918) 2026-05-19 19:38:42 -07:00
Bee e81c35d7c0 fix: Speed up CLI plugin loading and config toggles ENG-2082 (#10884)
* fix: Speed up CLI plugin loading and config toggles

Load sandboxed plugins concurrently during initialization while preserving
existing duplicate override ordering. Update plugin tool discovery to use a
single sandbox per listing and cache descriptor results by plugin path stats,
provider, and model.

Make CLI plugin/tool config toggles persistence-only from the data loader and
update the TUI optimistically, avoiding full config reloads and repeated plugin
imports when users disable tools or plugins.

* patches

* fix: refresh plugin tools when config update lacks data

Reload config data with plugin tools included when a plugin action does not return updated data. This keeps the config view in sync and clears stale plugin tool errors after refresh.

* fix(cli): preserve config item state on missing toggle data

Only update the dialog item when toggle responses include a matching item. This avoids applying fallback enabled-state changes that can desync the UI when returned config data is missing or incomplete.

---------

Co-authored-by: Tomás Barreiro <52393857+BarreiroT@users.noreply.github.com>
2026-05-19 18:18:47 -07:00
Ara 1ac5525fee fix: route Poolside Laguna models through next-gen prompts (#10910) 2026-05-19 16:40:19 -07:00
Saoud Rizwan a4d0937030 fix(cli): restore fuzzy file mention ranking (#10909)
* fix(cli): restore fuzzy file mention ranking

* fix(cli): address mention autocomplete review feedback
2026-05-19 16:09:43 -07:00
Bee 6d5c61f044 chore: gitignore SDK session and database files (#10907)
Ignore .cline session data, temp directories, SQLite artifacts, and generated metadata to prevent local SDK state and user data from being committed.
2026-05-19 13:54:53 -07:00
Mark Percival 8a6d031e8f fix(cli): keep interactive session live after cancel (#10903)
* fix(cli): keep interactive session live after cancel

* fix(sdk): use shared finish reason type
2026-05-19 13:44:59 -07:00
Renee Huang 3f808b369e sdk: add ClineCore CLI agent example (#10895)
* add a separate CLI agent using ClineCore, in comparison to the one built using Agent

* update SDK lockfile for ClineCore CLI example
2026-05-19 13:22:04 -07:00
Robin Newhouse e3b3e2306e fix(cli): accept dash-prefixed prompts after separator (#10905) 2026-05-19 12:47:07 -07:00
Tomás Barreiro b601b6e623 Update diff to 8.0.4 (#10904)
* Update diff to 8.0.4

* Remove types diff
2026-05-19 21:30:18 +02:00
Robin Newhouse ed008bd36e Route GLM thinking via provider metadata ENG-2019 (#10692)
* Route GLM thinking via provider metadata

* Address GLM provider routing review feedback
2026-05-19 12:12:51 -07:00
Saoud Rizwan 6e964c3ef0 chore(cli): release v3.0.8 2026-05-19 10:39:16 -07:00
Max 2e8579ebec bump version and update changelog (#10898)
Co-authored-by: Max Paulus 🥪 <max@cline.bot>
2026-05-19 09:54:36 -07:00
Ara cb0515d5f4 fix(cli): use Telegram numeric participant ids (#10879) 2026-05-19 08:16:38 -07:00
Bee 8c00eb9a77 fix(cli): keep failed plugins visible in config UI ENG-2073 (#10878)
* fix(cli): keep failed plugins visible in config UI ENG-2073

Preserve plugins that fail during load or setup and attach their
diagnostic phase and error message. Surface those errors in config detail
rows so users can identify and fix broken plugin definitions.

* fix(cli): preserve multiple plugin initialization failures

Aggregate all setup/load failures per plugin path instead of overwriting earlier errors. Summarize multiple errors in the config UI so rows remain readable while retaining the full failure details.
2026-05-18 22:06:57 -07:00
Saoud Rizwan 1a4122eb53 fix(cli): avoid option right fork shortcut (#10883) 2026-05-18 19:06:22 -07:00
Ara e6fa037c4c Add real PR dashboard to code review bot example (#10812)
* add real PR dashboard to code review bot example

* docs: add code review bot dashboard screenshot
2026-05-18 17:49:24 -07:00
Ara 6e0ae8db58 Add Hub Monitor UI to menubar example (#10688)
* add menubar hub monitor dashboard

* docs: add menubar preview screenshot
2026-05-18 17:44:32 -07:00
Bee 0b1fe6a982 fix: remove demo runner from weather metrics example (#10875)
Remove the embedded runDemo entry point, ClineCore import, and runDemo export so the weather metrics example behaves as a plugin-only module.
2026-05-18 16:39:13 -07:00
Tomás Barreiro c18eef5d4c Remove unused stuff on sdk (#10869)
* Clean-up root files

* Clean vscode duplicated stuff

* Remove duplicated license

* Remove duplicated tailwind recommendation

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-05-19 01:36:34 +02:00
Saoud Rizwan 65443a01ae fix(sdk): stage readme during publish (#10870)
* fix(sdk): publish readme with sdk package

* fix(sdk): use absolute readme links

* fix(sdk): stage readme during publish
2026-05-18 16:22:08 -07:00
Robin Newhouse 7d56dd1e69 refactor(sdk): remove unused codex model list helper (#10874) 2026-05-18 16:21:45 -07:00
Saoud Rizwan 501f512066 chore: remove Kanban launch modal (#10864)
* chore: remove Kanban launch modal

* chore: remove unused Kanban state key

* test: tolerate missing post-login modal
2026-05-18 16:19:52 -07:00
John Simone 5810a5d1e7 Fix AWS Bedrock region and auth configuration in CLI (#10807)
* fix AWS region and profile detection logic

* add additional bedrock config fields in provider config screens

* address greptile feedback

* remove .cline directory

* Fix Bedrock catalog and AWS region handling

---------

Co-authored-by: Robin Newhouse <robin@cline.bot>
2026-05-18 15:56:08 -07:00
Ara 6a262b395a Polish multi-agent example war room demo (#10690)
* polish multi-agent war room demo

* docs: add multi-agent war room screenshot
2026-05-18 15:45:09 -07:00
Robin Newhouse 5117b7b0b8 docs: clarify SDK agent test workflow (#10860) 2026-05-18 15:35:31 -07:00
Tomás Barreiro 30ce8b1e72 Clean-up package-lock (#10872) 2026-05-18 23:43:30 +02:00
Tomás Barreiro 2fcac58e24 Extract GH variables as env variables during workflows (#10862)
* Extract GH variables as env variables during workflows

* Update other variables
2026-05-18 13:41:34 -07:00
Saoud Rizwan 112dbaa15b ci(cli): align release changelog formatting (#10863) 2026-05-18 13:17:51 -07:00
Saoud Rizwan 996a2f6a9c fix(agents): reset usage at start of each execute() call (#10713)
AgentRuntime.execute() reset iteration, pendingToolCalls, and lastError
between run/continue calls but not usage. This caused result.usage to
accumulate across calls, and local-runtime-host then added the
already-cumulative result onto the session baseline, double-counting
every prior turn's tokens on each new turn.
2026-05-18 12:50:10 -07:00
Renee Huang 47f6d00f61 docs: add back memorybank; correct CLI install commands (#10830)
* add back memorybank; correct CLI install commands

* add more details on plugin

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-05-18 12:02:04 -07:00
Mikołaj Kondratek 9ba2c932b4 fix(mcp): disable Restart Server button when server is toggled off (#10852)
* fix(mcp): disable Restart Server button when server is toggled off

The expanded 'Restart Server' button in the MCP server row stayed clickable even when the server's toggle was off, but the backing restartMcpServer call is a no-op for disabled servers, so the click did nothing.

Bring the expanded button into parity with the collapsed icon-button (line 227 in the same file), which already gates on server.disabled. No state, label, or styling change beyond the disabled predicate.

Fixes cline/intellij-plugin#559.

* Update webview-ui/src/components/mcp/configuration/tabs/installed/server-row/ServerRow.tsx

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-05-18 18:55:47 +02:00
Robin Newhouse 01aaa5dd04 fix(e2e): harden VS Code teardown (#10796)
ENG-2064
2026-05-18 09:31:31 -07:00
Mikołaj Kondratek 38bac25721 fix(cli): satisfy useExhaustiveDependencies in ConfigPanelContent (#10850)
Destructure `loadConfigData` from `props` alongside the other props already
destructured at the top of `ConfigPanelContent`, and use the local binding in
the plugin-tools `useEffect` (both inside the body and in the dependency
array). This clears two Biome `lint/correctness/useExhaustiveDependencies`
errors introduced by #10819:

  sdk/apps/cli/src/tui/views/config-view.tsx:265:2
    × This hook specifies a dependency more specific than its captures:
      props.loadConfigData
    × This hook does not specify its dependency on props.

The previous shape mixed `props.loadConfigData` in the dep array with `props`
captures inside the effect body, which Biome flags because it cannot prove
that re-reading `props.loadConfigData` between renders is equivalent to
re-reading `props`. Destructuring once at the top resolves both diagnostics
without broadening the dep set to all of `props` (the rule's suggested
"unsafe fix") — the effect still only refires when `loadConfigData` itself
changes, matching the original intent.

No behavior change. The destructure pattern matches how `resolve`, `dismiss`,
`dialogId`, and `config` are already pulled off `props` on the same line.

The failure was first surfaced by the JetBrains-plugin integration workflow
on PR #10819, where `cline/intellij-plugin`'s `:buildClineCoreZip` task runs
`npm run lint` against the merged tree. Verified locally with:

    npx @biomejs/biome lint --diagnostic-level=error \
        sdk/apps/cli/src/tui/views/config-view.tsx

— clean, exit 0. Full repo `biome lint`: 2537 files, no errors.
2026-05-18 17:05:00 +02:00
Saoud Rizwan b4d1b83bad chore(cli): release v3.0.7 2026-05-17 17:58:53 -07:00
Saoud Rizwan 8e6780201c fix(sdk): align ChatGPT OAuth model catalog (#10841) 2026-05-17 17:56:03 -07:00
Saoud Rizwan 087c6449c3 fix(cli): avoid ChatGPT model refresh on startup (#10839)
* fix(cli): avoid ChatGPT model refresh on startup

* fix(cli): remove dead ChatGPT private model merge
2026-05-17 17:09:02 -07:00
Saoud Rizwan 5cd72e5892 chore(cli): release v3.0.6 2026-05-17 16:29:39 -07:00
Saoud Rizwan 2b2fa8a473 fix(sdk): fix ChatGPT provider models (#10836)
* fix(sdk): restore ChatGPT subscription models

* fix(sdk): tighten ChatGPT provider model tests
2026-05-17 16:26:38 -07:00
Saoud Rizwan acca9186f1 docs: clarify plugin SDK peer dependency (#10823) 2026-05-16 15:44:13 -07:00
Saoud Rizwan f3bddc4ec1 chore(cli): release v3.0.5 2026-05-16 15:28:24 -07:00
Saoud Rizwan 5c9cd557c7 fix(cli): hydrate plugin tools and commands in settings (#10819)
* fix(cli): list settings plugin tools through sandbox

* fix(cli): hydrate plugin slash commands asynchronously

* fix(cli): preserve config reload options on toggle

* fix(cli): preserve hydrated plugin tools on settings toggles
2026-05-16 15:16:33 -07:00
Saoud Rizwan 0383be5375 chore(cli): release v3.0.4 2026-05-16 11:59:08 -07:00
Saoud Rizwan 4ee1ce507e fix(cli): improve light theme TUI colors (#10817)
* fix(cli): improve light theme TUI colors

* fix(cli): address light theme review feedback
2026-05-16 11:54:27 -07:00
Saoud Rizwan 22b3d654db fix(cli): fix plugin tools in production build (#10809)
* fix(cli): ship sdk deps for plugins

* fix(core): load plugin sdk imports through jiti virtual modules

* fix(core): avoid transforming compiled plugin sdk deps

* test(core): cover packaged plugin sandbox bootstrap

* fix(cli): preflight sdk deps before wrapper publish

* test(core): keep plugin tla test lightweight
2026-05-16 10:27:45 -07:00
Saoud Rizwan 52493c5ba1 ci: add path filtering to root workflows (#10777)
* ci: add path filters to root workflows

* ci: simplify qlty workflow condition

* ci: run root workflows on main pushes

* ci: update path filters for renamed workflows

* ci: include missing workflow filter inputs

* fix(ci): include scripts in testing platform filter

* fix(ci): preserve required test gate
2026-05-15 13:19:56 -07:00
Saoud Rizwan e65e5d5900 chore: rename GitHub Actions workflows (#10793) 2026-05-15 11:58:45 -07:00
tjandy98 b144baf102 Add support for SAP AI Core Hosted LLMs to SAP AI Core Provider (#10751)
* add hosted llms

* Update sapaicore.ts

* Update sapaicore.ts

* Update sapaicore.ts

* Update sapaicore.ts
2026-05-15 02:26:34 -07:00
Renee Huang f70725dd57 nit: small improvements to readme (#10779) 2026-05-14 23:04:09 -07:00
Saoud Rizwan 3821f7e52e Add CHANGELOG links to product index in README 2026-05-14 16:59:58 -07:00
Saoud Rizwan 8bbd1ea0f1 chore(cli): release v3.0.3 2026-05-14 16:03:27 -07:00
Saoud Rizwan 9739e4d11f fix(cli): restore OpenAI compatible provider (#10761)
* fix(cli): restore OpenAI compatible provider

* fix(cli): canonicalize OpenAI compatible provider id

* fix(core): preserve migrated OpenAI compatible model metadata

* fix(core): keep OpenAI compatible migration scoped

* fix(cli): allow OpenAI compatible auth base URL

* fix(core): preserve stored model metadata
2026-05-14 15:57:38 -07:00
Robin Newhouse c8d6efd831 Route Ollama default-on reasoning disable (#10678) 2026-05-14 15:24:19 -07:00
Robin Newhouse b17dfa1593 Add typed model metadata facts (#10677) 2026-05-14 14:55:19 -07:00
Robin Newhouse d70b0d913f fix: exclude agent tooling from VSIX package (#10769) 2026-05-14 14:54:49 -07:00
Robin Newhouse 1468a7445d Refactor llms provider routing facts (#10676) 2026-05-14 14:45:34 -07:00
Saoud Rizwan 83dcd4c178 feat: add cline-sdk skill for Claude Code agents (#10766)
* feat: add cline-sdk skill for Claude Code agents

Add a comprehensive skill that teaches Claude Code how to build AI agents
with the Cline SDK (@cline/sdk). The skill covers the Agent runtime,
ClineCore sessions, custom tools, plugins, events, LLM providers,
scheduling, multi-agent teams, and production deployment.

Sourced from cline/sdk-skill, installed via the skills system.

* feat: add symlink for cline-sdk skill in .claude/skills
2026-05-14 13:06:37 -07:00
Saoud Rizwan 18181829f2 fix: handle detached hub event errors (#10760) 2026-05-14 12:04:22 -07:00
Robin Newhouse 8688678ea7 fix(cli): resolve macOS screenshot paths with narrow no-break space (#10691)
* fix(cli): resolve macOS screenshot paths with narrow no-break space

macOS Sonoma+ embeds U+202F (NARROW NO-BREAK SPACE) before AM/PM in
screenshot filenames like "Screenshot 2026-05-12 at 4.42.48\u202FPM.png".
When the path travels through clipboards, terminals, or anything that
normalizes whitespace, U+202F collapses to a regular space (U+0020) --
but the on-disk filename still contains U+202F, so readFileSync fails
with ENOENT and the paste handler silently drops the image.

Add resolveExistingImagePath() with a tiered Unicode-variant lookup
(narrow no-break space before AM/PM, NFD normalization, curly
apostrophe for localized names, and a parent-directory canonical-
whitespace scan as a last-resort fallback). Wire it into the TUI image
paste handler so dragged/pasted screenshots resolve correctly.

Tests added in image-paste.test.ts cover both the targeted AM/PM
variant and the generic NBSP-in-filename fallback path.

* fix(core): resolve macOS U+202F screenshot paths in read_files tool

The first commit on this branch fixed the CLI image-paste handler only.
A live repro from session 1778635423953_jac9b showed the same U+202F bug
on a different surface: the agent`s read_files tool. When the LLM was
asked about a macOS screenshot, it called read_files with the path text
from the user message -- which had a regular space where the on-disk
filename has U+202F -- and fs.stat returned ENOENT.

Extract the tiered Unicode-variant resolver into @cline/shared as
resolveExistingFilePath (the algorithm is generic, not image-specific).
Wire it into:

  - packages/core/src/extensions/tools/executors/file-read.ts (the
    new fix; read_files now tolerates U+202F / NFD / curly apostrophe
    variants before falling back to a parent-dir scan).
  - apps/cli/src/utils/image-attachments.ts (refactor to use the
    shared helper instead of duplicating the algorithm).

Regression tests in packages/shared/src/path-resolution.test.ts and
packages/core/src/extensions/tools/executors/file-read.test.ts; the
CLI tests from the first commit continue to pass via the shared helper.

Live verification against the real screenshot from the bug repro
(/Users/robin/Desktop/Screenshots/Screenshot 2026-05-12 at 6.18.40 PM.png,
on-disk name uses U+202F): direct invocation of the executor with a
regular-space path now returns an image block (42,296 bytes) instead
of throwing ENOENT.

* fix(shared): tighten unicode path variant probes

* fix(shared): move path resolver to storage exports

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-05-14 12:03:58 -07:00
tjandy98 da58ddd35e Add support for Opus 4.6 & 4.7, Gemini 2.5 Flash Lite & Flash Image, Amazon Nova models to SAP AI Core Provider (#10635)
* Add opus 4.6 and 4.7

* Add gemini 2.5 flash lite and flash image

* Add support for amazon nova models

* Update sapaicore.ts

* Update sapaicore.ts

* Update api.ts
2026-05-14 11:44:38 -07:00
Saoud Rizwan 1f5e2086a1 chore: remove legacy cli/ source and adjacent dead glue (#10658)
* chore: remove legacy cli/ source and direct references

The legacy React Ink CLI in cli/ has been superseded by the new SDK
CLI at sdk/apps/cli/ (published as cline@nightly today, taking over
the cline npm package on the next latest cut).

This commit deletes the cli/ source tree (~9.5MB, 142 files) and the
remaining references that point at it:
- .clinerules/cli.md (the per-area tribal-knowledge file for working
  in cli/)
- package.json workspaces: drop the "cli" entry (root no longer
  publishes a workspace from there)
- package.json coverage excludes: drop the stale **/evals/cli/**
  paths (the directory does not exist)
- tsconfig.json: drop "cli/src/**/*" from the include list so the
  root typecheck stops trying to walk into a missing tree
- .github/copilot-instructions.md: drop the CLI architecture bullet
  and the cli/src/components/ModelPicker.tsx mention from the
  add-API-provider checklist

Intentionally left alone:
- .claude/hooks/claude-code-for-web-setup.sh references
  github.com/cli/cli (the gh CLI), not our deleted cli/
- src/core/locks/SqliteLockManager.ts comments mention
  cli/pkg/common/schema.go which is the Go-based cline-core schema,
  a different component
- .github/workflows/cline-evals-regression.yml is already disabled
  pending rewire at the new SDK CLI

* chore: remove orphaned legacy CLI distribution scripts

With cli/ gone, the public-install path (curl | bash → install.sh →
download CLI binaries from cline/cline GitHub releases) and the
enterprise endpoint-bundling helpers no longer have anything to
install or bundle:

- scripts/install.sh — curl-bash installer that downloaded the old
  CLI binary from cline/cline GH releases. The new install path is
  npm i -g cline.
- scripts/test-install.sh — only tested install.sh.
- scripts/test-bundled-endpoints.sh — built a VSIX *and* CLI tgz with
  bundled staging endpoints for enterprise distribution. The CLI half
  is dead; the VSIX half can be done with vsce + add-endpoints-to-vsix.sh
  directly. The script as a whole was niche test infra, not production.
- scripts/add-endpoints-to-npm.sh — injected endpoints.json into the
  old CLI's npm tarball. Companion add-endpoints-to-vsix.sh and
  add-endpoints-to-jetbrains.sh stay (they target the extension and
  JetBrains plugin).
- package.json: drop the orphaned `test:install` root script that
  wrapped scripts/test-install.sh.

If install.cline.bot (or any public URL) was still serving
scripts/install.sh as a curl|bash target, that URL will 404 after
this lands. Worth checking and either redirecting or stubbing with
a `npm i -g cline` hint.

* chore: clean up legacy CLI references

* chore: disable legacy smoke eval workflow

* Revert "chore: disable legacy smoke eval workflow"

This reverts commit 193ee78718.

* chore: remove disabled smoke eval workflow

* docs: clarify disabled smoke eval CI
2026-05-13 22:13:58 -07:00
Robin Newhouse 59e5eac3e0 feat(cli): add --worktree flag to auto-create and run in a new git worktree ENG-2028 (#10730)
* feat(cli): add --worktree flag to auto-create and run in a new git worktree

Adds a --worktree flag to the CLI root command and 'cline task' that detects the git repo root for the current directory, creates a detached-HEAD worktree at ~/.cline/worktrees/<uuid>/<repoName>/ (matching Kanban's convention), and runs the task with cwd set to the new worktree.

The core implementation lives in src/utils/git-worktree.ts as createTaskWorktree() so any surface (CLI, VS Code, JetBrains, future tools) can use the same helper.

Works with --taskId and --continue so the legitimate workflow of 'resume this task in a fresh clean worktree to try a different approach' is supported. For --continue, the most-recent-task lookup uses the original cwd before the worktree rewrite so history resolves correctly.

Refs: ENG-2028

* address greptile review

- Distinguish 'git not installed' from 'not a git repo' so the error message points at the actual problem instead of misleading the user.

- Reject taskIds containing a null byte; would otherwise sneak past the '..' substring check on some kernels (e.g. 'safe\\0../escape').

- Reject `--worktree` without a prompt / --taskId / --continue before kanban auto-launches, so 'cline --worktree' no longer creates an orphan worktree that's abandoned if the user exits the welcome TUI without submitting a task.

* feat(cli): move worktree flag to sdk cli

* fix(cli): match kanban worktree ids

* fix(cli): handle piped worktree input

* chore: remove legacy worktree diff

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-05-13 21:38:13 -07:00
alex-lum fd2c5e1a73 feat(sdk): add auth and provider-configured telemetry to SDK TUI onboarding (#10686)
* feat(core): add user.provider_configured event and captureProviderConfigured helper

Adds a new standardized telemetry event for when a user successfully
saves a BYO (bring-your-own) provider API key, separate from the
existing user.auth_* OAuth events.

- Add USER.PROVIDER_CONFIGURED = 'user.provider_configured' to the
  core event registry.
- Add captureProviderConfigured(telemetry, provider) helper that
  mirrors the { provider } payload shape of captureAuthSucceeded and
  routes through capture() (so it respects telemetry opt-out).
- Export captureProviderConfigured from @cline/core.
- Add focused tests for payload shape, provider=undefined fallback,
  and telemetry=undefined no-op.

* feat(core): thread optional telemetry through loginLocalProvider

Adds an optional telemetry parameter to loginLocalProvider so callers
can opt into standardized auth event emission without changing
existing behavior when no telemetry is passed.

- loginLocalProvider() accepts optional telemetry service.
- Forwards telemetry to loginClineOAuth, loginOcaOAuth, and
  loginOpenAICodex for the start/success/failure auth events.
- For Cline device-code login, passes telemetry into
  completeClineDeviceAuth().
- Telemetry remains optional throughout; existing callers continue to
  compile and run unchanged.

* feat(sdk-tui): emit auth and provider_configured telemetry in onboarding flows

Wires the SDK TUI onboarding login page into the standardized
telemetry events introduced in earlier commits.

- runOAuthAuthFlow() and runDeviceCodeAuthFlow() accept an optional
  telemetry service and forward it into loginLocalProvider so the
  start/success/failure auth events fire from the underlying core
  auth helpers.
- useOnboardingController() passes getCliTelemetryService() into the
  OAuth and device-code login paths so events use existing CLI
  metadata and respect telemetry opt-out.
- saveByoConfig() fires captureProviderConfigured for the BYO
  (bring-your-own API key) onboarding path right after the
  credential save. Invalid credentials still surface later as the
  existing task.provider_api_error on the first API call.
- Esc/cancel remains a local abort and does not synthesize extra
  failure telemetry unless the underlying auth helper itself emits
  an error.
- Adds a focused test confirming the SDK TUI auth path passes
  telemetry into the underlying auth helpers.

ENG-2018

* test(core): lock in telemetry opt-out policy for captureProviderConfigured

Adds captureProviderConfigured to the existing telemetry-policy
regression suite (`describe("telemetry policy: helpers respect
telemetry opt-out")`) so a disabled adapter is verified to drop the
event end-to-end through a real TelemetryService instance, matching
the coverage the other capture* helpers already have.

Addresses PR #10686 review feedback.

* test(core): include captureProviderConfigured in disabled-adapter policy test

The policy regression test that asserts a correctly-policed disabled
adapter drops all non-required events was missing a call to
captureProviderConfigured. Add it alongside the other captures and
include the expected dropped event in the assertion.
2026-05-13 18:05:27 -07:00
Bee 6d93c8af2d fix(core): clarify tool and task execution guidance (#10734)
Update tool definitions and the shared system prompt to emphasize exact output formatting, unsupported file read cases, long-running shell command handling, and final verification requirements.

This helps agents follow expected formats, avoid unsupported reads, and complete tasks only after relevant validation passes.
2026-05-13 15:38:54 -07:00
Renee Huang 1acb2b428c add skills to example readme as well (#10733) 2026-05-13 14:57:44 -07:00
Bee be55c3ac5d feat(cli): show session status in history view (#10685)
Display session status labels in CLI history output and refresh the
standalone history TUI so running sessions can update in place.

Merge refreshed status rows with existing hydrated metadata to preserve
titles and cost details while keeping status current.
2026-05-13 14:30:38 -07:00
Bee 52872f300c fix(llms): align Bedrock auth config for API key support (#10731)
* fix(llms): align Bedrock auth config for API key support

Add Bedrock bearer token env support, accept the apikey auth alias,
and map AWS profiles correctly for gateway config. Cover bearer, IAM,
and profile credential handling with new Bedrock provider tests.

* patches
2026-05-13 12:51:02 -07:00
Tomás Barreiro 8c7617cead Update simple-git (#10720)
* Update simple-git

* Pin specific version
2026-05-13 19:55:40 +02:00
Robin Newhouse d62077d59a feat(sdk): emit telemetry for compaction lifecycle events (#10694)
* feat(sdk): emit telemetry for compaction lifecycle events

Adds two SDK telemetry events to give us observability into the
compaction pipeline, which previously only fired a generic
`agent.status-notice` pass-through that carried no result data.

- `task.compaction_executed` fires after a successful compaction with
  strategy, mode, before/after message counts, before/after token
  counts, threshold, durationMs, provider, modelId, and agent identity.
- `task.compaction_skipped` fires when the configured strategy returns
  `undefined` (reason: "no_result"). Strategy exceptions still propagate.

`createContextCompactionPrepareTurn` is extended to accept `telemetry`
and `sessionId` from `CoreSessionConfig`; the CLI manual `/compact`
path forwards both so manual compactions emit events alongside auto.

Known gap (documented inline): plugin `registerMessageBuilder()` and
runtime hook `beforeModel` compactions bypass this wrapper and emit no
telemetry today.

Field names follow the existing `TASK.*` capture-function convention
(`provider`, `modelId`, `ulid`, `Partial<TelemetryAgentIdentityProperties>`)
so downstream PostHog joins remain consistent.

Fixes CLINE-2174

* test(sdk): cover compaction telemetry opt-out policy

* fix(cli): wire telemetry service into run config so compaction events fire
2026-05-13 10:22:38 -07:00
Saoud Rizwan fece9f232e Fix link formatting for Kanban entry in README 2026-05-13 10:11:58 -07:00
Robin Newhouse 99b65b421a test(cli): cover streaming token display update (#10726) 2026-05-13 10:02:08 -07:00
Max 80783b62cb changelog and version bump (#10727)
Co-authored-by: Max Paulus 🥪 <max@cline.bot>
2026-05-13 09:37:31 -07:00
3890 changed files with 292765 additions and 625801 deletions
+208
View File
@@ -0,0 +1,208 @@
---
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
@@ -0,0 +1,107 @@
# 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
@@ -0,0 +1,231 @@
# 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
@@ -0,0 +1,134 @@
# 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
@@ -0,0 +1,258 @@
# 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
@@ -0,0 +1,131 @@
# 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
@@ -0,0 +1,304 @@
# 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
@@ -0,0 +1,148 @@
# 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
@@ -0,0 +1,279 @@
# 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
@@ -0,0 +1,269 @@
# 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
@@ -0,0 +1,157 @@
# 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
@@ -0,0 +1,649 @@
# 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
@@ -0,0 +1,253 @@
# 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
@@ -0,0 +1,257 @@
# 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
@@ -0,0 +1,227 @@
# 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
@@ -0,0 +1,259 @@
# 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
+5
View File
@@ -0,0 +1,5 @@
---
"claude-dev": patch
---
fix: use correct base URL for Vertex AI global endpoint with Claude models
+2 -2
View File
@@ -41,11 +41,11 @@ fi
# Install project dependencies
echo "Installing dependencies..."
npm run install:all
bun run install:all
# Generate gRPC/protobuf types (required for TypeScript)
echo "Generating proto types..."
npm run protos
bun run protos
echo ""
echo "Session setup complete!"
+1
View File
@@ -0,0 +1 @@
../../.agents/skills/cline-sdk
+266
View File
@@ -0,0 +1,266 @@
---
name: publish-cli
description: Use when preparing, tagging, and publishing an apps/cli npm release. Guides changelog drafting, apps/cli/package.json version bumps, cli-vX.Y.Z tags, local npm publishing, and the publish-cli GitHub workflow.
---
# CLI Release
Use this skill when the user asks to release the CLI, publish `cline`, bump the CLI version, draft release notes, create a `cli-vX.Y.Z` tag, or trigger the CLI publish workflow.
The CLI is npm-only. Do not add alternate distribution or signing steps.
> Working directory: run every command below from the repository root. Paths and scripts (e.g. `apps/cli/package.json`, `sdk/packages/`, `bun release cli`, `bun run version`) are written relative to the repo root.
The skill should guide the user through one release preparation flow, then offer the publish path options. The two normal publish paths are GitHub Actions and local publishing from an authenticated machine.
## Release contract
- SDK prerequisite: the CLI depends on the SDK via `workspace:*` (`@cline/core`, `@cline/shared`, and friends). If the SDK changed since its last release, release the SDK first and wait for it to finish publishing before releasing the CLI. See "Step 0: Release the SDK first if it changed" below.
- Version source: `apps/cli/package.json`.
- Main release tag: `cli-vX.Y.Z`, where `X.Y.Z` matches `apps/cli/package.json`.
- Nightly release version: `X.Y.Z-nightly.TIMESTAMP`.
- Release prep includes approved release notes, a version bump, and an `apps/cli/CHANGELOG.md` update.
- Publish paths:
- GitHub workflow: `.github/workflows/cli-publish.yml`.
- Local publish helper: `bun release cli`.
- npm dist-tags and git tags are separate. `--tag latest` and `--tag nightly` are npm registry channels. `cli-vX.Y.Z` is a git tag for source history and GitHub releases.
- The GitHub main release workflow runs from `main`, requires an existing `cli-vX.Y.Z` tag, checks out that tag, and publishes from it.
- The GitHub nightly workflow publishes to npm with the `nightly` dist-tag and does not create a tag.
- The local release helper requires a clean checkout and `cli-vX.Y.Z` to point at `HEAD` locally and on `origin` before publishing.
- Local GitHub release creation requires `gh` to be authenticated with release permissions for the repo.
- Always ask before pushing commits or tags.
- Do not amend commits unless explicitly requested.
## Step 0: Release the SDK first if it changed
Do this before anything else in the Workflow below.
The CLI builds and ships against the SDK source in the monorepo (`workspace:*` for `@cline/core`, `@cline/shared`, and the rest), so a CLI release always contains the latest SDK code whether or not the SDK was released. The build and tests use that source too, not anything from npm. Releasing the SDK alongside the CLI is still worth doing for two reasons:
- Hub freshness. The hub daemon lives in `@cline/core` and stamps a `buildId` that defaults to the `@cline/core` package version (`resolveHubBuildId` in `sdk/packages/core/src/hub/discovery/index.ts`). A running hub is only retired and respawned when that `buildId` changes (`isCompatibleHubRecord` / `retireIncompatibleHub` in `sdk/packages/core/src/hub/daemon/index.ts`). So if the SDK code changed but the version did not, a user who upgrades the CLI keeps talking to their already-running hub, which is still executing the old SDK code. Bumping the SDK version makes the new CLI's `buildId` differ, so the stale hub is detected as incompatible and respawned with the fresh code.
- Release hygiene. We want regular SDK releases; cutting one whenever we cut a CLI release keeps the published SDK in step with what the CLI ships.
So when the SDK has changed, release it first (which bumps the `@cline/core` version), then cut the CLI release on top of that bump. Leave the CLI's SDK dependency as `workspace:*` — the fix is to release the SDK, not to pin the CLI.
1. Check for unreleased SDK changes.
```sh
git fetch origin --tags
git tag --list 'sdk/sdk/v*' 'sdk-v*' --sort=-v:refname | head -1
git log <last-sdk-tag>..origin/main --oneline --no-merges -- sdk/packages
```
`sdk/<pkg>/v*` tags are created by the `sdk-publish.yml` workflow; `sdk-v*` tags are created by the local `bun release sdk` helper. Use whichever is newest as the baseline.
If `git log` prints no commits, the SDK is already up to date. Skip the rest of Step 0 and continue with the Workflow below.
If it prints commits, sanity-check the diff (ignore entries that are only the previous version-bump commit's lockfile or generated files), then release the SDK.
2. Decide the SDK version bump.
All SDK packages share one version, read from `sdk/packages/llms/package.json`. Ask whether this is patch, minor, major, or an explicit version. Patch is the default. Do not guess if the user has not made it clear.
3. Draft the SDK release notes and update the changelog.
Draft user-facing notes from the SDK commits found in step 1, translating commit messages into user-facing language (same approach as the CLI release notes below). Prepend a new `## <version>` section with those notes to the top of `sdk/CHANGELOG.md`, using the header format `## <version>` with no date — the same flat, newest-on-top format as `apps/cli/CHANGELOG.md`. This is the SDK changelog (all SDK packages share one version) and it is maintained by hand; the `sdk-publish.yml` workflow does not read it.
4. Bump versions and regenerate.
```sh
bun run version <version>
```
This bumps every SDK `package.json` to the new version, regenerates the lockfile and the generated model catalog, formats, and builds. Review the result.
5. Commit and push the bump to `main`.
The `sdk-publish.yml` workflow publishes the version that is committed on `main` and tags that commit, so the bump must land on `main` before the workflow runs.
```sh
git add -A
git commit -m "chore(sdk): release v<version>"
```
Ask before pushing:
```sh
git push origin HEAD
```
6. Trigger the SDK publish workflow on the `latest` channel.
```sh
gh workflow run sdk-publish.yml -f channel=latest -f confirm_publish=publish
gh run list --workflow=sdk-publish.yml --limit=1 --json databaseId,url,status,createdAt --jq '.[0]'
```
The workflow runs the SDK tests, publishes `@cline/shared`, `@cline/llms`, `@cline/agents`, `@cline/core`, and `@cline/sdk` to npm with the `latest` dist-tag in dependency order, and pushes `sdk/<pkg>/v<version>` git tags.
7. Wait for the SDK workflow to succeed before starting the CLI release.
```sh
gh run watch <run-id> --exit-status
```
Do not start the CLI release until this run has finished successfully. The CLI does not install the SDK from npm, but cutting the CLI release on top of a clean, completed SDK release keeps the two in step: the CLI release commit then sits on top of the `@cline/core` version bump, so the shipped CLI carries the new version that forces a running hub to respawn with the new code, and you are not building a CLI release on top of an SDK release that failed midway.
After the SDK release succeeds, pull `main` so the CLI release is prepared on top of the SDK version bump:
```sh
git checkout main && git pull --ff-only
```
Then continue with the Workflow below.
For a local SDK publish from an authenticated machine instead of the workflow, `bun release sdk <version>` exists, but prefer the `sdk-publish.yml` workflow for normal releases so the CLI release can gate on a single GitHub Actions run.
## Workflow
Complete Step 0 first. Only proceed once the SDK is released (or you confirmed no SDK release was needed).
1. Gather context.
```sh
git status --short --branch
git fetch origin --tags
git tag --list 'cli-v*' --sort=-v:refname | head -10
node -p "require('./apps/cli/package.json').version"
```
Find the latest CLI tag. If there is no `cli-v*` tag, use the first relevant CLI release commit as the baseline and say that the baseline is inferred.
2. Collect release commits.
```sh
git log <last-cli-tag>..HEAD --oneline --no-merges -- apps/cli sdk/packages sdk/scripts .github/workflows/cli-publish.yml
```
The `sdk/packages` commits matter here even though the SDK was released separately in Step 0: the CLI bundles the SDK, so SDK changes ship in this CLI release too. Read those commits and fold anything user-relevant to the CLI into the release notes (provider/model updates, behavior changes, fixes the CLI inherits). Skip SDK changes that are purely internal or have no CLI-visible effect.
3. Draft user-facing release notes.
Include user-facing features, fixes, behavior changes, compatibility changes, and notable install or release changes. Exclude pure refactors, tests, style, chores, and internal file moves unless they matter to users.
Write a flat bullet list. Translate commit messages into user-facing language. If a commit is unclear, read the full commit before summarizing it.
Present the draft and wait for approval before editing files.
4. Decide the version bump.
Ask whether this should be patch, minor, major, or an explicit version. Do not guess if the user has not made it clear.
5. Update release files.
Update `apps/cli/package.json` to the approved version.
Prepend a section to `apps/cli/CHANGELOG.md` for the approved version using the approved release notes. Use the header format `## X.Y.Z` with no date. The publish workflow extracts the top section of the changelog by matching `^## [0-9]` and pastes it verbatim into the GitHub release body and the Slack release announcement, so the section content is the release notes that get shipped.
6. Verify before committing.
Run focused checks first:
```sh
bun -F @cline/cli typecheck
bun -F @cline/cli test:unit
```
For higher confidence, run:
```sh
bun run types
bun --cwd apps/cli run build:platforms:single
```
If the user wants full release confidence before tagging, run:
```sh
bun run test
bun --cwd apps/cli run build:platforms
```
Known local-only test failure: `src/commands/distribution-package.test.ts > rejects direct source package packing by default` will fail on machines that have `ignore-scripts=true` in `~/.npmrc` (set by the npm supply-chain hardening guide). Bun reads npm's `ignore-scripts` from `~/.npmrc`, so `bun pm pack --dry-run` skips the source-publish `prepack` guard and exits 0, which the test reads as a failure. CI does not set `ignore-scripts`, so the test passes there. Confirm by running `bun pm pack --dry-run` directly: with `~/.npmrc` in place it exits 0 with no guard output; with `~/.npmrc` moved aside it exits 1 and prints the guard message. This is not a release blocker by itself, but it does mean the local-publish path (`bun release cli`) will also bypass the source-publish guard on this machine; prefer the GitHub Actions publish path on machines with `ignore-scripts=true` set globally, or temporarily unset it (`npm config delete ignore-scripts` or `mv ~/.npmrc ~/.npmrc.bak`) for the duration of a local publish.
7. Commit release changes.
Only after the user approves the notes and version:
```sh
git add apps/cli/package.json apps/cli/CHANGELOG.md
git commit -m "chore(cli): release vX.Y.Z"
```
Ask before pushing the release commit:
```sh
git push origin HEAD
```
For the GitHub main release path, ask before creating and pushing the release tag:
```sh
git tag -a cli-vX.Y.Z -m "CLI vX.Y.Z"
git push origin refs/tags/cli-vX.Y.Z
```
8. Publish.
Ask the user which path to use:
- GitHub main release. Use this after the release commit is on `main` and the matching `cli-vX.Y.Z` tag has been pushed. The workflow publishes to npm from that tag, creates the GitHub release, and posts to Slack.
- Local release. Use this when the user wants to publish from this machine. The local machine must be authenticated to npm and GitHub.
- GitHub nightly release.
- Stop after the version commit.
For GitHub main release:
```sh
gh workflow run cli-publish.yml -f publish_target=main -f git_tag=cli-vX.Y.Z -f confirm_publish=publish
gh run list --workflow=cli-publish.yml --limit=1 --json url,status,conclusion,createdAt --jq '.[0]'
```
For GitHub nightly release:
```sh
gh workflow run cli-publish.yml -f publish_target=nightly
```
For forced GitHub nightly release:
```sh
gh workflow run cli-publish.yml -f publish_target=nightly -f force_nightly_publish=true
```
For local publish:
```sh
gh auth status
npm whoami
git tag -a cli-vX.Y.Z -m "CLI vX.Y.Z"
git push origin refs/tags/cli-vX.Y.Z
bun release cli
```
After a successful local publish, ask before running:
```sh
gh release create cli-vX.Y.Z --verify-tag --title "CLI vX.Y.Z" --notes "Paste the approved release notes here."
```
If publishing with another npm dist-tag:
```sh
bun release cli --tag next
```
9. Final response.
Report:
- version
- tag
- changelog file updated
- commit hash
- whether anything was pushed
- publish path selected
- workflow URL or local publish result
- tests and builds run
+55
View File
@@ -0,0 +1,55 @@
# Bun (tooling) and Node (runtime)
This repo uses **bun** for package management and task running, and **Node** as
the execution runtime. Both are correct at the same time; the distinction is the
source of most confusion, so keep it straight before editing scripts, configs,
docs, or comments.
## Use bun for tooling
- `bun install` (never `npm install` / `npm ci`)
- `bun run <script>` (never `npm run <script>`)
- `bunx <bin>` (never `npx <bin>`)
- `bun <file>.ts` to run a TS entrypoint directly (no `ts-node` / `tsx`)
- `bun esbuild.mjs` to drive the build (esbuild/vite are still the bundlers)
- `bun run --parallel ...` for parallel tasks
The root `bun.lock` is the single lockfile for the whole workspace, including
`apps/vscode`, `webview-ui`, and `testing-platform`. There are no per-package npm
lockfiles.
## Node is the runtime — do NOT rewrite these to bun
The build product runs on Node: the VS Code extension host loads
`dist/extension.js` as CommonJS under Node, and the standalone `cline-core` is a
Node process. The following are Node runtime/ABI references and are correct as-is:
| Reference | Why it is Node |
|-----------|----------------|
| esbuild `platform: "node"` / `target: "node..."` | The bundle targets the Node runtime (extension host, standalone core). |
| `TARGET_NODE_VERSION` (`scripts/package-standalone.mjs`) | Pins the Node ABI of the bundled standalone runtime (matches the JetBrains-packaged Node). |
| `prebuild-install --target=<node version>` | Downloads native `.node` binaries for that Node ABI. |
| `NODE_PATH=... node cline-core.js` | The standalone core is launched by Node, not bun. |
| `node:` import specifiers (e.g. `node:fs`) | Node builtin module scheme; unrelated to tooling. |
| `process.versions.node`, `engines.node`, `@types/node` | Runtime version probe / declared runtime / its types. |
| `ELECTRON_RUN_AS_NODE` | VS Code/Electron runs the extension host as Node. |
When a file legitimately uses both bun and node (e.g. `package-standalone.mjs`
does `bun install` but `prebuild-install --target=<node>`), the `node` token is
the runtime/ABI target, not tooling. If unsure, leave it.
## Tests: bun vs the VS Code host
A test file's runner is decided by its import:
- **`import ... from "bun:test"`** → runs under `bun test` (the node-side unit
suites + the SDK/model-catalog suites). `scripts/run-bun-unit-tests.ts`
discovers these by the `bun:test` import and runs one isolated bun process per
file. `build-tests.js` excludes them from the integration compile so the
`bun:test` builtin never reaches Node.
- **`import ... from "mocha"`** → runs under `@vscode/test-cli` in a real VS Code
extension host (Node). These exercise the live `vscode` API and cannot run
under bun.
So a file imports `bun:test` XOR `mocha`. Don't add `bun:test` to a test that
needs the real extension host.
-33
View File
@@ -1,33 +0,0 @@
# CLI Development
The CLI lives in `cli/` and uses React Ink for terminal UI.
- If needed, look at `cli/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/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/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.
+129
View File
@@ -0,0 +1,129 @@
# Debug Harness
HTTP-controlled debugger for the VSCode extension at `src/dev/debug-harness/server.ts`.
## Quick start
```bash
# Build extension first if needed (protos + esbuild):
bun run protos && IS_DEV=true bun esbuild.mjs
# Launch (skip-build if already built). Run with node, NOT bun — Playwright's
# Electron launch times out under bun:
node src/dev/debug-harness/server.ts --skip-build --auto-launch
# In another terminal:
curl localhost:19229/api -d '{"method":"status"}'
```
## Data Isolation
The debugee runs with `CLINE_DIR=~/.cline2` by default, separate from your real `~/.cline`.
This prevents the debugee's logout from logging out the debugger, and vice versa.
Override with `--cline-dir /tmp/test-dir`. Check with `status()``clineDir`.
## Browser Capture & OAuth
The debugee runs with `CLINE_CAPTURE_BROWSER=1`, which intercepts `openExternal()` in
`src/utils/env.ts`. URLs are captured instead of opening a real browser:
- Logged to `$CLINE_DIR/data/debug-captured-urls.jsonl`
- POSTed in real-time to `/captured-url` on the harness server
- Queryable via `oauth.captured_urls`
### OAuth API
- **`oauth.captured_urls`** `{clear?}` — URLs the debugee tried to open
- **`oauth.read_stored_token`** — Check auth token presence in secrets.json
- **`oauth.simulate_callback`** `{path, code?, state?, provider?, token?}` — Build vscode:// callback URI
- **`oauth.read_captured_urls_file`** — Read on-disk JSONL of captured URLs
### OAuth testing flow
For **Cline OAuth** (SDK local callback): The SDK starts a local HTTP server, the auth URL
is captured. To complete: open the captured URL in a real browser (it redirects back to the
SDK's callback server), OR extract the callback port and `curl http://127.0.0.1:PORT/callback?code=...`.
For **MCP/Provider OAuth** (vscode:// URI): The redirect goes to a vscode:// URI.
`oauth.simulate_callback` only *builds* the URI — it does not deliver it, and the ESM
extension host can't `require()` the handler. To actually deliver the callback, call the
debug-only hook via `ext.evaluate` (with `awaitPromise: true`):
`globalThis.__clineHandleUri("vscode://saoudrizwan.claude-dev/...?code=...&state=...")`.
It runs the same `SharedUriHandler.handleUri` as VSCode's real URI handler and exists only
when `CLINE_CAPTURE_BROWSER` is set (the harness always sets it; never ships in prod).
For end-to-end MCP OAuth, get a real `code` from the local MCP OAuth test server
(`bun run dev:mcp-oauth-test-server`).
## Navigating Views — Use Commands, Not Clicks
Don't try to find/click small sidebar icons. Use VSCode commands via command palette.
Registered in `src/registry.ts`:
| Command | View |
|---------|------|
| `cline.accountButtonClicked` | Account / sign-in |
| `cline.historyButtonClicked` | Task history |
| `cline.settingsButtonClicked` | Settings |
| `cline.mcpButtonClicked` | MCP servers |
| `cline.plusButtonClicked` | New task (chat) |
| `cline.worktreesButtonClicked` | Worktrees |
```bash
curl localhost:19229/api -d '{"method":"ui.command_palette","params":{"command":"cline.accountButtonClicked"}}'
```
## Key commands
All via `POST localhost:19229/api` with `{"method":"...", "params":{...}}`:
- **`launch`** / **`shutdown`** — lifecycle
- **`ui.screenshot`** — screenshot to `/tmp/cline-debug/`; returns `{path}`**use `read_file` on the path to examine, do NOT `open` the file** (Preview.app covers the VSCode window)
- **`ui.open_sidebar`** — open the Cline sidebar
- **`ext.set_breakpoint`** `{file, line, condition?}` — breakpoint by source file (sourcemap-resolved)
- **`ext.evaluate`** `{expression, callFrameId?}` — eval in extension host
- **`ext.resume`** / **`ext.step_over`** / **`ext.step_into`** — stepping
- **`ext.call_stack`** — inspect when paused
- **`web.evaluate`** `{expression}` — eval in webview
- **`web.post_message`** `{message}` — send postMessage to extension host via exposed vsCodeApi
- **`wait_for_pause`** `{timeout?}` — block until breakpoint hit
- **`ui.locator`** `{role?, testId?, text?, frame?}` — Playwright locator (auto-retries on stale sidebar frame)
- **`ui.react_input`** `{text, selector?, clear?, submit?}` — set React textarea value via `execCommand('insertText')`; works reliably across multiple tasks
- **`ui.send_message`** `{text, images?, files?, responseType?}` — send chat message bypassing the textarea entirely (via gRPC postMessage)
- **`ui.command_palette`** `{command}` — run VSCode command
## Typical Session
```bash
# 1. Launch
curl localhost:19229/api -d '{"method":"launch","params":{"skipBuild":true}}'
# 2. Open sidebar + dismiss overlays (ALWAYS do this first)
curl localhost:19229/api -d '{"method":"ui.open_sidebar"}'
curl localhost:19229/api -d '{"method":"web.evaluate","params":{"expression":"document.querySelectorAll(\".sr-only\").forEach(el => el.parentElement?.click())"}}'
# 3. Navigate to view
curl localhost:19229/api -d '{"method":"ui.command_palette","params":{"command":"cline.accountButtonClicked"}}'
# 4. Check captured OAuth URLs if testing auth
curl localhost:19229/api -d '{"method":"oauth.captured_urls"}'
# 5. Verify
curl localhost:19229/api -d '{"method":"ui.screenshot"}'
```
## Caveats
- **⚠️ Dismiss promotional overlays FIRST**: On fresh launches, full-screen promo overlays block the sidebar. **Dismiss immediately after `ui.open_sidebar`**, before any other interaction or screenshot. May need to run twice:
```bash
curl localhost:19229/api -d '{"method": "ui.open_sidebar"}'
curl localhost:19229/api -d '{"method": "web.evaluate", "params": {"expression": "document.querySelectorAll(\".sr-only\").forEach(el => el.parentElement?.click())"}}'
```
- **Screenshots — don't open the file**: `ui.screenshot` and `ui.sidebar_screenshot` save PNGs to `/tmp/cline-debug/` and return the `{path}`. Use `read_file` on that path to examine screenshots. Running `open <path>` launches Preview.app on macOS which covers the VSCode window.
- **Scripts count = 0 after launch**: CDP connects after extension host starts, so scripts parsed during startup aren't tracked. Breakpoints still work via sourcemap resolution.
- **Port 9230**: Extension host inspector. If another VSCode instance uses this port, the harness will fail to connect. Kill other debug instances first.
- **macOS only** for now (Playwright Electron launch behavior).
- **Webview CDP**: `connect_webview` may fail depending on Electron version. `web.evaluate` still works via Playwright's `frame.evaluate()` fallback.
- **Sourcemap paths**: esbuild outputs relative paths like `../src/extension.ts` in the sourcemap. The resolver handles this, but if a file isn't found, use `ext.source_files` to see exact paths.
- **OAuth with fake codes**: Browser capture intercepts the URL but doesn't provide a valid auth code. For real OAuth testing, open the captured URL in a browser. For unit testing, mock the token exchange.
See `src/dev/debug-harness/README.md` for full API reference.
+103 -103
View File
@@ -13,11 +13,57 @@ This file is the secret sauce for working effectively in this codebase. It captu
**What NOT to add:** Stuff you can figure out from reading a few files, obvious patterns, or standard practices. This file should be high-signal, not comprehensive.
## 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`).
- The whole repo (including `apps/vscode`) uses **bun** for package management and task running. Emit `bun run X` / `bun install` / `bunx <bin>` / `bun file.ts`, never npm/npx. Node remains the *runtime* (VS Code's extension host and the standalone cline-core are Node), so Node-runtime tokens are legitimate and must not be "fixed" to bun — see @.clinerules/bun-and-node.md for the keep-list vs rewrite-list.
- Avoid provider-specific string matching / hardcoded provider branches when fixing provider/config plumbing. Prefer provider metadata, shared catalog/defaults, explicit protocol/client capabilities, or centralized normalization utilities that apply by data shape rather than `providerId === "..."`. If a provider exception seems necessary, stop and explain why instead of adding ad-hoc string matching.
- This is a VS Code extension—check `package.json` for available scripts before trying to verify builds (e.g., `bun run compile`, not `bun run build`).
- When reading a configuration files that users may edit, use `readFileStrippingUtf8Bom`, `readFileSyncStrippingUtf8Bom`, or `stripUtf8Bom` from `@cline/shared/node`. DON'T strip byte order marks of user files handled by tools/passed to models.
- When creating PRs, contributors should not create changelog-entry files. Maintainers handle release versioning and changelog curation during the release process.
- 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
## Searching the Codebase — Avoiding Build Output
Several directories contain build output or generated code that produces
noisy or unusable results with `search_files` / `grep`:
| Directory | What it is | Why it's a problem |
|-----------|-----------|-------------------|
| `out/` | esbuild bundle output | Mirrors `src/` structure as minified JS — every search gets duplicate hits on single-line files |
| `dist/` | Packaged extension | Entire extension bundled into one minified `extension.js` (~1 long line) |
| `dist-standalone/` | Standalone build output | Same minification issue |
| `src/generated/` | Generated protobuf code | Auto-generated from `proto/`; not the source of truth |
| `src/shared/proto/` | Generated proto type defs | Auto-generated from `proto/`; not the source of truth |
| `node_modules/` | Dependencies | Huge, not project source |
### How to skip build output
**`search_files`** — Point at `src/` (not the project root) and use `file_pattern`:
```
search_files(path="src/core", regex="myFunction", file_pattern="*.ts")
```
The `file_pattern` parameter is the most effective filter — e.g. `"*.ts"`,
`"*.tsx"`, `"*.proto"`.
**`grep` directly** — Exclude build dirs and restrict to source extensions:
```bash
grep -rn "myFunction" src/ --include="*.ts" --exclude-dir={out,dist,node_modules,generated}
```
### When you must search minified files
Sometimes you need to verify what got bundled (e.g., checking if a change
made it into the build). Minified files are typically one long line, so
normal `grep` shows the entire file as context. Use these approaches:
- **`grep -oP`** to extract just the match with limited surrounding context:
```bash
grep -oP '.{0,40}myFunction.{0,40}' dist/extension.js
```
- **`read_file`** on files in `out/src/` — these have source maps and are
more readable than `dist/extension.js` (which is the fully bundled output).
- **Source maps** — `out/src/*.js.map` and `dist/extension.js.map` can be
used to trace minified output back to original source locations.
## gRPC/Protobuf Communication
The extension and webview communicate via gRPC-like protocol over VS Code message passing.
@@ -28,7 +74,7 @@ The extension and webview communicate via gRPC-like protocol over VS Code messag
- Naming: Services `PascalCaseService`, RPCs `camelCase`, Messages `PascalCase`
- For streaming responses, use `stream` keyword (see `subscribeToAuthCallback` in `account.proto`)
**Run `npm run protos`** after any proto changes—generates types in:
**Run `bun run protos`** after any proto changes—generates types in:
- `src/shared/proto/` - Shared type definitions
- `src/generated/grpc-js/` - Service implementations
- `src/generated/nice-grpc/` - Promise-based clients
@@ -48,104 +94,15 @@ The extension and webview communicate via gRPC-like protocol over VS Code messag
- `src/core/controller/task/explainChanges.ts` - Handler implementation
- `webview-ui/src/components/chat/ChatRow.tsx` - UI rendering
## Adding a New API Provider
When adding a new provider (e.g., "openai-codex"), you must update the proto conversion layer in THREE places or the provider will silently reset to Anthropic:
1. `proto/cline/models.proto` - Add to the `ApiProvider` enum (e.g., `OPENAI_CODEX = 40;`)
2. `convertApiProviderToProto()` in `src/shared/proto-conversions/models/api-configuration-conversion.ts` - Add case mapping string to proto enum
3. `convertProtoToApiProvider()` in the same file - Add case mapping proto enum back to string
**Why this matters:** Without these, the provider string hits the `default` case and returns `ANTHROPIC`. The webview, provider list, and handler all work fine, but the state silently resets when it round-trips through proto serialization. No error is thrown.
**Other files to update when adding a provider:**
- `src/shared/api.ts` - Add to `ApiProvider` union type, define models
- `src/shared/providers/providers.json` - Add to provider list for dropdown
- `src/core/api/index.ts` - Register handler in `createHandlerForProvider()`
- `webview-ui/src/components/settings/utils/providerUtils.ts` - Add cases in `getModelsForProvider()` and `normalizeApiConfiguration()`
- `webview-ui/src/utils/validate.ts` - Add validation case
- `webview-ui/src/components/settings/ApiOptions.tsx` - Render provider component
## Responses API Providers (OpenAI Codex, OpenAI Native)
Providers using OpenAI's Responses API require native tool calling. XML tools don't work with the Responses API.
**Symptoms of broken native tool calling:**
- Tools get called multiple times (e.g., `ask_followup_question` asks the same question twice)
- Tool arguments get duplicated or malformed
- The model responds but tools aren't recognized
**Root causes to check:**
1. **Provider missing from `isNextGenModelProvider()`** in `src/utils/model-utils.ts`. The native variant matchers (e.g., `native-gpt-5/config.ts`) call this function. If your provider isn't in the list, the matcher returns false and falls back to XML tools.
2. **Model missing `apiFormat: ApiFormat.OPENAI_RESPONSES`** in its model info (`src/shared/api.ts`). This property signals that the model requires native tool calling. The task runner in `src/core/task/index.ts` checks this and forces `enableNativeToolCalls: true` regardless of user settings.
**When adding a new Responses API provider:**
1. Add provider to `isNextGenModelProvider()` list in `src/utils/model-utils.ts`
2. Set `apiFormat: ApiFormat.OPENAI_RESPONSES` on all models that use the Responses API
3. The variant matcher and task runner will handle the rest automatically
## Adding Tools to System Prompt
This is tricky—multiple prompt variants and configs. **Always search for existing similar tools first and follow their pattern.** Look at the full chain from prompt definition → variant configs → handler → UI before implementing.
1. **Add to `ClineDefaultTool` enum** in `src/shared/tools.ts`
2. **Tool definition** in `src/core/prompts/system-prompt/tools/` (create file like `generate_explanation.ts`)
- Define variants for each `ModelFamily` (generic, next-gen, xs, etc.)
- Export variants array (e.g., `export const my_tool_variants = [GENERIC, NATIVE_NEXT_GEN, XS]`)
- **Fallback behavior**: If a variant isn't defined for a model family, `ClineToolSet.getToolByNameWithFallback()` automatically falls back to GENERIC. So you only need to export `[GENERIC]` unless the tool needs model-specific behavior.
3. **Register in `src/core/prompts/system-prompt/tools/init.ts`** - Import and spread into `allToolVariants`
4. **Add to variant configs** - Each model family has its own config in `src/core/prompts/system-prompt/variants/*/config.ts`. Add your tool's enum to the `.tools()` list:
- `generic/config.ts`, `next-gen/config.ts`, `gpt-5/config.ts`, `native-gpt-5/config.ts`, `native-gpt-5-1/config.ts`, `native-next-gen/config.ts`, `gemini-3/config.ts`, `glm/config.ts`, `hermes/config.ts`, `xs/config.ts`
- **Important**: If you add to a variant's config, make sure the tool spec exports a variant for that ModelFamily (or relies on GENERIC fallback)
5. **Create handler** in `src/core/task/tools/handlers/`
6. **Wire up in `ToolExecutor.ts`** if needed for execution flow
7. **Add to tool parsing** in `src/core/assistant-message/index.ts` if needed
8. **If tool has UI feedback**: add `ClineSay` enum in proto, update `src/shared/ExtensionMessage.ts`, update `src/shared/proto-conversions/cline-message.ts`, update `webview-ui/src/components/chat/ChatRow.tsx`
## Modifying System Prompt
**Read these first:** `src/core/prompts/system-prompt/README.md`, `tools/README.md`, `__tests__/README.md`
System prompt is modular: **components** (reusable sections) + **variants** (model-specific configs) + **templates** (with `{{PLACEHOLDER}}` resolution).
**Key directories:**
- `components/` - Shared sections: `rules.ts`, `capabilities.ts`, `editing_files.ts`, etc.
- `variants/` - Model-specific: `generic/`, `next-gen/`, `xs/`, `gpt-5/`, `gemini-3/`, `hermes/`, `glm/`, etc.
- `templates/` - Template engine and placeholder definitions
**Variant tiers (ask user which to modify):**
- **Next-gen** (Claude 4, GPT-5, Gemini 2.5): `next-gen/`, `native-next-gen/`, `native-gpt-5/`, `native-gpt-5-1/`, `gemini-3/`, `gpt-5/`
- **Standard** (default fallback): `generic/`
- **Local/small models**: `xs/`, `hermes/`, `glm/`
**How overrides work:** Variants can override components via `componentOverrides` in their `config.ts`, or provide a custom template in `template.ts` (e.g., `next-gen/template.ts` exports `rules_template`). If no override, the shared component from `components/` is used.
**Example: Adding a rule to RULES section**
1. Check if variant overrides rules: look for `rules_template` in `variants/*/template.ts` or `componentOverrides.RULES` in `config.ts`
2. If shared: modify `components/rules.ts`
3. If overridden: modify that variant's template
4. XS variant is special—has heavily condensed inline content in `template.ts`
**After any changes, regenerate snapshots:**
```bash
UPDATE_SNAPSHOTS=true npm run test:unit
```
Snapshots live in `__tests__/__snapshots__/`. Tests validate across model families and context variations (browser, MCP, focus chain).
## Modifying Default Slash Commands
Three places need updates:
- `src/core/slash-commands/index.ts` - Command definitions
- `src/core/prompts/commands.ts` - System prompt integration
- `webview-ui/src/utils/slash-commands.ts` - Webview autocomplete
## Adding New Global State Keys
Adding a new key to global state requires updates in multiple places. Missing any step causes silent failures.
Required steps:
1. Type definition in `src/shared/storage/state-keys.ts` - Add to `GlobalState` or `Settings` interface
2. Read from globalState in `src/core/storage/utils/state-helpers.ts`:
- Add `const myKey = context.globalState.get<GlobalStateAndSettings["myKey"]>("myKey")` in `readGlobalStateFromDisk()`
- Add to the return object: `myKey: myKey ?? defaultValue,`
3. StateManager handles read/write via `setGlobalState()`/`getGlobalStateKey()` after initialization
2. Add any default value or transform in `src/shared/storage/state-keys.ts` if the key needs one
3. Read and write the value through `StateManager` (`setGlobalState()` / `getGlobalStateKey()`) after initialization
Common mistake: Adding only the return value without the `context.globalState.get()` call. This compiles but the value is always `undefined` on load.
Persistent state is file-backed through `StateManager`; do not add new runtime reads or writes against VS Code `ExtensionContext` storage. That storage is only a legacy migration source.
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(...)`
@@ -153,28 +110,26 @@ Settings plumbing gotcha: if a key is user-toggleable from settings, wire both c
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`
- Add the field to `UpdateSettingsRequest` in `proto/cline/state.proto` (for webview update requests), then run `bun 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()`.
StateManager uses an in-memory cache populated during `StateManager.initialize()` from file-backed storage. For most state, use `controller.stateManager.setGlobalState()`/`getGlobalStateKey()`.
Exception: State needed immediately at extension startup (before cache is ready)
Exception: host migration code may read legacy VS Code storage before file-backed storage is initialized.
When Window A sets state and immediately opens Window B, the new window's StateManager cache is populated from `context.globalState` during initialization. If you need to read state in Window B right at startup (e.g., in `common.ts` during `initialize()`), read directly from `context.globalState.get()` instead of StateManager's cache.
Example pattern (see `lastShownAnnouncementId` and `worktreeAutoOpenPath`):
Example pattern:
```typescript
// Writing (normal pattern)
controller.stateManager.setGlobalState("myKey", value)
// Reading at startup in common.ts (bypass cache)
const value = context.globalState.get<string>("myKey")
// Reading after initialization
const value = controller.stateManager.getGlobalStateKey("myKey")
```
This is only needed for cross-window state read during the brief startup window before StateManager cache is fully usable. Normal state access after initialization should use StateManager.
Use `context.globalState` only in VS Code migration code that copies legacy ExtensionContext values into the shared file-backed stores.
## ChatRow Cancelled/Interrupted States
When a ChatRow displays a loading/in-progress state (spinner), you must handle what happens when the task is cancelled. This is non-obvious because cancellation doesn't update the message content—you have to infer it from context.
@@ -203,3 +158,48 @@ const isGenerating = explanationInfo.status === "generating" && !wasCancelled
**See also:** `BrowserSessionRow.tsx` uses similar pattern with `isLastApiReqInterrupted` and `isLastMessageResume`.
**Backend side:** When streaming is cancelled, clean up properly (close tabs, clear comments, etc.) by checking `taskState.abort` after the streaming function returns.
## Debug Harness: clear inherited VSCode/Electron env vars before launching
The debug harness (`apps/vscode/src/dev/debug-harness/server.ts`) launches a child
VSCode via Playwright's `_electron.launch({ env: { ...process.env, ... } })`. If you
run the harness from a process that was itself spawned by VSCode (e.g. the Cline
extension host, an integrated terminal, or an agent running inside VSCode), the
parent's VSCode/Electron env vars leak into the child and break the launch.
The fatal one is **`ELECTRON_RUN_AS_NODE=1`**: it makes the child VSCode binary run
as plain Node, so it rejects every VSCode CLI flag. Symptom:
```
.../Visual Studio Code.app/Contents/MacOS/Code: bad option: --extensionDevelopmentPath=...
Error: Process failed to launch! (Playwright _electron.launch)
```
This is NOT the macOS Playwright flakiness mentioned in the harness README — it's
env inheritance. Fix: strip the inherited vars before starting the harness:
```bash
env -u ELECTRON_RUN_AS_NODE -u ELECTRON_NO_ATTACH_CONSOLE \
-u VSCODE_CLI -u VSCODE_CODE_CACHE_PATH -u VSCODE_CRASH_REPORTER_PROCESS_TYPE \
-u VSCODE_CWD -u VSCODE_ESM_ENTRYPOINT -u VSCODE_HANDLES_UNCAUGHT_ERRORS \
-u VSCODE_IPC_HOOK -u VSCODE_NLS_CONFIG -u VSCODE_PID -u VSCODE_L10N_BUNDLE_LOCATION \
bun src/dev/debug-harness/server.ts --auto-launch --skip-build
```
Check your own env with `env | grep -iE 'electron|vscode_'` first; `ELECTRON_RUN_AS_NODE=1`
present means you must scrub before launching.
Other harness notes confirmed in practice:
- The extension host is **ESM** (`VSCODE_ESM_ENTRYPOINT`), so `ext.evaluate` has no
`require` and module-internal functions aren't reachable as globals. To inspect
internal builders (e.g. `buildBedrockProviderConfig`), set a breakpoint with
`ext.set_breakpoint` and read locals via `ext.evaluate` with the paused `callFrameId`
— don't try to `require()` the bundle.
- `web.evaluate` wraps the expression as a single returned expression; multi-statement
snippets must be an IIFE `(() => { ...; return x; })()`, otherwise you get
`SyntaxError: Unexpected token ';'`.
- Webview settings inputs are `vscode-text-field` web components with debounced React
onChange. Setting `.value` + dispatching events via `web.evaluate` is unreliable for
some fields; focus the inner shadow `input` then use real keystrokes (`ui.type` +
`ui.press Tab`, or click the dropdown option) to make the value persist.
+1 -1
View File
@@ -42,7 +42,7 @@ Here, we use the common `StringRequest` and `KeyValuePair` types.
After editing a `.proto` file, regenerate the TypeScript code. From the project root, run:
```bash
npm run protos
bun run protos
```
This command compiles all `.proto` files and outputs the generated code to `src/generated/` and `src/shared/`. Do not edit these generated files manually.
+26
View File
@@ -0,0 +1,26 @@
# SDK Adapter
The VSCode extension runs on the Cline SDK (`@cline/core`, `@cline/llms`,
`@cline/shared`) through an adapter layer in `apps/vscode/src/sdk/`. The
webview still talks gRPC; the adapter translates between gRPC handlers and SDK
calls. See `apps/vscode/src/dev/debug-harness/README.md` for the debug harness.
## Conventions
1. **Look up SDK APIs, don't guess.** Use `kb_search(name="sdk", query="...")`
before implementing against an SDK surface.
2. **Reference the pre-SDK implementation when replacing a module.** Add a
`// Replaces classic src/core/... (see origin/main)` header and use
`kb_search(name="cline", commit="origin/main")` or
`git show origin/main:path` to consult the prior implementation.
3. **Single entry point.** There is one codepath — the SDK adapter. No
`CLINE_SDK` env flag.
4. **Use `{appBaseUrl}`**, never hardcode `app.cline.bot`.
5. **Avoid `as` casts.** Use explicit conversion functions with tests. The
branded types in `apps/vscode/src/sdk/model-catalog/contracts.ts` exist so
casts are unnecessary outside parse/compute boundaries.
## Debug harness
- **Dismiss the Kanban/promo overlay** before any debug harness interaction.
- **Use the command palette** to navigate tabs in the debug harness.
+2 -2
View File
@@ -91,7 +91,7 @@ On the main branch, create a commit that updates:
3. No changelog-entry file cleanup is needed. Contributors do not create changelog-entry files in this repo.
**Skip running `npm run install:all`** - release automation handles lockfile consistency as needed.
**No dependency install is needed.** A CHANGELOG + `version` bump does not change any dependency, and `bun.lock` does not pin workspace-package versions, so the lockfile stays consistent. The publish workflow runs `bun install --frozen-lockfile`, which would *fail* on an out-of-sync lock — so only run `bun install` here if you actually change dependencies (then commit the updated `bun.lock`).
Commit with message format: `v{VERSION} Release Notes (hotfix)`
@@ -176,7 +176,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/publish.yml (paste `v{VERSION}` as the tag)
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)
2. Post the Slack message to announce the hotfix
## Important Notes
+1 -1
View File
@@ -43,7 +43,7 @@ git push origin v<version>
### 4) Trigger publish workflow
Tell the maintainer to run:
https://github.com/cline/cline/actions/workflows/publish.yml
https://github.com/cline/cline/actions/workflows/ext-vscode-publish-stable.yml
Use `v<version>` as the release tag.
+3 -2
View File
@@ -20,8 +20,9 @@ command = "chmod +x ./scripts/run-extension-host.sh && ./scripts/run-extension-h
name = "CLI"
icon = "run"
command = '''
npm run cli:build
npm run cli:run
cd sdk
bun install
bun run cli
'''
[[actions]]
-2
View File
@@ -1,6 +1,4 @@
demo.gif filter=lfs diff=lfs merge=lfs -text
assets/docs/demo.gif filter=lfs diff=lfs merge=lfs -text
webview-ui/src/assets/cline_kanban_demo.mp4 filter=lfs diff=lfs merge=lfs -text
webview-ui/src/assets/cline_kanban_demo.webm filter=lfs diff=lfs merge=lfs -text
* text=auto eol=lf
+1 -1
View File
@@ -1,2 +1,2 @@
/.github/ @saoudrizwan @arafatkatze @maxpaulus43 @candieduniverse
/.github/ @saoudrizwan @arafatkatze @maxpaulus43 @dominiccooney
/README.md @saoudrizwan @juanpflores
+15 -3
View File
@@ -7,10 +7,10 @@ body:
value: |
**Important:** All bug reports must be reproducible using Claude Sonnet 4.5. Cline uses complex prompts so less capable models may not work as expected.
- type: dropdown
id: plugin-type
id: cline-surface
attributes:
label: Plugin Type
description: Which plugin are you reporting a bug for?
label: Cline Surface
description: Which Cline surface are you reporting a bug for?
options:
- VSCode Extension
- JetBrains Plugin
@@ -59,6 +59,18 @@ body:
placeholder: 'e.g., cline:anthropic/claude-sonnet-4.5, gemini:gemini-2.5-pro-exp-03-25'
validations:
required: false
- type: textarea
id: ide-diagnostics
attributes:
label: IDE / CLI Diagnostics
description: |
Paste the "About" diagnostics for your Cline surface. This captures the IDE build, runtime, and host details we need.
- VSCode Extension: open `Help → About` (Windows/Linux) or `Code → About Visual Studio Code` (macOS), then copy the info.
- JetBrains Plugin: open `Help → About` (Windows/Linux) or `<IDE name> → About` (macOS), then click `Copy` to grab build, runtime, OS, memory, and cores.
- CLI: there is no About dialog. Run `cline --version` and paste the output.
placeholder: Paste the copied About info or `cline --version` output here.
validations:
required: false
- type: textarea
id: system-info
attributes:
+8 -9
View File
@@ -5,19 +5,18 @@ This is a VS Code extension. Read `.clinerules/general.md` for tribal knowledge
## 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.
- **CLI** (`cli/`): React Ink terminal UI sharing core logic. Update CLI when changing webview features.
- **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`.
- **Build**: `bun run compile` — NOT `bun run build`.
- **Watch**: `bun run watch` (extension + webview).
- **Protos**: `bun run protos` — run **immediately** after any `.proto` change. Generates into `src/shared/proto/`, `src/generated/`.
- **Tests**: `bun run test:unit`. After prompt/tool changes: `UPDATE_SNAPSHOTS=true bun 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`.
2. **Generate**: `bun 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`.
@@ -28,7 +27,7 @@ Three proto conversion updates are **required** or the provider silently resets
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`, and `cli/src/components/ModelPicker.tsx`.
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.
@@ -39,13 +38,13 @@ For Responses API providers: add to `isNextGenModelProvider()` in `src/utils/mod
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`.
7. Regenerate snapshots: `UPDATE_SNAPSHOTS=true bun 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`.
Adding a key requires updating the typed storage definitions in `src/shared/storage/state-keys.ts`; runtime reads and writes should go through `StateManager`, not VS Code `ExtensionContext` storage. Persistent state is file-backed so it works across VS Code, CLI, and JetBrains hosts.
## Slash Commands (3 places)
- `src/core/slash-commands/index.ts` — definitions.
+2 -2
View File
@@ -2,7 +2,7 @@ version: 2
updates:
# Main extension dependencies
- package-ecosystem: "npm"
directory: "/"
directory: "/apps/vscode"
schedule:
interval: "weekly"
# Group all updates into a single PR
@@ -20,7 +20,7 @@ updates:
# Webview UI dependencies
- package-ecosystem: "npm"
directory: "/webview-ui"
directory: "/apps/vscode/webview-ui"
schedule:
interval: "weekly"
groups:
+1 -1
View File
@@ -59,7 +59,7 @@ We're not looking for exhaustive documentation - just evidence that you've thoug
<!-- Put an 'x' in all boxes that apply -->
- [ ] 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`)
- [ ] Tests are passing (`bun test`) and code is formatted and linted (`bun run format && bun run lint`)
- [ ] I have reviewed [contributor guidelines](https://github.com/cline/cline/blob/main/CONTRIBUTING.md)
### Screenshots
+435
View File
@@ -0,0 +1,435 @@
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"
@@ -1,78 +0,0 @@
name: Smoke Tests
# Temporarily disabled: this workflow built and linked the legacy CLI
# (`cd cli && npm install && npm run build && npm link`) before running the
# smoke-test scenarios. The legacy CLI publish chain has been retired in
# favor of the SDK CLI at `sdk/apps/cli/`. The scenarios under
# `evals/smoke-tests/scenarios/` are CLI-agnostic and should be re-enabled
# once the build step is repointed at the new SDK CLI. Until then, only
# manual `workflow_dispatch` runs are accepted (and will fail in their
# current form).
on:
workflow_dispatch:
permissions:
contents: read
concurrency:
group: smoke-tests-${{ github.ref }}
cancel-in-progress: true
jobs:
smoke-tests:
name: Smoke Tests
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '22'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Build and install CLI
run: |
npm run protos
cd cli && npm install && npm run build && npm link
echo "$(npm config get prefix)/bin" >> $GITHUB_PATH
- name: Verify CLI
run: cline --version
- name: Run smoke tests
env:
CLINE_API_KEY: ${{ secrets.CLINE_API_KEY }}
run: |
cline auth -p cline -k "$CLINE_API_KEY" -m "anthropic/claude-sonnet-4.5"
max_attempts=3
for attempt in $(seq 1 $max_attempts); do
echo "::group::Attempt $attempt of $max_attempts"
if npx tsx evals/smoke-tests/run-smoke-tests.ts --trials 1 --parallel; then
echo "::endgroup::"
echo "Smoke tests passed on attempt $attempt"
exit 0
fi
echo "::endgroup::"
if [ $attempt -lt $max_attempts ]; then
echo "::warning::Smoke tests failed on attempt $attempt, retrying..."
sleep 10
fi
done
echo "::error::Smoke tests failed after $max_attempts attempts"
exit 1
- name: Generate summary
if: always()
run: cat evals/smoke-tests/results/latest/summary.md >> $GITHUB_STEP_SUMMARY
- name: Upload results
uses: actions/upload-artifact@v4
if: always()
with:
name: smoke-test-results-${{ github.run_id }}
path: evals/smoke-tests/results/latest/
retention-days: 30
-111
View File
@@ -1,111 +0,0 @@
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
run: npm ci
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Install webview-ui dependencies
run: cd webview-ui && npm 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/
@@ -0,0 +1,100 @@
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"
+205
View File
@@ -0,0 +1,205 @@
name: ext-vscode-ab-package
# Build (and optionally publish) the combined A/B VSIX: a tiny loader plus two
# complete extension bundles — `next/` from the SDK-based apps/vscode on main,
# `legacy/` from the legacy-extension branch. Cohort selection happens at
# runtime via PostHog flags; see apps/vscode-rollout/README.md for the design
# and the rollout runbook.
on:
workflow_dispatch:
inputs:
version:
description: "Combined VSIX version — must exceed every previously published version (e.g. 4.1.0)"
required: true
type: string
next-ref:
description: "Ref to build the next (SDK) bundle from"
required: true
default: "main"
type: string
legacy-ref:
description: "Ref to build the legacy bundle from"
required: true
default: "legacy-extension"
type: string
publish:
description: "Publish to the VS Code Marketplace (unchecked: just build the .vsix artifact)"
required: true
default: false
type: boolean
permissions:
contents: read
concurrency:
group: ext-vscode-ab-package-${{ github.event.inputs.version }}
cancel-in-progress: false
jobs:
package:
name: Build combined (legacy + next) VSIX
runs-on: ubuntu-latest
environment: publish
steps:
- name: Checkout next (SDK) source
uses: actions/checkout@v4
with:
ref: ${{ github.event.inputs.next-ref }}
path: next-src
lfs: true
- name: Checkout legacy source
uses: actions/checkout@v4
with:
ref: ${{ github.event.inputs.legacy-ref }}
path: legacy-src
lfs: true
- uses: oven-sh/setup-bun@v2
with:
bun-version: 1.3.14
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 22
- name: Install next workspace dependencies
working-directory: next-src
run: bun install
# @cline/* are local workspace symlinks to source packages; apps/vscode's
# `package` script does NOT build them, so without this the esbuild step
# fails on a fresh checkout. (The nightly workflow already does this.)
- name: Build SDK packages
working-directory: next-src
run: bun run build:sdk
# Stamp the combined version into each bundle's package.json AFTER
# install and BEFORE its build: the About tab and telemetry
# extension_version read the bundle's own manifest, so without this
# the VSIX reports three different versions depending on where you
# look. (The nightly workflow gets the same alignment via nightlify.mjs.)
- name: Align next bundle version
working-directory: next-src/apps/vscode-rollout
run: node scripts/set-version.mjs --dir "$GITHUB_WORKSPACE/next-src/apps/vscode" --version "${{ github.event.inputs.version }}"
- name: Build next bundle
working-directory: next-src/apps/vscode
env:
CLINE_ENVIRONMENT: production
TELEMETRY_SERVICE_API_KEY: ${{ secrets.TELEMETRY_SERVICE_API_KEY }}
ERROR_SERVICE_API_KEY: ${{ secrets.ERROR_SERVICE_API_KEY }}
# Inlined by esbuild: attributes every telemetry event with
# extension_variant and unlocks the bundle's authoritative
# extension.rollout.bundle_activated capture. Rollout builds only.
CLINE_ROLLOUT_VARIANT: next
# Match the stable publish workflow's OpenTelemetry production defaults.
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: bun run package
- name: Install legacy dependencies
working-directory: legacy-src
run: |
npm --prefix apps/vscode install --include=optional
npm --prefix apps/vscode/webview-ui install --include=optional
- name: Align legacy bundle version
working-directory: next-src/apps/vscode-rollout
run: node scripts/set-version.mjs --dir "$GITHUB_WORKSPACE/legacy-src/apps/vscode" --version "${{ github.event.inputs.version }}"
- name: Build legacy bundle
working-directory: legacy-src/apps/vscode
env:
CLINE_ENVIRONMENT: production
TELEMETRY_SERVICE_API_KEY: ${{ secrets.TELEMETRY_SERVICE_API_KEY }}
ERROR_SERVICE_API_KEY: ${{ secrets.ERROR_SERVICE_API_KEY }}
CLINE_ROLLOUT_VARIANT: legacy
# Match the stable publish workflow's OpenTelemetry production defaults.
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 package
- name: Build loader and run rollout tests
working-directory: next-src/apps/vscode-rollout
env:
TELEMETRY_SERVICE_API_KEY: ${{ secrets.TELEMETRY_SERVICE_API_KEY }}
run: |
bun run typecheck
bun run test
bun run build:production
- name: Stitch combined VSIX staging
working-directory: next-src/apps/vscode-rollout
run: |
node scripts/stitch.mjs \
--next "$GITHUB_WORKSPACE/next-src/apps/vscode" \
--legacy "$GITHUB_WORKSPACE/legacy-src/apps/vscode" \
--loader dist/extension.js \
--version "${{ github.event.inputs.version }}" \
--out "$GITHUB_WORKSPACE/staging"
- name: Smoke-test loader against staging
working-directory: next-src/apps/vscode-rollout
run: node scripts/smoke-loader.mjs "$GITHUB_WORKSPACE/staging"
# This workflow publishes the STABLE identity. If nightlify ever leaks
# into this path the union manifest would ship under the wrong name.
# The bundle sub-manifest checks guard the set-version.mjs stamping:
# the About tab and telemetry extension_version read those files.
- name: Assert stable manifest identity
working-directory: staging
env:
EXPECTED_VERSION: ${{ github.event.inputs.version }}
run: |
node -e '
const assert = require("node:assert");
const expected = process.env.EXPECTED_VERSION;
const pkg = require("./package.json");
assert.equal(pkg.name, "claude-dev", `unexpected name ${pkg.name}`);
assert.equal(pkg.publisher, "saoudrizwan", `unexpected publisher ${pkg.publisher}`);
assert.equal(pkg.version, expected, `unexpected union version ${pkg.version}`);
for (const bundle of ["next", "legacy"]) {
const sub = require(`./${bundle}/package.json`);
assert.equal(sub.version, expected, `unexpected ${bundle} bundle version ${sub.version}`);
}
console.log(`stable identity ok: ${pkg.publisher}.${pkg.name}@${pkg.version} (bundle versions aligned)`);
'
- name: Package VSIX
working-directory: staging
run: |
npm install -g @vscode/vsce
# Preserve the narrowly scoped VSCE `sendgrid` scanner exemption used by
# both standalone bundle workflows. No SendGrid credential is intentionally
# supplied here; inspect the reported artifact before widening the exemption.
vsce package --no-dependencies --allow-package-secrets sendgrid --out "claude-dev-${{ github.event.inputs.version }}.vsix"
- name: Upload VSIX artifact
uses: actions/upload-artifact@v4
with:
name: claude-dev-${{ github.event.inputs.version }}
path: staging/claude-dev-${{ github.event.inputs.version }}.vsix
if-no-files-found: error
- name: Publish to Marketplace
if: ${{ github.event.inputs.publish == 'true' }}
working-directory: staging
env:
VSCE_PAT: ${{ secrets.VSCE_PAT }}
run: |
if [[ -z "$VSCE_PAT" ]]; then
echo "Error: VSCE_PAT is required to publish."
exit 1
fi
vsce publish --no-dependencies --packagePath "claude-dev-${{ github.event.inputs.version }}.vsix"
@@ -0,0 +1,294 @@
name: ext-vscode-publish-legacy
# Publishes the legacy (pre-SDK-migration) VS Code extension from the
# `legacy-extension` branch. This branch holds the npm-based 3.89.x codebase,
# rolled forward under a 4.0.x version so existing 4.0.0 users still receive
# the update. The main `ext-vscode-publish-stable.yml` workflow (bun-based)
# stays the path for releasing main once the SDK migration is solid.
#
# This workflow lives on and is dispatched from `main` (so it satisfies the
# default-branch dispatch requirement), but it checks out and builds the
# `legacy-extension` branch.
on:
workflow_dispatch:
inputs:
release-type:
description: "Choose release type (release or pre-release)"
required: true
default: "release"
type: choice
options:
- pre-release
- release
branch:
description: "Branch holding the legacy extension code"
required: true
default: "legacy-extension"
type: string
permissions:
contents: write
packages: write
checks: write
pull-requests: write
concurrency:
group: ext-vscode-publish-legacy-${{ github.event.inputs.branch }}
cancel-in-progress: false
jobs:
# Gate the publish on the legacy branch's own npm-based test suite. We can't
# reuse ./.github/workflows/ext-vscode-test.yml here — on main that's the
# bun-based suite and it would test main, not the legacy branch — so the
# essential quality + test steps are inlined against the checked-out legacy
# branch.
test:
name: Test Legacy Extension
runs-on: ubuntu-latest
defaults:
run:
working-directory: apps/vscode
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.inputs.branch }}
- name: Setup Node.js
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 (lint + typecheck)
run: npm run ci:check-all
- name: Build Tests and Extension
id: build_step
run: npm run ci:build
- name: Unit Tests
if: ${{ !cancelled() && steps.build_step.outcome == 'success' }}
run: npm run test:unit
- name: Extension Integration Tests
if: ${{ !cancelled() && steps.build_step.outcome == 'success' }}
run: xvfb-run -a npm run test:coverage
- name: Webview Tests
if: ${{ !cancelled() && steps.build_step.outcome == 'success' }}
run: |
cd webview-ui
npm run test:coverage
publish:
needs: test
name: Publish Legacy Extension
runs-on: ubuntu-latest
environment: publish
defaults:
run:
working-directory: apps/vscode
steps:
# Check out the legacy branch (NOT main). fetch-depth: 0 + tags so we
# can create/push the release tag and compute the previous tag.
- uses: actions/checkout@v4
with:
ref: ${{ github.event.inputs.branch }}
fetch-depth: 0
fetch-tags: true
lfs: true
- name: Resolve Release Tag
id: resolve_tag
working-directory: ${{ github.workspace }}
env:
BRANCH: ${{ github.event.inputs.branch }}
run: |
# Tag is derived from the package version on the legacy branch.
VERSION=$(node -p "require('./apps/vscode/package.json').version")
TAG="v$VERSION"
if [[ ! "$TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+([-.][0-9A-Za-z.]+)?$ ]]; then
echo "Error: derived tag '$TAG' does not match vX.Y.Z"
exit 1
fi
TAG_REF="refs/tags/$TAG"
HEAD_SHA=$(git rev-parse HEAD)
if git show-ref --verify --quiet "$TAG_REF"; then
TAG_SHA=$(git rev-list -n 1 "$TAG_REF^{commit}")
if [[ "$TAG_SHA" != "$HEAD_SHA" ]]; then
echo "Error: tag '$TAG' already exists at $TAG_SHA, not at branch head ($HEAD_SHA)"
exit 1
fi
echo "Tag '$TAG' already exists at branch head. Continuing."
else
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git tag "$TAG" "$HEAD_SHA"
git push origin "$TAG_REF"
echo "Created and pushed tag '$TAG' from $BRANCH head $HEAD_SHA."
fi
echo "tag=$TAG" >> $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: Verify Changelog Entry
working-directory: ${{ github.workspace }}
run: |
EXPECTED_HEADING="## [${{ steps.get_version.outputs.version }}]"
FIRST_HEADING=$(grep -m 1 '^## \[' CHANGELOG.md || true)
if [[ "$FIRST_HEADING" != "$EXPECTED_HEADING" ]]; then
echo "Error: CHANGELOG.md must start with '$EXPECTED_HEADING' before publishing."
echo "Current first release heading: ${FIRST_HEADING:-<none>}"
exit 1
fi
echo "Found changelog entry for ${{ steps.get_version.outputs.version }}"
- name: Verify Marketplace Tokens
env:
VSCE_PAT: ${{ secrets.VSCE_PAT }}
OVSX_PAT: ${{ secrets.OVSX_PAT }}
run: |
if [[ -z "$VSCE_PAT" ]]; then
echo "Error: VSCE_PAT is required to publish the stable VS Code extension."
exit 1
fi
if [[ -z "$OVSX_PAT" ]]; then
echo "Error: OVSX_PAT is required to publish the stable Open VSX extension."
exit 1
fi
echo "Marketplace publish tokens are configured."
- 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 }} (legacy)"
blocks:
- type: "section"
text:
type: "mrkdwn"
text: "*Cline ${{ steps.resolve_tag.outputs.tag }} (legacy)*"
- 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 }}"
@@ -0,0 +1,302 @@
name: ext-vscode-publish-nightly
# Publishes saoudrizwan.cline-nightly as the COMBINED A/B VSIX: the rollout
# loader plus two complete extension bundles — `next/` from this ref's
# apps/vscode (SDK-based) and `legacy/` from the legacy-extension branch.
# Cohort selection happens at runtime via PostHog flags; see
# apps/vscode-rollout/README.md for the design and rollout runbook.
#
# The stable-identity equivalent of this pipeline is ext-vscode-ab-package.yml
# (manual dispatch, publishes claude-dev). Shared logic lives in
# apps/vscode-rollout/scripts (nightlify/gen-manifest/stitch/smoke) so both
# workflows stay thin. The single-bundle nightly path this replaced
# (apps/vscode/scripts/publish-nightly.mjs) remains for manual feature-branch
# pre-release publishes.
on:
schedule:
# Every day at 4:00 AM PST (12:00 UTC)
- cron: "0 12 * * *"
workflow_dispatch:
inputs:
legacy-ref:
description: "Ref to build the legacy bundle from"
required: false
default: "legacy-extension"
type: string
dry-run:
description: "Build and upload the .vsix artifact without publishing or tagging"
required: false
default: false
type: boolean
run-name: "Publish Combined Nightly from ${{ github.ref_name }} @ ${{ github.sha }}"
# Prevent concurrent publish runs on the same branch: the version is generated
# 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.
concurrency:
group: ext-vscode-publish-nightly-${{ github.ref }}
cancel-in-progress: false
permissions: {}
jobs:
test:
if: github.repository == 'cline/cline'
permissions:
contents: read
pull-requests: read
uses: ./.github/workflows/ext-vscode-test.yml
publish:
needs: test
permissions:
contents: write
name: Publish Cline (Nightly) Combined Extension
# Defense in depth: only protected main may enter the publishing environment.
# This `if` is advisory because a dispatched branch runs its own copy of this
# file; the enforced gate is the PublishNightly environment's deployment-branch
# policy, which must also allow only main.
if: github.repository == 'cline/cline' && github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
environment: PublishNightly
steps:
- name: Checkout next (SDK) source
uses: actions/checkout@v4
with:
ref: ${{ github.sha }}
path: next-src
lfs: true
persist-credentials: false
- name: Checkout legacy source
uses: actions/checkout@v4
with:
# NOTE: inputs are empty strings on `schedule` events, so the ||
# fallback (not the input's declared default) is what the cron uses.
ref: ${{ inputs.legacy-ref || 'legacy-extension' }}
path: legacy-src
lfs: true
persist-credentials: false
- name: Show build sources
env:
# Routed through env rather than interpolated into the script body so
# a crafted dispatch input can't inject shell (hygiene: dispatchers
# need write access anyway, but keep the pattern clean).
LEGACY_REF: ${{ inputs.legacy-ref || 'legacy-extension' }}
run: |
echo "next: $(git -C next-src rev-parse HEAD)"
echo "legacy: $(git -C legacy-src rev-parse HEAD) ($LEGACY_REF)"
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: 1.3.14
# Node is required beyond install: the rollout scripts run under node and
# publishing shells out to vsce/ovsx. Pinned to Node 22 because newer LTS
# (Node 24 / npm 11) can make vsce's dependency detection fail.
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 22
# ONE version for the next bundle, the legacy bundle, and the union
# manifest: gen-manifest hard-fails if the bundle identities diverge.
# Same scheme as the standalone nightly: <major>.<minor>.<unix-seconds>
# from next's base version, so it keeps outranking earlier nightlies.
- name: Compute nightly version
id: version
run: |
BASE=$(node -p "require('./next-src/apps/vscode/package.json').version")
VERSION="$(echo "$BASE" | cut -d. -f1,2).$(date +%s)"
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
echo "Combined nightly version: $VERSION (base $BASE)"
- name: Install next workspace dependencies
working-directory: next-src
run: bun install --frozen-lockfile
- name: Build SDK packages
working-directory: next-src
run: bun run build:sdk
- name: Assert better-sqlite3 native binary present
working-directory: next-src/apps/vscode
run: |
NODE_FILE="node_modules/better-sqlite3/build/Release/better_sqlite3.node"
if [ ! -f "$NODE_FILE" ]; then
echo "ERROR: better-sqlite3 native binary missing at apps/vscode/$NODE_FILE"
echo "(bun trustedDependencies postinstall likely did not run)"
exit 1
fi
echo "Found better-sqlite3 native binary: $NODE_FILE"
# Rewrite each bundle's package.json to the cline-nightly identity BEFORE
# its build (runtime command/config IDs derive from the manifest) and
# AFTER dependency install (workspace self-links key off the original
# package name).
- name: Nightlify next bundle manifest
working-directory: next-src/apps/vscode-rollout
run: node scripts/nightlify.mjs --dir "$GITHUB_WORKSPACE/next-src/apps/vscode" --version "${{ steps.version.outputs.version }}"
- name: Build next bundle
working-directory: next-src/apps/vscode
env:
CLINE_ENVIRONMENT: production
TELEMETRY_SERVICE_API_KEY: ${{ secrets.TELEMETRY_SERVICE_API_KEY }}
ERROR_SERVICE_API_KEY: ${{ secrets.ERROR_SERVICE_API_KEY }}
# Inlined by esbuild: attributes every telemetry event with
# extension_variant and unlocks the bundle's authoritative
# extension.rollout.bundle_activated capture. Rollout builds only.
CLINE_ROLLOUT_VARIANT: next
# 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: bun run package
- name: Install legacy dependencies
working-directory: legacy-src
run: |
npm --prefix apps/vscode install --include=optional
npm --prefix apps/vscode/webview-ui install --include=optional
- name: Nightlify legacy bundle manifest
working-directory: next-src/apps/vscode-rollout
run: node scripts/nightlify.mjs --dir "$GITHUB_WORKSPACE/legacy-src/apps/vscode" --version "${{ steps.version.outputs.version }}"
- name: Build legacy bundle
working-directory: legacy-src/apps/vscode
env:
CLINE_ENVIRONMENT: production
TELEMETRY_SERVICE_API_KEY: ${{ secrets.TELEMETRY_SERVICE_API_KEY }}
ERROR_SERVICE_API_KEY: ${{ secrets.ERROR_SERVICE_API_KEY }}
CLINE_ROLLOUT_VARIANT: legacy
# Legacy's esbuild inlines these too (its own publish workflow passes
# them) — omitting them here would ship the legacy bundle with the
# OTel pipeline dead, unlike what legacy users get today.
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 package
- name: Build loader and run rollout tests
working-directory: next-src/apps/vscode-rollout
env:
TELEMETRY_SERVICE_API_KEY: ${{ secrets.TELEMETRY_SERVICE_API_KEY }}
run: |
bun run typecheck
bun run test
bun run build:production
- name: Stitch combined VSIX staging
working-directory: next-src/apps/vscode-rollout
run: |
node scripts/stitch.mjs \
--next "$GITHUB_WORKSPACE/next-src/apps/vscode" \
--legacy "$GITHUB_WORKSPACE/legacy-src/apps/vscode" \
--loader dist/extension.js \
--version "${{ steps.version.outputs.version }}" \
--out "$GITHUB_WORKSPACE/staging"
- name: Smoke-test loader against staging
working-directory: next-src/apps/vscode-rollout
run: node scripts/smoke-loader.mjs "$GITHUB_WORKSPACE/staging"
# The nightly identity must have fully propagated (nightlify -> both
# bundle manifests -> union manifest) or we'd publish over the stable
# extension ID. The bundle sub-manifest checks guard the version
# stamping: the About tab and telemetry extension_version read those.
- name: Assert nightly manifest identity
working-directory: staging
env:
EXPECTED_VERSION: ${{ steps.version.outputs.version }}
run: |
node -e '
const assert = require("node:assert");
const expected = process.env.EXPECTED_VERSION;
const pkg = require("./package.json");
assert.equal(pkg.name, "cline-nightly", `unexpected name ${pkg.name}`);
assert.equal(pkg.publisher, "saoudrizwan", `unexpected publisher ${pkg.publisher}`);
assert.equal(pkg.version, expected, `unexpected union version ${pkg.version}`);
for (const bundle of ["next", "legacy"]) {
const sub = require(`./${bundle}/package.json`);
assert.equal(sub.name, "cline-nightly", `unexpected ${bundle} bundle name ${sub.name}`);
assert.equal(sub.version, expected, `unexpected ${bundle} bundle version ${sub.version}`);
}
console.log(`nightly identity ok: ${pkg.publisher}.${pkg.name}@${pkg.version} (bundle identities aligned)`);
'
- name: Install Publishing Tools
run: npm install -g @vscode/vsce ovsx
- name: Package VSIX
working-directory: staging
# Preserve the narrowly scoped VSCE `sendgrid` scanner exemption used by
# both standalone bundle workflows. No SendGrid credential is intentionally
# supplied here; inspect the reported artifact before widening the exemption.
run: vsce package --no-dependencies --allow-package-secrets sendgrid --out "cline-nightly-${{ steps.version.outputs.version }}.vsix"
- name: Upload VSIX artifact
uses: actions/upload-artifact@v4
with:
name: cline-nightly-${{ steps.version.outputs.version }}
path: staging/cline-nightly-${{ steps.version.outputs.version }}.vsix
if-no-files-found: error
# The job is main-only; step-level dry-run gating still permits a build-only
# rehearsal without publishing or tagging.
- name: Publish to VS Code Marketplace and Open VSX
if: github.ref == 'refs/heads/main' && inputs.dry-run != true
working-directory: staging
env:
VSCE_PAT: ${{ secrets.VSCE_PAT }}
OVSX_PAT: ${{ secrets.OVSX_PAT }}
run: |
if [[ -z "$VSCE_PAT" ]]; then
echo "Error: VSCE_PAT is required to publish."
exit 1
fi
vsce publish --no-dependencies --packagePath "cline-nightly-${{ steps.version.outputs.version }}.vsix"
if [[ -n "$OVSX_PAT" ]]; then
npx ovsx publish --packagePath "cline-nightly-${{ steps.version.outputs.version }}.vsix" --pat "$OVSX_PAT"
else
echo "WARNING: OVSX_PAT not set; skipping Open VSX publish."
fi
- name: Tag published commit
if: github.ref == 'refs/heads/main' && inputs.dry-run != true
# Best-effort bookkeeping: the default GITHUB_TOKEN cannot create a ref
# whose commit modifies workflow files (no workflows permission exists
# for it), so this step fails whenever HEAD touched .github/workflows.
# The publish already succeeded by this point — don't mark the run red;
# push the tag manually with user credentials when it matters.
continue-on-error: true
working-directory: next-src
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}"
LEGACY_SHA=$(git -C "$GITHUB_WORKSPACE/legacy-src" rev-parse HEAD)
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 (combined A/B) published from ${GITHUB_REF_NAME} at ${GITHUB_SHA} (legacy bundle: ${LEGACY_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"
@@ -0,0 +1,310 @@
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
concurrency:
group: ext-vscode-publish-stable-${{ github.event.inputs.tag }}
cancel-in-progress: false
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
TAG_SHA=$(git rev-list -n 1 "$TAG_REF^{commit}")
if [[ "$TAG_SHA" != "$TESTED_SHA" ]]; then
echo "Error: existing tag '$TAG' points to $TAG_SHA, but this workflow tested $TESTED_SHA"
echo "Dispatch from the tag ref, or from the exact main commit the tag points to."
exit 1
fi
echo "Using existing tag '$TAG' at tested SHA $TESTED_SHA."
fi
git checkout --detach "$TAG_REF^{commit}"
echo "tag=$TAG" >> $GITHUB_OUTPUT
echo "resolved_sha=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: 1.3.14
# Node is still REQUIRED in the publish job (not just for install): the
# publish scripts run as `node scripts/publish-*.mjs`, the version step uses
# `node -p`, and `npx ovsx` needs npm. setup-bun does not provide a Node
# runtime, so keep setup-node. Pinned to Node 22 because newer LTS
# (Node 24 / npm 11) can make vsce's `npm list` detection fail with
# ELSPROBLEMS during packaging.
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 22
# Single root install resolves the whole bun workspace at once (replaces the
# per-package `npm install` steps for apps/vscode + webview-ui).
- name: Install workspace dependencies
working-directory: ${{ github.workspace }}
run: bun install --frozen-lockfile
# @cline/* are local workspace symlinks to source packages; build dist/ before
# packaging/publishing the extension.
- name: Build SDK packages
working-directory: ${{ github.workspace }}
run: bun run build:sdk
- name: Assert better-sqlite3 native binary present
run: |
NODE_FILE="node_modules/better-sqlite3/build/Release/better_sqlite3.node"
if [ ! -f "$NODE_FILE" ]; then
echo "ERROR: better-sqlite3 native binary missing at apps/vscode/$NODE_FILE"
echo "(bun trustedDependencies postinstall likely did not run)"
exit 1
fi
echo "Found better-sqlite3 native binary: $NODE_FILE"
# vsce is a workspace devDependency (on node_modules/.bin), but ovsx is not
# vendored and the publish script invokes it via `npx ovsx`, so install ovsx
# globally (npm is available via setup-node). vsce is installed globally too
# to preserve the script's existing PATH expectations.
- 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: Verify Changelog Entry
working-directory: ${{ github.workspace }}
run: |
EXPECTED_HEADING="## [${{ steps.get_version.outputs.version }}]"
FIRST_HEADING=$(grep -m 1 '^## \[' CHANGELOG.md || true)
if [[ "$FIRST_HEADING" != "$EXPECTED_HEADING" ]]; then
echo "Error: CHANGELOG.md must start with '$EXPECTED_HEADING' before publishing."
echo "Current first release heading: ${FIRST_HEADING:-<none>}"
exit 1
fi
echo "Found changelog entry for ${{ steps.get_version.outputs.version }}"
- name: Verify Marketplace Tokens
env:
VSCE_PAT: ${{ secrets.VSCE_PAT }}
OVSX_PAT: ${{ secrets.OVSX_PAT }}
run: |
if [[ -z "$VSCE_PAT" ]]; then
echo "Error: VSCE_PAT is required to publish the stable VS Code extension."
exit 1
fi
if [[ -z "$OVSX_PAT" ]]; then
echo "Error: OVSX_PAT is required to publish the stable Open VSX extension."
exit 1
fi
echo "Marketplace publish tokens are configured."
- name: Get Previous Tag
id: prev_tag
working-directory: ${{ github.workspace }}
run: |
CURRENT_TAG="${{ steps.resolve_tag.outputs.tag }}"
PREV_TAG=$(
git tag --merged "$CURRENT_TAG^" --list 'v[0-9]*.[0-9]*.[0-9]*' --sort=-v:refname \
| grep -E '^v[0-9]+\.[0-9]+\.[0-9]+([-.][0-9A-Za-z.]+)?$' \
| head -n 1 || true
)
echo "prev_tag=$PREV_TAG" >> $GITHUB_OUTPUT
- name: Get Changelog Entry
id: changelog
working-directory: ${{ github.workspace }}
run: |
# Get content between the matching version heading and the next release heading.
CONTENT=$(awk -v version="${{ steps.get_version.outputs.version }}" '
$0 == "## [" version "]" { found=1; next }
found && /^## \[/ { exit }
found { print }
END { if (!found) exit 1 }
' CHANGELOG.md)
echo "content<<EOF" >> $GITHUB_OUTPUT
echo "$CONTENT" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
- 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. --no-dependencies: the extension
# is fully esbuild-bundled, and under the bun workspace the @cline/*
# deps are symlinks pointing outside the package, so without this vsce
# would walk them and pull the whole monorepo into the .vsix.
vsce package --no-dependencies --allow-package-secrets sendgrid --out "cline-${{ steps.get_version.outputs.version }}.vsix"
# These scripts run under `node scripts/publish-marketplace.mjs`;
# bun run just launches them. Node + npm (for `npx ovsx`) come from
# setup-node above.
if [ "$RELEASE_TYPE" = "pre-release" ]; then
bun run publish:marketplace:prerelease
echo "Successfully published pre-release version ${{ steps.get_version.outputs.version }} to VS Code Marketplace and Open VSX Registry"
else
bun run publish:marketplace
echo "Successfully published release version ${{ steps.get_version.outputs.version }} to VS Code Marketplace and Open VSX Registry"
fi
- 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 }}"
+179
View File
@@ -0,0 +1,179 @@
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/webview-ui/package.json'
# Root bun lockfile is authoritative for the whole workspace (incl. apps/vscode).
- 'bun.lock'
# SDK source packages are local workspace symlinks (@cline/*), so SDK changes affect the build.
- 'sdk/packages/**'
- 'apps/vscode/buf.yaml'
- 'apps/vscode/tsconfig*.json'
- 'apps/vscode/biome.jsonc'
- 'apps/vscode/esbuild.mjs'
- 'apps/vscode/bunfig.toml'
- '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 Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: 1.3.14
# Cache Bun's global install cache - keyed on the authoritative root bun.lock.
- name: Cache Bun install cache
uses: actions/cache@v4
id: bun-cache
with:
path: ~/.bun/install/cache
key: ${{ runner.os }}-bun-${{ hashFiles('bun.lock') }}
restore-keys: |
${{ runner.os }}-bun-
# 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('bun.lock') }}
restore-keys: |
playwright-browsers-${{ runner.os }}-
# Single root install resolves the entire bun workspace at once (replaces
# the per-package `npm ci` steps for apps/vscode + webview-ui).
- name: Install workspace dependencies
working-directory: ${{ github.workspace }}
run: bun install --frozen-lockfile
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# @cline/* are local workspace symlinks to source packages; build dist/
# before building/packaging the extension for E2E.
- name: Build SDK packages
working-directory: ${{ github.workspace }}
run: bun run build:sdk
- name: Assert better-sqlite3 native binary present
# Force bash: the Windows runner defaults to pwsh, which can't parse this
# POSIX test. Git Bash ships on GitHub's windows-latest images.
shell: bash
run: |
NODE_FILE="node_modules/better-sqlite3/build/Release/better_sqlite3.node"
if [ ! -f "$NODE_FILE" ]; then
echo "ERROR: better-sqlite3 native binary missing at apps/vscode/$NODE_FILE"
echo "(bun trustedDependencies postinstall likely did not run)"
exit 1
fi
echo "Found better-sqlite3 native binary: $NODE_FILE"
# NOTE: vsce is no longer installed globally. @vscode/vsce is a workspace
# devDependency of apps/vscode (resolved into node_modules/.bin), and the
# `test:e2e:build` script invokes `vsce` via `bun run`, which puts the local
# .bin on PATH. No global install needed.
- 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 bun run test:e2e:optimal
- name: Run E2E tests - Non-Linux
if: matrix.runner != 'ubuntu'
run: bun run test:e2e:optimal
- uses: actions/upload-artifact@v4
if: ${{ failure() }}
with:
name: playwright-recordings-${{ matrix.runner }}
path: |
test-results/playwright/
+428
View File
@@ -0,0 +1,428 @@
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/webview-ui/package.json'
# Root bun lockfile is authoritative for the whole workspace (incl. apps/vscode).
- 'bun.lock'
# SDK source packages are local workspace symlinks (@cline/*), so SDK changes affect the build.
- 'sdk/packages/**'
- 'apps/vscode/buf.yaml'
- 'apps/vscode/tsconfig*.json'
- 'apps/vscode/biome.jsonc'
- 'apps/vscode/esbuild.mjs'
- 'apps/vscode/bunfig.toml'
- '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/testing-platform/package.json'
- 'apps/vscode/tests/specs/**'
- 'apps/vscode/package.json'
# Root bun lockfile is authoritative for the whole workspace (incl. apps/vscode).
- 'bun.lock'
# SDK source packages are local workspace symlinks (@cline/*), so SDK changes affect the build.
- 'sdk/packages/**'
- '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 Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: 1.3.14
# Single root install resolves the entire bun workspace (apps/vscode,
# webview-ui, testing-platform and the @cline/* SDK symlinks) at once,
# so the previous per-package `npm ci` steps collapse into one.
- name: Install workspace dependencies
working-directory: ${{ github.workspace }}
run: bun install --frozen-lockfile
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# @cline/* are local workspace symlinks to source packages; their dist/
# output must be built before the extension can type-check/compile.
- name: Build SDK packages
working-directory: ${{ github.workspace }}
run: bun run build:sdk
- name: Assert better-sqlite3 native binary present
run: |
NODE_FILE="node_modules/better-sqlite3/build/Release/better_sqlite3.node"
if [ ! -f "$NODE_FILE" ]; then
echo "ERROR: better-sqlite3 native binary missing at apps/vscode/$NODE_FILE"
echo "(bun trustedDependencies postinstall likely did not run)"
exit 1
fi
echo "Found better-sqlite3 native binary: $NODE_FILE"
- name: Run Quality Checks (Parallel)
run: bun run ci:check-all
vscode-test:
needs: [detect-changes, quality-checks]
if: needs.detect-changes.outputs.vscode == 'true'
env:
VSCODE_TEST_VERSION: 1.101.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 Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: 1.3.14
# Single root install resolves the entire bun workspace at once (replaces
# the per-package `npm ci` steps for apps/vscode + webview-ui).
- name: Install workspace dependencies
working-directory: ${{ github.workspace }}
run: bun install --frozen-lockfile
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# @cline/* are local workspace symlinks to source packages; build dist/
# before compiling/testing the extension.
- name: Build SDK packages
working-directory: ${{ github.workspace }}
run: bun run build:sdk
- name: Assert better-sqlite3 native binary present
run: |
NODE_FILE="node_modules/better-sqlite3/build/Release/better_sqlite3.node"
if [ ! -f "$NODE_FILE" ]; then
echo "ERROR: better-sqlite3 native binary missing at apps/vscode/$NODE_FILE"
echo "(bun trustedDependencies postinstall likely did not run)"
exit 1
fi
echo "Found better-sqlite3 native binary: $NODE_FILE"
# NOTE: The old `npm config set script-shell bash` step is intentionally
# removed. Scripts are now launched with `bun run`, which uses Bun's own
# built-in cross-platform shell rather than npm's configured script-shell,
# so that npm-specific Windows workaround no longer applies. Bash-dependent
# scripts (e.g. scripts/proto-lint.sh, standalone/runclinecore.sh) are
# invoked explicitly via `bash ...` from within the package scripts, and
# this job's `defaults.run.shell: bash` (Git Bash on Windows) still covers
# the workflow `run:` blocks below.
- 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: bun run ci:build
- name: Vitest Suites (SDK adapter + model catalog)
id: vitest_tests
if: ${{ !cancelled() && steps.build_step.outcome == 'success' }}
# The vitest config sets passWithNoTests: true, so a broken glob/alias
# would "pass" with zero tests. Capture output and assert a non-zero
# test count to guard against silent skips.
run: |
set -o pipefail
bun run test:vitest 2>&1 | tee vitest-output.log
# Strip ANSI color codes before matching — vitest colorizes the
# "Tests N passed" summary, so the count is not adjacent to the
# "Tests" label in the raw bytes.
if ! sed -r 's/\x1b\[[0-9;]*m//g' vitest-output.log | grep -Eq 'Tests[[:space:]]+[0-9]*[1-9][0-9]* (passed|failed)'; then
echo "ERROR: vitest reported zero tests (possible silent skip)."
exit 1
fi
- name: Unit Tests (bun) - Linux
id: unit_tests_linux
if: ${{ !cancelled() && steps.build_step.outcome == 'success' && runner.os == 'Linux' }}
# The node-side unit suite (.mocharc spec set) now runs under `bun test`
# via scripts/run-bun-unit-tests.ts (one isolated bun process per file).
# The runner exits non-zero on any failure and prints a final
# "Files: N Pass: P Fail: F" summary; assert a non-zero pass count to
# guard against an empty glob silently "passing".
run: |
set -o pipefail
bun run test:unit 2>&1 | tee unit-output.log
if ! grep -Eq 'Pass:[[:space:]]+[0-9]*[1-9][0-9]*' unit-output.log; then
echo "ERROR: bun unit runner reported zero passing tests (possible silent skip)."
exit 1
fi
- name: Unit Tests (bun) - Non-Linux
id: unit_tests_non_linux
if: ${{ !cancelled() && steps.build_step.outcome == 'success' && runner.os != 'Linux' }}
run: |
bun 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 bun 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 bun 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
bun 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/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 Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: 1.3.14
# Single root install resolves the whole bun workspace, including the
# testing-platform package, so the separate per-package `npm ci` steps
# (extension + webview-ui + testing-platform) collapse into one.
- name: Install workspace dependencies
working-directory: ${{ github.workspace }}
run: bun install --frozen-lockfile
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# @cline/* are local workspace symlinks to source packages; build dist/
# before compiling the standalone core.
- name: Build SDK packages
working-directory: ${{ github.workspace }}
run: bun run build:sdk
- name: Assert better-sqlite3 native binary present
run: |
NODE_FILE="node_modules/better-sqlite3/build/Release/better_sqlite3.node"
if [ ! -f "$NODE_FILE" ]; then
echo "ERROR: better-sqlite3 native binary missing at apps/vscode/$NODE_FILE"
echo "(bun trustedDependencies postinstall likely did not run)"
exit 1
fi
echo "Found better-sqlite3 native binary: $NODE_FILE"
- name: Download ripgrep binaries
run: bun run download-ripgrep
- name: Compile Standalone
run: bun run compile-standalone
- name: Running testing platform integration spec tests
timeout-minutes: 7
run: bun 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
@@ -1,65 +0,0 @@
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']
});
}
}
// 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']
});
}
}
-394
View File
@@ -1,394 +0,0 @@
name: Publish CLI to NPM
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: sdk
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
run: |
TAG="${{ github.event.inputs.git_tag }}"
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 "sdk/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 "sdk/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
- name: Run tests
run: bun run test
- name: Build platform binaries
run: bun script/build.ts --install-native-variants --skip-sdk-build
working-directory: sdk/apps/cli
- name: Verify build output
run: |
VERSION="${{ steps.version.outputs.version }}"
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: sdk/apps/cli
- name: Get Previous CLI Tag
id: prev_tag
run: |
CURRENT_TAG="${{ steps.version.outputs.tag }}"
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
run: |
VERSION="${{ steps.version.outputs.version }}"
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
- 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'
run: |
VERSION="${{ steps.version.outputs.version }}"
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"'
env:
VERSION: ${{ steps.version.outputs.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: sdk/apps/cli
- name: Verify build output
if: steps.check_commits.outputs.skip != 'true'
run: |
VERSION="${{ steps.version.outputs.version }}"
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: sdk/apps/cli
- name: Summary
if: steps.check_commits.outputs.skip != 'true'
run: |
VERSION="${{ steps.version.outputs.version }}"
echo "Published cline@${VERSION} to npm with dist-tag 'nightly'"
echo "Install with: npm install -g cline@nightly"
@@ -1,72 +0,0 @@
name: "Publish New SDK Extension Nightly"
on:
schedule:
- cron: '0 12 * * *' # 4 AM PST (UTC-8) = 12 UTC
workflow_dispatch:
permissions:
contents: read
packages: write
checks: write
pull-requests: write
env:
# Keep the publish source pinned to one reviewed branch instead of accepting arbitrary refs.
SDK_NIGHTLY_REF: dpc/sdk-migration-simpler-login
jobs:
publish:
name: Publish Cline New SDK Extension Nightly
if: github.repository == 'cline/cline' && github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
environment: PublishNightly
steps:
- name: Checkout trusted SDK nightly branch
uses: actions/checkout@v4
with:
ref: ${{ env.SDK_NIGHTLY_REF }}
lfs: true
persist-credentials: false
- 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 root dependencies
run: npm ci --include=optional
- name: Install webview-ui dependencies
run: cd webview-ui && npm ci --include=optional
- name: Install Publishing Tools
run: npm install -g @vscode/vsce ovsx
- name: Verify LFS media assets are resolved
run: |
for FILE in webview-ui/src/assets/cline_kanban_demo.mp4 webview-ui/src/assets/cline_kanban_demo.webm; do
if grep -q "git-lfs.github.com/spec/v1" "$FILE"; then
echo "Error: $FILE is still a Git LFS pointer in CI checkout"
exit 1
fi
done
- name: Publish SDK 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
-105
View File
@@ -1,105 +0,0 @@
name: "Publish Nightly Release"
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: 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
uses: ./.github/workflows/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
steps:
- name: Checkout selected branch
uses: actions/checkout@v4
with:
ref: ${{ github.sha }}
lfs: true
persist-credentials: false
- name: Show build source
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 root dependencies
run: npm ci --include=optional
- name: Install webview-ui dependencies
run: cd webview-ui && npm ci --include=optional
- name: Install Publishing Tools
run: npm install -g @vscode/vsce ovsx
- name: Verify LFS media assets are resolved
run: |
for FILE in webview-ui/src/assets/cline_kanban_demo.mp4 webview-ui/src/assets/cline_kanban_demo.webm; do
if grep -q "git-lfs.github.com/spec/v1" "$FILE"; then
echo "Error: $FILE is still a Git LFS pointer in CI checkout"
exit 1
fi
done
- 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
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"
-269
View File
@@ -1,269 +0,0 @@
name: Publish Main SDK Packages
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: sdk
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
run: |
# Default to nightly for scheduled runs
if [ "${{ github.event_name }}" = "schedule" ]; then
echo "channel=nightly" >> $GITHUB_OUTPUT
else
echo "channel=${{ inputs.channel }}" >> $GITHUB_OUTPUT
fi
- name: Check for recent commits
id: check_commits
run: |
CHANNEL="${{ steps.channel.outputs.channel }}"
# 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 [ "${{ inputs.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
run: |
CHANNEL="${{ steps.channel.outputs.channel }}"
BASE_VERSION=$(node -p "require('./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'
run: bun scripts/version.ts "${{ steps.version.outputs.version }}"
- name: Verify publishability
if: steps.check_commits.outputs.skip != 'true'
run: bun 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"
run: |
CHANNEL="${{ steps.channel.outputs.channel }}"
echo "Publishing @cline/shared@${{ steps.version.outputs.version }} with tag '${CHANNEL}'..."
cd 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"
run: |
CHANNEL="${{ steps.channel.outputs.channel }}"
echo "Publishing @cline/llms@${{ steps.version.outputs.version }} with tag '${CHANNEL}'..."
cd 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"
run: |
CHANNEL="${{ steps.channel.outputs.channel }}"
echo "Publishing @cline/agents@${{ steps.version.outputs.version }} with tag '${CHANNEL}'..."
cd 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"
run: |
CHANNEL="${{ steps.channel.outputs.channel }}"
echo "Publishing @cline/core@${{ steps.version.outputs.version }} with tag '${CHANNEL}'..."
cd 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"
run: |
CHANNEL="${{ steps.channel.outputs.channel }}"
echo "Publishing @cline/sdk@${{ steps.version.outputs.version }} with tag '${CHANNEL}'..."
cd 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'
run: |
VERSION="${{ steps.version.outputs.version }}"
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'
run: |
VERSION="${{ steps.version.outputs.version }}"
CHANNEL="${{ steps.channel.outputs.channel }}"
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
-230
View File
@@ -1,230 +0,0 @@
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
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/test.yml
publish:
needs: test
name: Publish Extension
runs-on: ubuntu-latest
environment: publish
steps:
- uses: actions/checkout@v4
with:
ref: main
fetch-depth: 0
fetch-tags: true
lfs: true
- name: Resolve Release Tag
id: resolve_tag
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 root dependencies
run: npm install --include=optional
- name: Install webview-ui dependencies
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: 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: Verify LFS media assets are resolved
run: |
for FILE in webview-ui/src/assets/cline_kanban_demo.mp4 webview-ui/src/assets/cline_kanban_demo.webm; do
if grep -q "git-lfs.github.com/spec/v1" "$FILE"; then
echo "Error: $FILE is still a Git LFS pointer in CI checkout"
exit 1
fi
done
- 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
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
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: "*.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 }}"
+65
View File
@@ -0,0 +1,65 @@
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*Cline Surface\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*Cline Surface\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*Cline Surface\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
@@ -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: 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 }}
+340
View File
@@ -0,0 +1,340 @@
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: Get Previous SDK Tag
if: steps.check_commits.outputs.skip != 'true' && steps.channel.outputs.channel == 'latest'
id: prev_tag
env:
VERSION: ${{ steps.version.outputs.version }}
run: |
# The checkout is shallow and tagless, so fetch the release tags explicitly.
git fetch origin "+refs/tags/sdk/sdk/v*:refs/tags/sdk/sdk/v*"
PREV_TAG=$(git tag -l 'sdk/sdk/v*' | grep -vx "sdk/sdk/v${VERSION}" | sort -V | tail -1 || echo "")
echo "prev_tag=$PREV_TAG" >> $GITHUB_OUTPUT
- name: Get Changelog Entry
if: steps.check_commits.outputs.skip != 'true' && steps.channel.outputs.channel == 'latest'
id: changelog
run: |
# Grab content between the first "## " header and the next one in sdk/CHANGELOG.md
CONTENT=$(awk '/^## [0-9]/{if(found) exit; found=1; next} found{print}' sdk/CHANGELOG.md)
DELIMITER=$(openssl rand -hex 8)
echo "content<<${DELIMITER}" >> $GITHUB_OUTPUT
echo "$CONTENT" >> $GITHUB_OUTPUT
echo "${DELIMITER}" >> $GITHUB_OUTPUT
- name: Create GitHub Release
if: steps.check_commits.outputs.skip != 'true' && steps.channel.outputs.channel == 'latest'
uses: softprops/action-gh-release@v1
with:
tag_name: sdk/sdk/v${{ steps.version.outputs.version }}
name: "SDK 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}...sdk/sdk/v{2}', github.repository, steps.prev_tag.outputs.prev_tag, steps.version.outputs.version) || '' }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- 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
- name: Post release to Slack
if: steps.check_commits.outputs.skip != 'true' && steps.channel.outputs.channel == 'latest'
uses: slackapi/slack-github-action@v3.0.1
with:
method: chat.postMessage
token: ${{ secrets.SLACK_RELEASE_BOT_TOKEN }}
payload: |
channel: "C0APVKGGZFC"
text: "Cline SDK v${{ steps.version.outputs.version }}"
blocks:
- type: "section"
text:
type: "mrkdwn"
text: "Cline SDK 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/sdk/v/${{ steps.version.outputs.version }}|View on npm>${{ steps.prev_tag.outputs.prev_tag != '' && format(' | Full Changelog: https://github.com/{0}/compare/{1}...sdk/sdk/v{2}', github.repository, steps.prev_tag.outputs.prev_tag, steps.version.outputs.version) || '' }}"
+5 -5
View File
@@ -1,4 +1,4 @@
name: SDK Tests
name: sdk-test
on:
push:
@@ -21,7 +21,7 @@ permissions:
defaults:
run:
working-directory: sdk
working-directory: .
jobs:
quality-checks:
@@ -96,12 +96,12 @@ jobs:
- 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 './packages/**' test
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 scripts/ci-node-smoke.ts
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' }}
@@ -109,4 +109,4 @@ jobs:
- 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 scripts/check-publish.ts
run: bun sdk/scripts/check-publish.ts
-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: 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
@@ -1,32 +0,0 @@
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
-245
View File
@@ -1,245 +0,0 @@
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
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
cache: 'npm'
cache-dependency-path: |
package-lock.json
webview-ui/package-lock.json
- name: Install root dependencies
run: npm ci
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Install webview-ui dependencies
run: cd webview-ui && npm ci
- name: Run Quality Checks (Parallel)
run: npm run ci:check-all
test:
needs: quality-checks
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' && '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
cache: 'npm'
cache-dependency-path: |
package-lock.json
webview-ui/package-lock.json
- name: Install root dependencies
run: npm ci
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Install webview-ui dependencies
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"
- 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: CLI Tests
id: cli_tests
if: ${{ !cancelled() && steps.build_step.outcome == 'success' }}
run: cd cli && npm run test:run
- 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
cache: 'npm'
cache-dependency-path: |
package-lock.json
webview-ui/package-lock.json
testing-platform/package-lock.json
- name: Install root dependencies
run: npm ci
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Install webview-ui dependencies
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
run: cd testing-platform && npm 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: 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
@@ -1,98 +0,0 @@
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') &&
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: 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"
+37 -5
View File
@@ -13,12 +13,15 @@ tmp
pnpm-lock.yaml
.clineignore
.cline/enterprise
.cline/remote-config
**/.cline/remote-config
.venv
.actrc
CLAUDE.local.md
webview-ui/src/**/*.js
webview-ui/src/**/*.js.map
apps/vscode/webview-ui/src/**/*.js
apps/vscode/webview-ui/src/**/*.js.map
# Ignore coverage directories and files
coverage
@@ -35,9 +38,9 @@ coverage-unit
.worktrees
## Generated files ##
src/generated/
src/shared/proto/
webview-ui/src/services/grpc-client.ts
apps/vscode/src/generated/
apps/vscode/src/shared/proto/
apps/vscode/webview-ui/src/services/grpc-client.ts
*.tsbuildinfo
# E2E Tests
@@ -60,3 +63,32 @@ 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
apps/vscode/tsconfig.test.generated.json
.next/dev/static
**/src-tauri/target/debug/.fingerprint
apps/examples/desktop-app/src-tauri/target
apps/examples/desktop-app/webview/.next
# Next.js generated type shim (churns between dev and build)
apps/examples/desktop-app/webview/next-env.d.ts
@@ -6,15 +6,18 @@
{
"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": ["packages/agents/src/**", "packages/core/src/**"],
"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": [
"packages/core/src/cline-core/**",
"packages/core/src/runtime/**"
"sdk/packages/core/src/cline-core/**",
"sdk/packages/core/src/runtime/**"
],
"severity": "high"
},
@@ -22,8 +25,8 @@
"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": [
"packages/core/src/**",
"packages/agents/src/**",
"sdk/packages/core/src/**",
"sdk/packages/agents/src/**",
"apps/cli/src/**",
"apps/vscode/src/**"
],
@@ -32,14 +35,10 @@
{
"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": ["packages/core/src/auth/**"],
"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": ["packages/core/src/services/telemetry/core-events.ts"],
"severity": "medium"
}
]
}
+28
View File
@@ -0,0 +1,28 @@
{
"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": "sdk/ARCHITECTURE.md",
"description": "Architecture reference. Telemetry design decisions and completion semantics (submit_and_exit anchoring) 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."
}
]
}
+147
View File
@@ -0,0 +1,147 @@
# 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. Add a unit test in `core-events.test.ts` asserting the event flows through the
opt-out-respecting `capture` path and never `captureRequired` (opt-out is enforced by
`OptedOutTelemetryService`, whose `capture` is a no-op — the test convention is
"emits X as a normal opt-out-respecting event"). Events that intentionally bypass
opt-out must use `captureRequired` and assert that explicitly.
**All events should be named using snake_case and so should their properties**
## 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`:
```ts
if (configDir) setClineDir(configDir);
setHomeDir(homedir());
captureCliExtensionActivated(); // <-- after dir overrides
```
## Hub Daemon Telemetry
The detached hub daemon (`sdk/packages/core/src/hub/daemon/entry.ts`) hosts the
`LocalRuntimeHost` that emits `task.conversation_turn` and `task.tokens` for every
hub-backed session, so the daemon must own its own `ITelemetryService`. It builds one via
`createHubDaemonTelemetry()` (`sdk/packages/core/src/hub/daemon/telemetry.ts`), which
identifies from the cached cline account (re-resolved periodically, since the daemon often
starts before login) and flushes on every shutdown path, including startup failure.
Flag changes that remove this wiring, construct runtime hosts inside the daemon without
passing its telemetry handle, or add daemon exit paths that skip the flush — hub-backed
sessions would silently drop their lifecycle telemetry (this exact bug shipped once).
## 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, all callers go through the lazy `telemetryService` proxy in
`apps/vscode/src/services/telemetry/index.ts`, which constructs the service once on first
use. 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.
+11 -1
View File
@@ -1 +1,11 @@
lint-staged
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
cd apps/vscode && bunx lint-staged
-16
View File
@@ -1,16 +0,0 @@
{
"extension": [
"ts"
],
"spec": [
"src/**/__tests__/*.ts",
"src/test/services/**/*.test.ts"
],
"require": [
"ts-node/register",
"source-map-support/register",
"./src/test/requires.ts"
],
"recursive": true,
"exit": true
}
+1 -1
View File
@@ -1 +1 @@
lts/*
22
-48
View File
@@ -1,48 +0,0 @@
{
"all": true,
"check-coverage": false,
"reporter": [
"text",
"lcov"
],
"include": [
"src/**/*.ts"
],
"exclude": [
"**/*.d.ts",
"**/*.{test,spec}.{js,jsx,ts,tsx,mjs,cjs}",
"**/__tests__/**",
"**/test/**",
"**/tests/**",
"**/.nyc_output/**",
"**/.vscode-test/**",
"**/tests-results/**",
"src/test/**",
"src/generated/**",
"**/node_modules/**",
"**/dist/**",
"**/out/**",
"**/build/**",
"**/coverage/**",
"**/coverage-unit/**",
"**/proto/**",
"**/*.{config,setup}.{js,ts,mjs,cjs}",
"**/vite-env.d.ts",
"**/*.{css,scss,sass,less,styl}",
"**/*.{svg,png,jpg,jpeg,gif,ico}",
"**/*.{json,yaml,yml}"
],
"extension": [
".ts",
".js"
],
"cache": true,
"sourceMap": true,
"instrument": true,
"report-dir": "./coverage-unit"
}
+3
View File
@@ -0,0 +1,3 @@
bun 1.3.13
node 22
-19
View File
@@ -1,19 +0,0 @@
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}",
mocha: {
ui: "bdd",
timeout: 20000, // Maximum time (in ms) that a test can run before failing
/** Set up alias path resolution during tests
* @See {@link file://./test-setup.js}
*/
require: ["./test-setup.js"],
},
workspaceFolder: "test-workspace",
version: vscodeTestVersion,
extensionDevelopmentPath: path.resolve("./"),
launchArgs: ["--disable-extensions"],
})

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