Compare commits

..
Author SHA1 Message Date
Saoud Rizwan 71da25835a docs: add per-plugin descriptions to plugin examples page
Builds on the plugin examples page from #10617 by adding per-plugin
H3 sections that explain what each example demonstrates code-wise
(workspace-aware setup, lifecycle hook coverage, message builders,
beforeTool policy gating, automation event sources, AST resolution,
multi-agent presets, etc.) instead of one-line summaries.

Keeps the existing usage walkthroughs (block ignored file access,
add web search, custom message compaction, background shell jobs)
and moves them into a dedicated section below the examples.

Also updates the writing-plugins guide to drop the now-redundant
standalone typescript-lsp-plugin external link and point at the
in-docs plugin examples page (the typescript-lsp plugin is one of
the listed examples there).
2026-05-11 16:04:37 -07:00
Renee Huang 8f79c7a714 remove features/connectors 2026-05-11 14:56:16 -07:00
Saoud RizwanandRenee Huang db4bd8b24c docs: streamline SDK docs with example references (#10617)
* docs: streamline SDK docs with example references and @cline/sdk imports

Replace large standalone code blobs in SDK docs with references to
working examples in the SDK repository. Users can now clone and run
real code instead of copy-pasting from docs.

- Update all imports from @cline/agents, @cline/core, @cline/shared
  to @cline/sdk (the public-facing package)
- Fix model IDs from claude-sonnet-4-6 to claude-sonnet-4-20250514
- Quickstart: trim duplicate code patterns, add cards linking to
  cli-agent, code-review-bot, multi-agent, desktop-app examples
- Overview: add examples table with difficulty progression, update
  install instructions to use @cline/sdk
- Building an Agent: rewrite as a walkthrough of the code-review-bot
  example rather than inline code blobs across 4 separate files
- Creating Custom Tools: rewrite to use createTool with zod, add
  completion tools section, reference working examples
- Tools: show createTool with zod as primary pattern
- Events: reference multi-agent example for streaming UI pattern
- Architecture: update install to @cline/sdk

* fix: use claude-sonnet-4-6 model ID across all SDK docs

* remove connector page from sdk

* small reordering

* clean up sdk app and plugin examples

---------

Co-authored-by: Renee Huang <renee@cline.bot>
2026-05-11 14:53:40 -07:00
Saoud Rizwan 1d8b99f900 docs: rename @clinebot to @cline and clite to cline
SDK packages moved to the @cline npm org. Update all docs references
to use @cline/sdk, @cline/agents, @cline/core, @cline/shared, @cline/llms
and the cline CLI binary name.
2026-05-08 18:06:08 -07:00
Renee Huang b6e6d7afad mark warnings 2026-05-08 11:08:24 -07:00
Renee Huang a616b4c5f8 further clean up 2026-05-08 09:06:13 -07:00
Renee Huang 16774c7cd2 further simplify the doc 2026-05-08 08:49:16 -07:00
Saoud Rizwan d5a577389d docs: deduplicate plugin install docs
Trim redundant plugin install content from CLI reference, SDK plugins
concept page, and writing-plugins guide. Each now links to the
customization/plugins page as the single source of truth for manifest
format, install commands, and directory layout.
2026-05-07 22:45:48 -07:00
Saoud Rizwan bba90e9af4 docs: add plugin install command documentation
Document the new clite plugin install command across CLI reference,
customization plugins page, SDK plugins concept page, and the writing
plugins guide. Cover all three source types (npm, git, local), the
package.json manifest format with cline.plugins field, host-provided
dependency handling, auto-detection logic, and the install directory
structure.

Reference cline/typescript-lsp-plugin as a concrete install example.
2026-05-07 22:16:18 -07:00
Renee Huang d566a79929 remove learn section 2026-05-07 20:58:15 -07:00
Renee Huang 2484d24f97 merge 2026-05-07 20:54:17 -07:00
Heyang Wangandheyang.why b8f40f4edc fix(mcp): ensure callTool sends empty object when toolArguments is undefined (#10553) (#10568)
- Fix toolArguments fallback to {} to comply with MCP SDK Zod validation (ZodRecord<ZodString, ZodUnknown>)

- Add comprehensive unit tests for McpHub.callTool() covering argument handling, request structure, error handling, telemetry, and response handling

Co-authored-by: heyang.why <heyang.why@alibaba-inc.com>
2026-05-07 09:08:41 -07:00
Ara f936d53d25 fix(openrouter): enable cache control for Qwen models (#10578) 2026-05-06 12:58:51 -07:00
Renee Huang 7dfe88072e nit update on codepaths 2026-05-04 12:24:49 -07:00
Mikołaj Kondratek 90c8112257 At-mention picker: show "Searching..." instead of misleading "No results found" (#10478)
* At-mention picker: show "Searching..." instead of misleading "No results found"

When the @-mention picker fires its initial empty-query searchFiles, slow
workspaces (e.g. network mounts) leave the call in flight for several
seconds. Three small UX bugs combined to make this look broken:

1. The 500ms delayed-loading effect was gated on `searchQuery` being
   non-empty, so the spinner never appeared during the initial open —
   the user just saw "No results found" forever.
2. While loading, the spinner row stacked above the "No results found"
   row, claiming both states at once.
3. The spinner also stacked above the static root-menu items
   ("Paste URL", "Problems", "Git Commits", "Add File", "Add Folder")
   when the picker first opens with empty input, even though those
   items are already actionable.

Fixes:
- Drop the `&& searchQuery` guard so the loading effect arms on empty
  queries too.
- In `filteredOptions`, strip the lone `NoResults` entry while
  `showDelayedLoading` is true — searching is not the same as nothing
  matched.
- Render the spinner only when `filteredOptions.length === 0`, so it
  never stacks above existing options.

The 500ms delay before the spinner appears is preserved, so fast
searches stay visually quiet.

* fixes

* Drop stale @-mention searchFiles responses to fix "No results" flash

* Track in-flight searches with a monotonic latestSearchTokenRef in
  ChatTextArea; resolve/error handlers bail when their captured token
  is no longer the latest.
* Send the token as mentionsRequestId; proto already supports it.
* Drop the never-read currentSearchQueryRef scaffold.
* Fixes the cancel-then-re-pick race (Add File → cancel → Add Folder)
  reported in CLINE-1814.
2026-05-04 15:46:28 +02:00
Renee Huang 83a2824103 doc update based on bee latest branch 2026-05-01 11:54:53 -07:00
MaxandMax Paulus 🥪 86f463496c bump versions and changelog (#10503)
Co-authored-by: Max Paulus 🥪 <max@cline.bot>
2026-05-01 09:25:09 -07:00
Renee Huang 5198db81f7 making more progress 2026-04-30 18:53:04 -07:00
Renee Huang 576d126208 more sdk doc tightening 2026-04-30 18:27:04 -07:00
Renee Huang c878c663eb sdk tighten up pt1 2026-04-30 17:48:03 -07:00
Saoud Rizwan 72562ea74e feat: add beta version checkbox to bug report issue template (#10490)
Add a checkbox for users to indicate they're on a beta version, and
auto-apply the 'beta' label via the existing auto-label workflow when
the checkbox is checked.
2026-04-30 17:35:32 -07:00
Renee Huang 562b636481 another big cleanup - remove the CLI tabgroup 2026-04-30 17:31:59 -07:00
MaxandMax Paulus 🥪 544e3aa240 publish sdk migration branch to nightly main channel (#10485)
Co-authored-by: Max Paulus 🥪 <max@cline.bot>
2026-04-30 12:41:43 -07:00
Dominic Cooney beb3ad78dc Revert "Remove foreground terminal from Cline VSCode extension (#10196)" (#10477)
This reverts commit 1862f15955.
2026-04-30 09:41:52 -07:00
Renee Huang 82faedead3 wip... 2026-04-29 19:04:53 -07:00
Renee Huang 770f3e0807 kanban finishing clean up 2026-04-29 18:57:58 -07:00
Renee Huang 79c6381893 one step further cleaning up 2026-04-29 18:41:16 -07:00
Renee Huang c9d051cc0b update providers 2026-04-29 18:15:14 -07:00
Renee Huang a5d6bcecea provider reorg cont 2026-04-29 18:03:47 -07:00
Renee Huang 289ddd6922 more concise instsallation and model selection 2026-04-29 17:45:56 -07:00
Renee Huang 65d93eedab config rewrite 2026-04-29 17:29:16 -07:00
Renee Huang 2928f68fd0 cli ref updates 2026-04-29 16:55:17 -07:00
Renee Huang c1e07f26a9 a large pass on workflows 2026-04-29 16:43:41 -07:00
Renee Huang 17018066a7 Merge origin/main into saoudrizwan/sdk-docs and resolve docs conflicts 2026-04-29 15:35:00 -07:00
Renee Huang 54fc8e2a7e part 1 of large provider changes 2026-04-29 15:32:14 -07:00
Renee Huang af35bd28b2 remove deprecated features 2026-04-29 15:00:36 -07:00
Renee Huang bce75f9821 clean up attempt 1 2026-04-29 14:56:06 -07:00
Mikołaj Kondratek ee1d4b4dcf CLINE-1814 typed RipgrepSpawnError + error_reason proto (#10443)
* CLINE-1814 typed RipgrepSpawnError + error_reason proto

* file-search.ts: define RipgrepSpawnError carrying stderr+exitCode; reject
  on non-zero ripgrep exit (with empty results) instead of resolving to []
* file-search.ts: re-throw from searchWorkspaceFiles and
  searchWorkspaceFilesMultiroot so the controller sees the error and can
  attach a structured error_reason to the proto response
* file-search.test.ts: assert spawn-time and exit-time errors both surface
  as RipgrepSpawnError
* file.proto/FileSearchResults: add optional error_reason and error_message
  fields with the closed enumeration of values documented inline

Phase 1 of the visibility patch. No behaviour change for healthy installs;
broken installs now surface a real error instead of an empty list.

Refs: CLINE-1814

* CLINE-1814 surface error_reason in picker UI

Controller (searchFiles.ts):
* classify thrown errors into a closed enumeration of error_reason values
  (workspace_unavailable, ripgrep_spawn_failed, unknown). RipgrepSpawnError
  unwraps the first line of stderr into error_message so the picker can
  surface ENOENT / EACCES / 'Operation not permitted' verbatim.
* the no-workspace-path branch now returns workspace_unavailable instead
  of an empty result list.
* keep using telemetry.captureMentionFailed for aggregate signal but map
  ripgrep_spawn_failed -> 'unknown' to stay within the existing closed enum.

Webview (ChatTextArea + ContextMenu):
* ChatTextArea threads errorReason / errorMessage from each searchFiles
  RPC response (and from RPC-level rejections) into ContextMenu.
* ContextMenu renders a grey, italic, smaller subtitle beneath the
  'No results found' row when an error_reason is present.
* renderErrorSubtitle() carries a short doc-comment for each value so
  reviewers can see at a glance how each error_reason maps to UI copy.

Refs: CLINE-1814

* CLINE-1814 trim verbose CLINE-1814 ticket-reference comments

Pure code-quality pass over the Phase 1 changes: condense the long
narrative comments that referenced the ticket into terser explanatory
comments where they still add value, and remove ones that just
repeated what the (now-stable) code already says. No behaviour change.

* CLINE-1814 fix RipgrepSpawnError override of Error.cause

TS error 'This member must have an override modifier because it
overrides a member in the base class Error' - Error gained an optional
'cause' field in ES2022. Drop the explicit field declaration and pass
the cause via the standard ES2022 ErrorOptions in super(). Behaviour
unchanged: instances still expose .cause via the base-class field.

* CLINE-1814 fix Windows race in executeRipgrepForFiles finalisation

CI hit this on Windows:

  AssertionError: expected [Promise] to be rejected with a message
  matching /ripgrep exited with code 2/, but got 'ripgrep exited with
  code null: rg: /bogus: No such file or directory (os error 2)'

The readline 'close' event and the child-process 'exit' event fire in
non-deterministic order on Windows. The previous code keyed off 'close'
alone, which meant the rejection branch could run with exitCode still
null even when the process eventually exited with a real code.

Fix: gate finalisation on both events with a small barrier (rlClosed +
processExited + finalised flags). Idempotent and safe under any
ordering. Test passes on darwin (where the ordering used to be benign)
and the same path now produces the expected exitCode=2 message on
Windows.

No production-behaviour change on the happy path: results still resolve
exactly when the readline finishes parsing stdout.

* CLINE-1814 address Phase 1 code-review feedback

Three small follow-ups from review on the Phase 1 PR:

1) ContextMenu.tsx: drop a stray trailing semicolon on the
   selectedType prop declaration so the interface style stays
   consistent with the rest of the file (no semicolons on field
   declarations). Cosmetic only, no behaviour change.

2) file-search.ts: in executeRipgrepForFiles' rgProcess.on('error')
   handler, set finalised = true before reject() so that a subsequent
   ('close', 'exit') pair can't pass the finalise() guards. The
   double-reject was already a no-op (Promises swallow further
   reject() calls once settled), but unconditionally maintaining the
   barrier invariant makes the lifecycle of this Promise much easier
   to reason about and matches the symmetry of the other two
   finalise() callers.

3) file-search.test.ts: collapse the awaited-twice rejected-promise
   pattern in 'should reject with RipgrepSpawnError when ripgrep
   exits non-zero with no results'. The previous form
   (await should(p).be.rejectedWith(...); await p.catch(...)) worked
   because settled promises replay their value, but it's subtly
   misleading. The new form awaits once via .catch() and asserts on
   the resulting error directly.

All six File Search unit tests continue to pass.

* CLINE-1814 revert picker error subtitle (UI for impl-detail leak)

Per code-review feedback: surfacing structured error_reason / error_message
from FileSearchResults as a grey-italic subtitle on the 'No results found'
row exposes implementation detail to end-users. The user can't act on
'(ripgrep failed: rg: ENOENT)' or '(internal error: spawn EACCES)' — those
are diagnostic data that belong in logs and aggregate telemetry.

Reverted in this commit:
  - ContextMenu.tsx: errorReason / errorMessage props removed,
    renderErrorSubtitle helper deleted, NoResults row reverts to a plain
    <span>No results found</span>.
  - ChatTextArea.tsx: searchErrorReason / searchErrorMessage state and
    all setSearchErrorReason / setSearchErrorMessage call-sites removed;
    ContextMenu invocation no longer passes the two props.

Kept on purpose:
  - The proto field FileSearchResults.error_reason — it's harmless on
    the wire and the next commit wires it up to telemetry + structured
    logging, which is where this signal actually belongs.
  - The classifyError helper and ERROR_REASON_* constants in the
    searchFiles controller — same reason, they feed telemetry next.

Six file-search unit tests still pass.

* CLINE-1814 telemetry: surface ripgrep_spawn_failed / workspace_unavailable

Until now the searchFiles controller's catch block collapsed every classified
error_reason — workspace_unavailable, ripgrep_spawn_failed, unknown — onto a
two-value telemetry enum (permission_denied | unknown), throwing away the
diagnostic signal we'd worked hard to extract. The Linear ticket explicitly
asks for the structured signal to feed telemetry; this commit delivers on that.

Changes:
  - TelemetryService.captureMentionFailed: extend the errorType enum with
    two new categorical values, ripgrep_spawn_failed and workspace_unavailable.
    Doc-comment updated to call out that those two are picker-search failures
    (vs the existing values which are mention-content retrieval failures).
  - searchFiles.ts:
      * empty-workspace branch now emits errorType=workspace_unavailable
        (previously: not_found).
      * catch-block computes errorType from the classified errorReason —
        RipgrepSpawnError -> ripgrep_spawn_failed, EACCES -> permission_denied,
        otherwise unknown — instead of always permission_denied | unknown.

Net result: ops can now distinguish 'ripgrep is broken on this user's
machine' from 'user is in a one-window-no-folder IntelliJ session' from
genuine code bugs in the search pipeline.

* CLINE-1814 log: include classified errorReason on searchFiles error line

Trivial follow-up to the previous commit. Triagers grepping
~/.cline/cline-core-service.log for searchFiles failures got the raw error
object dumped, but no hint as to which of the structured error_reason
buckets the failure falls into. Now the log line is

    [ERROR] Error in searchFiles (errorReason=ripgrep_spawn_failed): <Error...>

so a single grep for 'errorReason=ripgrep_spawn_failed' surfaces every
ripgrep-side failure across the user's session without having to read the
stack trace. Same field value as the gRPC response and the telemetry event,
so the three sources can be cross-referenced in incident triage.

Also moved the classifyError() call above the Logger.error() call (was
below) so the reason is computed once, used twice.

* CLINE-1814 address Phase 1 review feedback (rename, simplify, trim comments)

Five review comments rolled into one commit:

file-search.ts
  * Rename RipgrepSpawnError -> RipgrepError. The class covers spawn failures
    AND non-zero-exit / stderr-on-empty-stdout paths; the old name only
    described half its job.
  * Drop the unread 'cause' constructor option. We were never reading
    err.cause anywhere downstream, and the only producer was the
    spawn-error path which already encodes the underlying message in the
    string.
  * Replace the rlClosed/processExited/finalised state machine with two
    Promise resolvers awaited via Promise.all. Same ordering guarantees on
    Windows (both 'close' and 'exit' must fire before we settle), zero
    mutable bookkeeping, and the spawn-error path no longer needs to
    pre-flip a 'finalised' flag to be safe against a late close+exit pair.
    Used new Promise() rather than Promise.withResolvers() because TS lib
    is es2022 and withResolvers is es2024; behavior is identical.

searchFiles.ts
  * .trim() the stderr before split() so a leading newline doesn't yield
    an empty first line on the telemetry / log path.
  * Drop the 'this commit' comment (ephemera once 'this commit' is no
    longer the most recent one) and the 'Determine mention type based on
    the search request' comment, both of which restated the obvious code.

file-search.test.ts
  * Update test name and assertion to match the renamed class.

All 6 file-search unit tests pass.

* CLINE-1814 drop unread RipgrepError.exitCode field

Follow-up to the previous review-feedback commit. The 'we're not reading
this anywhere' note was about exitCode, not cause - my mistake. The exit
code is already encoded into the error message string ('ripgrep exited
with code N: <stderr>'), so the dedicated field was carrying no
additional information for any consumer.

Dropped:
  * RipgrepError.exitCode field and constructor option
  * 'exitCode' from the two new RipgrepError(...) call sites
  * 'should(err).have.property(exitCode, 2)' from the unit test

The internal exitCode local in executeRipgrepForFiles stays - it gates
the reject vs resolve decision after both 'close' and 'exit' have fired.
It's just no longer plumbed onto the error.

All 6 file-search unit tests still pass.
2026-04-29 10:42:48 -07:00
Renee Huangandgreptile-apps[bot] f2eda58c70 rm hardcoded model lists (#10436)
* rm hardcoded model lists

* Apply suggestion from @greptile-apps[bot]

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-04-27 17:26:30 -07:00
Ara 4afc973f7d feat(openai): add latest native models (#10435) 2026-04-27 16:16:19 -07:00
Ara 5fe6c9a8ce Add Z AI GLM-5.1 model (#10409) 2026-04-25 11:11:32 -07:00
Dominic Cooney 901d1b5c97 fix(hooks): Use shell escapes on JSON literals in hooks templates (#10382)
* Fix quote escaping in hooks templates.

* Bump timeouts.

* Escaping for CONTEXT_MOD.

* Fix documentation and existing templates to use correct input property names.
2026-04-24 11:11:44 -07:00
tjandy98 c139f7a4d5 Add GPT-5.4 and GPT-5.4-nano (#10394)
Signed-off-by: tjandy98 <3953059+tjandy98@users.noreply.github.com>
2026-04-24 18:02:47 +02:00
MaxandMax Paulus 🥪 07593bb42a version bump and changelog (#10395)
Co-authored-by: Max Paulus 🥪 <max@cline.bot>
2026-04-24 08:16:35 -07:00
Ara fd21c314c1 Adding Gpt-5.5 to OpenAI codex subscription provider (#10390) 2026-04-24 07:48:47 -07:00
Tomás Barreiro 32ca1cad9a Use env for github inputs (#10383) 2026-04-24 02:12:27 -07:00
Saoud Rizwan e272a8dfbb docs: rename extensions to plugins across all SDK and CLI docs
- Rename AgentExtension to AgentPlugin in all code examples
- Rename ExtensionAPI to PluginAPI
- Rename extensions.mdx to plugins.mdx, writing-extensions.mdx to writing-plugins.mdx
- Rename all variable names (databaseExtension -> databasePlugin, etc.)
- Rename config field from extensions to plugins
- Update all prose, section headers, and cross-links
- Add redirects for old paths
2026-04-23 15:49:45 -07:00
Saoud Rizwan c516230809 docs: move Ecosystem page after Guides and fix opening sentence 2026-04-23 13:34:59 -07:00
Saoud Rizwan 50021c8c5a docs: add Ecosystem page to SDK tab 2026-04-23 13:27:44 -07:00
Saoud Rizwan b953c3682a docs: update hub-spoke architecture from gRPC/RPC to WebSockets
- Rewrite hub-spoke.mdx: WebSocket protocol, capability brokerage,
  spoke workers, session participants, hub daemon discovery
- Replace all RPC/gRPC/sidecar references with hub/WebSocket across
  all SDK and CLI docs
- Backend modes: local/hub/remote/auto (was local/rpc/auto)
- Hub command replaces rpc command (cline hub start/stop/status/ensure)
- Default port 25463 (was 4317), log file hub-daemon.log
- Remove @clinebot/rpc and @clinebot/scheduler from architecture docs
  (functionality absorbed into @clinebot/core)
- Update ClineCore reference to remove rpc options
- Add capability brokerage and session participant concepts
2026-04-23 13:14:28 -07:00
Tomás Barreiro 852c65b70c Do not show hardcoded new items (#10374)
* Do not show hardcoded new items

* fix use effect dependencies

* fix tests
2026-04-23 11:52:03 -07:00
Mikołaj Kondratek 70f0e8d548 feat(memory-observability): add periodic memory logging to cline-core (#10343)
* feat(memory-observability): add periodic memory logging to cline-core

Introduces a lightweight memory monitor that logs process.memoryUsage()
snapshots to the existing cline-core log every 5 minutes, plus an
immediate baseline at startup and a final snapshot at graceful shutdown.

Each entry is written as a single `[MEMORY] key=valueMB ...` line so it
is trivially greppable and parseable:

    grep '\[MEMORY\]' ~/.cline/cline-core-service.log

The timer is unref()'d so it does not keep the event loop alive on its
own, ensuring the Node process can still exit cleanly.

Also adds an informational log line after process.chdir(__dirname) that
records where V8 will write heap snapshots if --heapsnapshot-near-heap-limit
triggers them, and a best-effort process.on("exit") handler that scans
cwd for .heapsnapshot files on abnormal exit and logs their paths/sizes
so post-mortem investigation starts with the diagnostic data in hand.

This is Part 1 (periodic memory logging) and the Node-side portions of
Part 2 (snapshot directory + exit handler) of the memory observability
implementation plan. The V8 flag itself and the
~/.cline/heapsnapshots/ move-and-cap cleanup live in the Kotlin
CoreProcessManager and are applied separately in the plugin repo.

No business-logic changes; purely additive diagnostics.

* chore(memory-observability): enable --heapsnapshot-near-heap-limit=3 in runclinecore.sh

When cline-core approaches the V8 heap ceiling, V8 will now write up to
3 .heapsnapshot files to the current working directory before giving up
and crashing. These snapshots can be loaded into Chrome DevTools → Memory
tab to identify the objects retaining the most memory.

N=3 is chosen because the last snapshot (written just before the fatal
OOM) shows only live, truly-unreclaimable objects — the earlier ones still
contain garbage the GC hadn't collected yet. Having all three lets us
compare.

This flag is a V8 runtime flag and must be passed on the node command
line; it cannot be enabled from JavaScript at runtime.

Matches the equivalent change on the cline-core launcher in the IntelliJ
plugin repo (CoreProcessManager.kt).

* chore(memory-observability): reduce --heapsnapshot-near-heap-limit from 3 to 1

Reviewer concern: with --max-old-space-size=8192, each heap snapshot
serializes at roughly 4-5x heapUsed on disk, so three snapshots can
burst 24-40 GB to disk in the seconds before an OOM crash — right
when the system is already under memory/CPU pressure. On a laptop
with <40 GB free this can leave partial/corrupted snapshots or
trigger OS pressure on unrelated processes.

The plan doc originally argued 'snapshot 3 of 3 is most valuable
because it contains only live objects'. In practice, by the time V8
triggers the flag it has already run aggressive mark-compact cycles,
so snapshot 1 is nearly-all-live too. Our own Scenario B verification
run confirmed that even the first snapshot contained the retainer
chain — snapshots 2 and 3 added no diagnostic signal.

Trade-off:
  - per-OOM disk burst:      24-40 GB  ->  8-14 GB  (3x reduction)
  - time-to-crash (frozen):  30-60 s   ->  10-20 s  (3x reduction)
  - diagnostic signal:       essentially unchanged

The persistent-directory cap in CoreProcessManager.kt stays at 3, so
we still retain snapshots from the 3 most recent OOM events for
cross-event comparison.

* chore(memory-observability): shorten runclinecore.sh flag comments

The one-line pointer to CoreProcessManager.kt was more noise than
signal given the flags are visible on the same line as the command.
Rationale for the --heapsnapshot-near-heap-limit value lives in the
Kotlin constant's KDoc and in the commit log.
2026-04-23 13:34:51 +09:00
Saoud Rizwan f3f5bdd902 docs: rename cline-cli/ to cli/ and cline-sdk/ to sdk/
Shorter, cleaner URL paths. Updated all internal links across every
doc page and added 44 redirects for old paths so existing URLs
don't break.
2026-04-22 21:09:00 -07:00
Saoud Rizwan 1d91dbc894 docs: rename CLI Getting Started page title to Overview 2026-04-22 20:39:32 -07:00
TheRealSpencer 5accd88d73 fix: pin protobufjs to 7.5.5 to address CVE-2026-41242 (#10365) 2026-04-22 20:22:32 -07:00
Saoud Rizwan 5b29be63b8 docs: remove demo video from README (#10363) 2026-04-22 18:46:17 -07:00
Saoud Rizwan 401abd9434 fix: use getting-started page in CLI tab nav and remove conflicting redirect 2026-04-22 15:59:09 -07:00
Saoud Rizwan 79e99eb526 docs: fix packages page and merge duplicate imports 2026-04-22 15:29:07 -07:00
Saoud Rizwan f1d2569931 docs: rename Extension tab to Cline 2026-04-22 15:20:49 -07:00
Saoud Rizwan efbacbbf33 docs: use @clinebot/sdk as the primary install and import path
@clinebot/sdk is an alias for @clinebot/core that re-exports from all
packages. All user-facing pages (overview, quickstart, examples, guides)
now show npm install @clinebot/sdk and import from @clinebot/sdk.
Architecture and reference pages keep individual package names since
they document the internal package structure.
2026-04-22 15:17:44 -07:00
Saoud Rizwan 69e6ab9069 docs: simplify SDK install to single @clinebot/core package
@clinebot/core re-exports from agents, llms, and shared, so users
only need one install and one import source. Updated all getting-started,
quickstart, examples, and guide pages to import from @clinebot/core.
Concept and reference pages keep individual package imports since they
document those specific packages.
2026-04-22 11:20:26 -07:00
MaxandMax Paulus 🥪 9dea336ced update changelog (#10356)
Co-authored-by: Max Paulus 🥪 <max@cline.bot>
2026-04-22 11:03:35 -07:00
Tomás Barreiro 697f801937 Use the recommended model list for the onboarding flow (#10355)
* Use the recommended model list for the onboarding flow

* show the welcome view if we fail to load models
2026-04-22 19:32:45 +02:00
Tomás Barreiro 6be35bfcea Remove old hardcoded banners (#10354)
* Remove old hardcoded banners

* Remove tests
2026-04-22 10:21:01 -07:00
Ara 5a91800b6c Add SDK nightly publish workflow (#10344)
* chore: add sdk nightly publish workflow

* chore: publish sdk nightly to prerelease channel
2026-04-22 10:13:56 -07:00
Jose Castelli dacadbaae0 use details instead of catched error message (#10353)
use details instead of caught error message
2026-04-22 16:50:45 +02:00
Saoud Rizwan cfc5abca02 docs: rename Docs tab to Extension and reorder tabs
Tab order: Extension, CLI, SDK, Kanban, Enterprise, API, Learn
2026-04-22 02:23:13 -07:00
Saoud Rizwan 4da5614863 feat(docs): create CLI tab with new feature pages and updated reference
- Add CLI as its own top-level tab in docs navigation
- Remove CLI group from Docs tab and SDK tab
- Delete SDK CLI pages (content moved to CLI tab)
- Add new feature pages: connectors, scheduling, MCP servers, agent teams
- Rewrite cli-reference.mdx with all new commands (connect, mcp, schedule,
  rpc, checkpoint, doctor) and new flags (reasoning-effort, thinking,
  sandbox, teams, spawn, tool-enable/disable, autoapprove)
- Update configuration.mdx with new directory structure, env vars
  (CLINE_DATA_DIR, CLINE_RPC_ADDRESS, CLINE_SESSION_BACKEND_MODE,
  CLINE_SANDBOX), and MCP wizard reference
2026-04-22 02:22:16 -07:00
Saoud Rizwan cef0da35e2 docs: add wizard commands, Discord connector, and platform credential details 2026-04-22 02:15:22 -07:00
Saoud Rizwan 62fa67d833 docs: fix SDK docs review feedback
- Change "desktop app" to "JetBrains plugin" in overview (not released yet)
- Rename "Providers & Models" to "Model Providers"
- Rename "Streaming & Events" to "Streaming Events"
- Remove duplicate cli/connectors.mdx (guides/connectors.mdx covers everything)
2026-04-22 00:31:51 -07:00
Saoud Rizwan 4a57450c07 docs: add comprehensive SDK documentation as top-level tab
Adds 27 new documentation pages for the Cline SDK (@clinebot/core,
@clinebot/agents, @clinebot/llms, @clinebot/shared) organized into
a dedicated "SDK" tab in the Mintlify docs navigation.

Structure:
- Getting Started: overview, quickstart, examples
- Core Concepts: agents, sessions, tools, streaming/events, extensions,
  hooks, providers/models
- Guides: building an agent, custom tools, writing extensions, permission
  handling, scheduled agents, multi-agent teams, connectors, production
- Architecture: layered stack overview, hub-spoke RPC, package reference
- API Reference: ClineCore, Agent, Gateway, Tools API, Events, Types
- CLI: commands, configuration, connector setup

Removes the old single-page SDK overview (cline-sdk/overview.md) that
documented the previous ACP-based ClineAgent API, and removes its
reference from the Cline CLI navigation group.
2026-04-21 22:05:32 -07:00
Dominic Cooney 8d020e89e6 chore: Publish regular nightlies to Cline (Nightly) *release* channel (#10338)
* Make the nightly publishing script use the stable channel of cline-nightly.

* Address PR review feedback from Greptile and Copilot

- Reject unknown CLI flags with an error message, preventing typos like
  --prerelease from silently publishing to the wrong channel (Greptile)
- Rename 'stable' to 'release' throughout docs, help text, and log
  messages to match VS Code Marketplace terminology (Copilot)
- Rename workflow step from 'Publish Extension as Pre-release' to
  'Publish Nightly Extension' since it now publishes to the release
  channel by default (Greptile)
2026-04-21 21:26:57 -07:00
Jose Castelli c6dbc8bcb0 Adding cline quota exceeded cap error message (#10323)
Adding cline quota exceeded cap error message
2026-04-21 11:51:40 +02:00
CandiedUniverseandArafatkatze 1862f15955 Remove foreground terminal from Cline VSCode extension (#10196)
* Create implementation plan doc

* Remove foreground terminal UI and default task execution to background mode

* Remove terminal mode UI service endpoint

* Remove foreground terminal mode state and RPC surface

* Add terminal settings UI regression test

* Guard removed foreground terminal state keys

* Test simplified terminal command routing

* Remove dead terminal profile plumbing

* Remove stale terminal mode references

* Add terminal settings verification story

* Remove implementation plan doc once implemented

* fix e2e launch under electron-run-as-node

* address greptile terminal follow-ups

* address greptile proto and vscode terminal notes

* address greptile test follow-ups

* remove dead acp terminal stubs

* Remove VS Code integrated terminal dependencies

* docs: sync integrated terminal removal plan status

* Remove terminal settings UI

* Remove terminal settings plumbing

* Mark terminal settings removal validated

* Remove implementation plan docs once implemented

* Polish shell integration warning UI

* Remove orphaned ACP terminal setters

* Add kanban install flow implementation plan

Start kanban install task from modal

Clarify kanban install task architecture

Verify kanban install task flow

Remove implementation plan doc once implemented

Restore direct terminal install launcher

Make the kanban installer change minimal and squashable

* Changes as per PR feedback

* Further deletions as per PR feedback

* Restore standalone kanban modal copy fallback

---------

Co-authored-by: Arafatkatze <arafat.da.khan@gmail.com>
2026-04-20 18:48:11 -07:00
Mikołaj Kondratek f6a9a02500 fix: set --max-old-space-size=8192 for cline-core node process (#10290)
* fix: set --max-old-space-size=8192 for cline-core node process

The cline-core Node.js process was launched without a V8 heap limit,
defaulting to ~2GB. Long conversations with large file reads cause
GC-thrashing and eventual OOM crashes. Set the limit to 8GB to provide
sufficient headroom for extended sessions.

* fix: set --max-old-space-size=8192 for cline-core node process
2026-04-20 09:42:08 -07:00
Tony Loehr 10af2439be docs: add prompt storage schema and OpenTelemetry events reference (#10195)
* docs: add prompt storage schema and OpenTelemetry events reference

- Add comprehensive prompt storage documentation (DEVREL-142)
  - Complete enterpriseTelemetry.promptUploading schema
  - Setup guides for AWS S3 and Cloudflare R2
  - Storage architecture and sync worker behavior
  - IAM policies and troubleshooting

- Add OpenTelemetry events catalog (DEVREL-143)
  - Document 80+ events across 8 categories
  - Example payloads and analytics query patterns
  - Integration examples for Datadog, Grafana, New Relic
  - Event schema reference and best practices

- Update monitoring documentation
  - Add cross-references between related pages
  - Update navigation in docs.json
  - Integrate new pages into Enterprise > Monitoring section

* fix: update broken link in telemetry.mdx to point to OTel events page

* docs: address PR review comments

- Fix file contents exclusion claim in prompt-storage.mdx
  - Remove misleading claim about file contents not being stored
  - Add warning that tool inputs (like write_to_file content) are included

- Standardize attribute naming in opentelemetry-events.mdx
  - Change model_id to model in event tables for consistency
  - Match actual emitted event schema shown in example payloads

- Add SQL syntax note in opentelemetry-events.mdx
  - Clarify that attribute access syntax is platform-specific
  - Provide examples for BigQuery and ClickHouse

* adjustments
2026-04-17 17:59:35 -04:00
9a8fbf9852 docs: update Anthropic docs for Claude Opus 4.7 release (#10295)
* docs: update Anthropic docs for Claude Opus 4.7 release

- Add claude-opus-4-7 and claude-opus-4-7:1m to Anthropic supported models
- Document adaptive thinking replacing classic extended thinking on Opus 4.7
- Document sampling parameter restrictions (temperature, top_p, top_k) on Opus 4.7
- Add claude-opus-4-7 to Claude Code supported models

* Update docs/provider-config/anthropic.mdx

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

* unblocker

---------

Co-authored-by: Ara <arafat.da.khan@gmail.com>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-04-17 11:51:45 -07:00
TheRealSpencer c5657a14bb chore: update axios to 1.15.0 across all packages (#10271)
- Update root package.json axios from 1.13.6 to 1.15.0
- Update evals/package.json axios from 1.13.6 to 1.15.0
- Update docs/package.json axios override from 1.13.5 to 1.15.0
- Regenerate all package-lock.json files
2026-04-17 10:52:28 -05:00
Robin NewhouseandClaude Opus 4.6 955ae8df7a feat: wire up remote globalSkills with enterprise UI and architectural fixes [ENG-1774] (#10283)
* feat: wire up globalSkills consumption from remote config

The remote config schema already includes globalSkills (merged in #10236).
The dashboard can save skills to remote config. This PR wires up the
extension to read and use them.

## Changes

### State storage (Layer 1)
- Add remoteGlobalSkills to REMOTE_CONFIG_EXTRA_FIELDS
- Add remoteSkillsToggles to GLOBAL_STATE_FIELDS

### Remote config transform/apply/clear (Layer 2)
- Map globalSkills → remoteGlobalSkills in transformRemoteConfigToStateShape
- Sync remoteSkillsToggles in applyRemoteConfig using frontmatter.name
  as the identity key (not entry.name)
- Clear remoteSkillsToggles in clearRemoteConfig

### Skill discovery (Layer 3)
- discoverSkills accepts optional remoteSkillEntries parameter (pure
  utility, no StateManager coupling)
- getSkillContent accepts optional remoteSkillEntries parameter for
  remote content loading without disk I/O
- Precedence: remote (enterprise) > disk-global (user) > project

### refreshSkills (Layer 3b)
- Reads remote entries from controller.stateManager, parses frontmatter,
  builds SkillInfo entries with alwaysEnabled field

### UseSkillToolHandler (Layer 4)
- Toggle filter checks remoteSkillsToggles for remote: prefixed skills
- Directory note omitted for remote skills
- Passes remoteSkillEntries to both discoverSkills and getSkillContent

### toggleSkill
- Routes remote: prefixed paths to remoteSkillsToggles keyed by name

### Proto + webview
- Added always_enabled field to SkillInfo proto message
- Modal passes isRemote + alwaysEnabled to RuleRow for remote skills
- Uses skill.name as display label for remote skills

## Design decisions
- frontmatter.name is the sole identity for remote skills (entry.name
  is ignored). This matches how local skills work.
- remote: path prefix distinguishes remote from disk skills in toggle
  stores and content loading.
- skills.ts remains a pure utility module with zero StateManager coupling.
  Callers inject remote entries as parameters.
- 42 unit tests covering discovery, precedence, content loading, toggle
  sync, and frontmatter parsing.

* fix: enforce alwaysEnabled in toggle sync to prevent stale false overrides

When applyRemoteConfig syncs skill toggles, synchronizeRemoteRuleToggles
preserves existing toggle values — including false. If an admin later
sets alwaysEnabled: true on a skill that a user had previously disabled,
the stale false toggle would survive the sync. The UI would show the
skill as locked-on (via the alwaysEnabled check in refreshSkills), but
UseSkillToolHandler's filter would see false in the toggle store and
exclude it, causing a 'Skill not found' error for a skill the user can
see is active.

Fix: after synchronizeRemoteRuleToggles, force any alwaysEnabled entry
with a false toggle back to true. This makes the toggle store the single
source of truth — both UI and handler now agree.

Adds 4 tests covering the alwaysEnabled enforcement edge cases.

* fix: deduplicate remote skill parsing, add drift validation, and fix architectural gaps

1. Extract shared parseRemoteSkillEntries utility (skills.ts)
   - Single validation point for remote skill entries, replacing duplicated
     frontmatter parsing in skills.ts, refreshSkills.ts, and remote-config/utils.ts
   - Enforces entry.name === frontmatter.name to catch drift between the
     dashboard and SKILL.md content (rejects with warning on mismatch)

2. Eliminate redundant frontmatter re-parsing in getSkillContent
   - Was re-parsing every entry's frontmatter to find a match by name
   - Now uses entry.name for lookup since drift validation guarantees equality

3. Enforce alwaysEnabled in UseSkillToolHandler
   - The toggle filter was missing the alwaysEnabled check, so a stale false
     toggle could hide an admin-locked skill from the model
   - Now matches the logic in refreshSkills.ts

4. Add remote_skills_toggles to SkillsToggles proto
   - toggleSkill now returns remoteSkillsToggles in the response, matching
     how remote rules/workflows already work

5. Separate Enterprise Skills section in UI
   - Remote skills now render under their own "Enterprise Skills" header,
     consistent with how rules and workflows display remote entries

6. Update tests for new validation behavior
   - Tests now use entry.name matching frontmatter.name (was deliberately
     mismatched before); added drift rejection tests

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: enable viewing remote skills and fix tooltip text in RuleRow

- openRemoteFile now handles remote://skill/{name} URIs (was only
  rule and workflow), looking up content from remoteGlobalSkills
- RuleRow's handleEditClick builds the correct URI type for skills
  (was falling through to "rule")
- Tooltip text now uses ruleType ("View skill file") instead of
  hardcoded "View rule file" for all remote entries

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: soften drift validation to warn-not-reject, fix content lookup fallback

The strict entry.name !== frontmatter.name rejection was silently hiding
org-configured skills when the dashboard's entry.name didn't match the
SKILL.md frontmatter name.

- parseRemoteSkillEntries now warns on drift but uses frontmatter.name as
  the canonical identity instead of rejecting the entry
- getSkillContent falls back to frontmatter match when entry.name lookup
  misses (handles drift for content loading)
- openRemoteFile falls back to frontmatter match for skill view (same
  reason)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: include remote skills in system prompt and fix remote config race

The system prompt generation called discoverSkills() without passing
remoteSkillEntries, so the model never learned about remote skills and
never invoked use_skill for them. This was the actual cause of remote
skills being invisible to the model despite showing in the UI.

Also fixes a race condition in applyRemoteConfig where clearRemoteConfig()
wiped the in-memory cache before repopulating it field-by-field. Any
concurrent reader (e.g., UseSkillToolHandler) during that window would
see an empty cache. Replaced with atomic replaceRemoteConfig() that
builds the new cache and swaps it in a single assignment.

- task/index.ts: pass remoteSkillEntries to discoverSkills, add
  remoteSkillsToggles + alwaysEnabled filtering (matching handler)
- StateManager: add replaceRemoteConfig() for atomic cache swap
- remote-config/utils.ts: use replaceRemoteConfig instead of
  clearRemoteConfig + setRemoteConfigField loop
- Remove debug logging from parseRemoteSkillEntries and handler

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: include remote skills in subagent path

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 18:38:02 -07:00
AraandClaude Sonnet 4.6 9405419efe v3.79.0 Release Notes (#10292)
* v3.79.0 Release Notes

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>

🔮 View transcript: https://agentlogs.beatrixwoo.workers.dev/s/i90f8jfxjc88hit3bs8v2e2k

* chore(cli): bump CLI to v2.15.0

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>

🔮 View transcript: https://agentlogs.beatrixwoo.workers.dev/s/i90f8jfxjc88hit3bs8v2e2k

* remove changeset

* remove changeset
2026-04-16 12:26:10 -07:00
f53dcb3096 feat(models): prepare Claude Opus 4.7 provider support (#10286)
* feat(models): prepare Claude Opus 4.7 provider support

* remove deprecated params for opus 4.7

- opus 4.7 doesn't accept params like temperature, top_p, top_k anymore.
This commit removes those params only for opus 4.7

* Agent hill climb fixes

* Anthropic adaptive thinking

* Removing 1m context switcher

* Removing 1m models fully

* Restore Anthropic 1M variants and context switchers

* Adding 1m

* remove changeset

* fix Opus 4.5 adaptive thinking detection

---------

Co-authored-by: Max Paulus 🥪 <max@cline.bot>
Co-authored-by: Arafatkatze <arafat.da.khan@gmail.com>
2026-04-16 12:02:28 -07:00
CandiedUniverseandgreptile-apps[bot] afa32bf801 fix: Stabilize flaky Windows CI test paths (#10291)
* docs: add CI flakiness stabilization plan

* test: harden global hook cwd timeout on windows

* test: stabilize CLI skills panel interactions

* ci: harden vscode test runtime setup

* test: stabilize BannerService timer behavior

* refactor: ignore CLI skills input while loading

* docs: update stabilization plan status

* docs: drop temporary stabilization plan

* Update cli/src/components/SkillsPanelContent.tsx

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

* Update .vscode-test.mjs

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

* test: expose banner service drain hook

* fix: stabilize CLI skills panel input state

* Change polling interval

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-04-16 11:27:56 -07:00
Ara 2d2d9d829a fix cache reflection for cline and vercel handlers (#10266) 2026-04-15 17:47:15 -07:00
John Choi 27a1b3da8a feat: user-level remote-config discovery with inline value reuse (#10056)
Replace the old client-side per-org scan for remote config with a single
discovery call to GET /api/v1/users/me/remote-config. Reuse the inline
config value when possible, falling back to the org-level endpoint only
when inline parse fails.

Key changes:
- Single discovery call replaces N org-level requests
- Resolve config before switching org to avoid stranding the user
- Transient errors preserve existing config (log-only, no clearing)
- authenticatedRequest() strict null vs undefined validation
- Auth precheck in fetchUserRemoteConfig() with token pass-through
2026-04-15 11:21:47 -07:00
Tony Loehr f5c8cd4384 docs: fix enterprise license link to point to contact sales (#10164)
Update the Cline Enterprise License link in enterprise onboarding prerequisites from https://cline.bot/enterprise to https://cline.bot/contact-sales
2026-04-15 10:00:26 -07:00
Tony Loehrandgreptile-apps[bot] fddabb6b8d docs: add Kanban remote access documentation (#10274)
* docs: add Kanban remote access documentation

Add comprehensive documentation for accessing Kanban remotely:
- Local network access (--host flag and KANBAN_RUNTIME_HOST env var)
- Tailscale for secure remote access
- Docker deployment
- SSH tunneling
- Ngrok for public URLs
- Cloudflare Tunnels with AWS CDK example

* Update docs/kanban/remote-access.mdx

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

* Update docs/kanban/remote-access.mdx

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-04-15 09:44:55 -07:00
Robin Newhouse 071f32ec92 fix: unblock stuck command_output ask when terminal command ends (#10269)
* fix: unblock pending command_output ask on terminal completion

* test: cover timeout and idempotent command_output ask release

* refactor: simplify pending command_output ask release guard
2026-04-14 12:56:55 -07:00
9bdb8a9362 fix(prompts): add use_subagents to GLM, Hermes, and XS TOOL_USE_SECTI… (#10200)
* fix(prompts): add use_subagents to GLM, Hermes, and XS TOOL_USE_SECTION overrides

These variants use hardcoded TOOL_USE_SECTION templates that bypass the
auto-generated tool descriptions. When use_subagents was added as a new tool,
it was registered in each variant's .tools() config but was never added to the
hardcoded override templates — so models using these variants never saw
use_subagents in their system prompt and could not call it.

This adds the use_subagents description block to the TOOL_USE_SECTION override
templates for glm, hermes, and xs variants, and updates the corresponding
test snapshots.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(prompts): gate use_subagents on subagentsEnabled and isSubagentRun context

The previous commit added use_subagents to the GLM, Hermes, and XS
TOOL_USE_SECTION override templates unconditionally. This was incorrect —
the canonical tool spec gates use_subagents with:
  context.subagentsEnabled === true && !context.isSubagentRun

Without this guard, models would advertise use_subagents even when
subagents are disabled by the user, and subagent runs could recursively
spawn further subagents.

This commit:
- Wraps the use_subagents block in all three templates with the same
  subagentsEnabled && !isSubagentRun conditional
- Converts HERMES_TOOL_USE_TEMPLATE from a plain string constant to a
  function so it can access context (matching the pattern used by GLM
  and XS templates)
- Updates snapshots accordingly

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(prompts): align use_subagents rendering guard with tool context requirements

---------

Co-authored-by: sunghyun <jjinjukks1227@gmail.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 12:41:23 -07:00
Tomás Barreiro 2d994530fd Add support for Azure Blob Storage (#10264)
* Add support for Azure Blob Storage

* fix comment
2026-04-14 18:42:39 +02:00
tjandy98 e477f8fa04 Add gpt-5.2 (#10024) 2026-04-13 19:36:22 +02:00
Tomás Barreiro 36b0baec81 Add globalSkills to remote config (#10236) 2026-04-13 19:36:09 +02:00
Tomás Barreiro a0faf7c677 Fix action injection risk (#10230) 2026-04-10 23:05:06 +02:00
Tomás Barreiro 1dcf356f98 Remove old evals tool (#10226)
* Remove old evals tool

* remove script
2026-04-10 23:04:51 +02:00
CandiedUniverseandgreptile-apps[bot] 71d795eec8 Changelog and version bump for release (#10227)
* Update changelog files for release

* Version bump for release

* Update cli/CHANGELOG.md

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-04-10 12:48:17 -07:00
TheRealSpencer 792b9e89a1 update security policy to include bugcrowd vdp (#10117) 2026-04-10 13:21:35 -05:00
Roberto Langaricaandgreptile-apps[bot] 10197b038d feat(chat): add SpendLimitError UI for SPEND_LIMIT_EXCEEDED (429) (#10207)
* feat(chat): add SpendLimitError UI for SPEND_LIMIT_EXCEEDED (429)

When the Cline backend returns a 429 with code SPEND_LIMIT_EXCEEDED (org
budget cap hit), the chat error flow now shows a dedicated SpendLimitError
component instead of falling through to the generic rate-limit message.

Changes:
- proto/cline/account.proto: add submitLimitIncreaseRequest RPC +
  SubmitLimitIncreaseResponse message
- src/services/error/ClineError.ts: add SpendLimit error type; detect
  SPEND_LIMIT_EXCEEDED before the generic rate-limit pattern check
- src/services/account/ClineAccountService.ts: add
  submitLimitIncreaseRequestRPC() calling POST /api/v1/users/me/budget/request
- src/core/controller/account/submitLimitIncreaseRequest.ts: new gRPC
  handler wired automatically by npm run protos
- webview-ui/src/components/chat/SpendLimitError.tsx: new card component
  mirroring CreditLimitError; shows spent/limit amounts, resets_at, org
  attribution, and a Request Increase button with 5-min localSto
When the Cline backend returns a 429 with code SPEND_LIMIT_EXCEEDED (org
budgetrors to
  SpendLbudget cap hit), the chat budget_period,limit_usd,spent_usd,resets_at}
- component instead of falling through to the generic rate-limnd Limit
  Reac
Changes:
- proto/cline/account.p

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

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

* chore: shorten spend limit error message verbiage

* fix(storybook): align spend limit story messages with component output

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-04-09 14:55:58 -07:00
John Choi 9390d3f933 fix: update KanbanMigrationView test to match component text (#10173)
#10161 updated the component copy but missed updating the test assertions.
This breaks CI on main and any branch based on it.
2026-04-07 16:27:38 -07:00
Saoud Rizwan 5df470bf48 fix(cli): update kanban migration view copy to not imply TUI deprecation (#10161)
The previous copy ("Cline is moving out of the terminal", "old CLI")
gave the impression that the terminal TUI was being deprecated. Updated
to frame Kanban as the new default while making clear the TUI is still
fully available.
2026-04-06 16:00:11 -07:00
Tony Loehr 034c4342d1 Complete documentation for environment variable-based OpenTelemetry configs (#10155)
* Complete documentation for environment variable-based OpenTelemetry configuration

* Address PR review comments

- Add Values column to OTLP Configuration table for consistency
- Fix New Relic endpoint to include required port 4318
- Add note about Datadog region-specific endpoints
2026-04-06 17:22:57 -04:00
Tony Loehr 349295ab2c docs: remove Teams Plan references, focus on Enterprise (#10153)
Remove Teams Plan as an option for seat upgrades in the managing-members documentation. This aligns with the product strategy to drive customers toward Enterprise for any multiplayer/team scenarios.

Related: https://github.com/cline/cline-web/pull/256
2026-04-06 11:28:50 -07:00
841402c178 docs: add all enterprise inference providers to Remote Provider Configuration (#10127)
* docs: add OpenAI Compatible and Anthropic to enterprise SaaS provider configuration

Add documentation for all inference providers supported by the core
platform's remote config system (ProviderSettingsSchema). Previously
only Bedrock, Vertex, and LiteLLM had dedicated pages.

New pages:
- OpenAI Compatible admin/member config (covers Azure Foundry)
- Anthropic admin/member config

Updated:
- overview.mdx: expanded provider table to 7 rows
- docs.json: added navigation groups
- Capabilities Manual: added provider links

* Update docs/enterprise-solutions/configuration/remote-configuration/overview.mdx

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

* Update docs/enterprise-solutions/configuration/remote-configuration/overview.mdx

Co-authored-by: Renee Huang <100229782+reneehuang1@users.noreply.github.com>

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: Renee Huang <100229782+reneehuang1@users.noreply.github.com>
2026-04-06 03:51:43 +00:00
Robin Newhouse e52a052c81 Show read_file line ranges in chat UI (#10090)
Surface the actual read_file line window in chat summaries so users can see what context was added, while keeping manual approval and repeated same-file reads rendered accurately.
2026-04-02 11:26:13 -07:00
Saoud Rizwan 7a91a9be2e Bump version from 2.12.0 to 2.13.0 2026-04-01 20:09:42 -07:00
Saoud Rizwan 93a494d009 feat(cli): simplify unified update flow for cline and kanban (#10099)
* feat(cli): unify update flow for cline and kanban

* fix(cli): only install kanban when update is available

* fix(cli): include cline and kanban versions in no-update message
2026-04-01 20:08:19 -07:00
CandiedUniverse 2bd21f8a45 Update changelog and release version (#10085) 2026-04-01 10:02:58 -07:00
John Simone 2f33f71ebd Add lazy teammate mode (#10081) 2026-04-01 08:55:43 -07:00
TheRealSpencer dec10aaec3 pin axios version due to current package integrity (#10060)
* pin axios version due to current package integrity

* update axios versions
2026-03-31 11:02:20 -07:00
Dominic Cooney c09705a45f Add Linux ARM64 (aarch64) support for JetBrains plugin (#10062)
- Add linux-aarch64 to TARGET_PLATFORMS in package-standalone.mjs so
  better-sqlite3 prebuilt binaries are downloaded for this platform.
- Rename linux-arm64 to linux-aarch64 in download-ripgrep.mjs for
  consistency with the JetBrains plugin naming convention (which uses
  Java's os.arch value 'aarch64').
2026-03-30 23:57:12 -07:00
Saoud Rizwan 73058c871a ci: post release changelogs to #releases Slack channel (#10057) 2026-03-30 19:43:00 -07:00
03211f1364 feat(sdk): add fetchFeaturebaseToken method [ENG-1673] (#10005)
* feat(sdk): add fetchFeaturebaseToken method

- Add fetchFeaturebaseToken() to ClineAccountService
- Add FeaturebaseTokenResponse type and endpoint constant
- Add E2E mock server handler and unit tests

Ref: ENG-1673

* fix: address review feedback - narrow test glob and add JSDoc

- Fix P2: scope mocharc glob to **/*.test.ts to avoid matching non-test files
- Fix P2: add JSDoc comment to fetchFeaturebaseToken for consistency

---------

Co-authored-by: John Choi <john.choi@cline.bot>
Co-authored-by: John Choi <johnwschoi@users.noreply.github.com>
2026-03-30 14:48:53 -07:00
CandiedUniverse 65e9727c65 Add is_remote_workspace metric (#10019)
* Create implementation plan doc

* feat(telemetry): track remote workspace metadata

* test(workspace): assert telemetry emission during setup

* Remove implementation plan doc

* Improvements pre- code review

* Fix as per Greptile feedback
2026-03-27 19:04:07 -07:00
Robin Newhouse 884fbfb21e feat(read_file): add chunked reading with start_line/end_line parameters (#9711)
* fix(read_file): add stable line labels in act/plan

* prompt: clarify read_file line labels for replace_in_file

* Update prompt snapshots

* feat(read_file): add chunked reading with start_line/end_line parameters

Add optional start_line and end_line parameters to read_file so models
can read files in chunks instead of loading entire files into context.
Default limit is 1000 lines per read, with a continuation hint guiding
the model to paginate when needed.

Made-with: Cursor

* feat: align read_file line format with SDK while keeping superior chunking

- Change line format from 'L1:' to '1 |' to match SDK format
- Add proper NaN validation for start_line/end_line parameters
- Keep 1000-line chunking with continuation hints (superior to SDK)
- Update tool description and tests to reflect new format
- Add directory usage guardrail back to tool description
- Update replace_in_file prompt to reference new line format

This aligns the PR with the newer SDK design patterns while
preserving the superior chunking behavior that prevents context
overflow issues.

* fix: resolve duplicate step numbering in replace_in_file instructions

Fixes the duplicate step 5 issue when NOTEBOOK_INSTRUCTIONS is
concatenated with BASE_DIFF_INSTRUCTIONS for .ipynb files.
Changes notebook instructions to step 6 to avoid ambiguity.

* test: update unit test snapshots

* test: fix gemini3 tools snapshot

* test: regenerate gemini3 tools snapshot

* fix: preserve line ranges on cached file reads

* test: refresh gemini3 tools snapshot

* test: strengthen chunked read assertions

* fix: normalize inverted read file ranges
2026-03-27 12:24:57 -07:00
Dominic Cooney 0c880c7bb1 fix: Serve H.264/MP4 for VSCode and VP9/WebM for JetBrains (#10012)
VSCode's webview doesn't reliably play VP9/WebM, while JetBrains'
JCEF lacks H.264 decoding. Restore the original H.264/MP4 video and
use HTML5 <source> elements with explicit MIME types so each platform
picks the format it supports:

  <source src=... type="video/mp4" />   <!-- VSCode -->
  <source src=... type="video/webm" />  <!-- JetBrains -->

Also:
- Restore .mp4 Git LFS tracking in .gitattributes
- Update CI LFS verification to check both files
- Add webm to JetBrains MIME type map (BrowserRequestHandler)
2026-03-27 03:35:20 -07:00
Dominic Cooney a915122c6d fix: Transcode kanban demo video from H.264/MP4 to VP9/WebM (#9997)
JetBrains IDEs use JCEF (Chromium Embedded Framework) for webviews,
which often does not include H.264 decoding due to licensing
restrictions. This causes the kanban demo video to fail to play.

Transcode the video from H.264/MP4 to VP9/WebM, which is a royalty-free
codec universally supported in Chromium derivatives. This also reduces
the file size from 3.6MB to ~900KB.

Updated all references:
- Source component import (ClineKanbanLaunchModal.tsx)
- Git LFS tracking (.gitattributes)
- CI workflow LFS checks (publish.yml, publish-nightly.yml)
2026-03-27 07:39:05 +09:00
Robin Newhouse 835ed94736 fix: exclude new_task tool from system prompt in yolo/headless mode (#9958) 2026-03-26 12:10:47 -07:00
Robin Newhouse 00ca3d13fa test(oca): add messages api routing and stream helper tests (#9494) 2026-03-26 12:02:13 -07:00
3f3a87aed9 Hooks: Notification hook polish (#9909)
* Create implementation plan doc

* Implement notification hook helper

* Remove generated proto artifacts from branch

* Remove implementation plan doc

* Update src/core/hooks/__tests__/notification-hook.test.ts

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

* Update src/core/hooks/notification-hook.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>
Co-authored-by: Tony Loehr <turingxo@gmail.com>
2026-03-26 10:10:15 -07:00
John SimoneandRenee Huang 7a0f11837e Add Kanban docs (#9988)
* add kanban docs

* fix rendering error in overview, drop kanban flags from commands

* fix stray link

---------

Co-authored-by: Renee Huang <100229782+reneehuang1@users.noreply.github.com>
2026-03-26 08:12:09 -07:00
Max 03d2d01eed chore: bump version to 3.76.0 (#9986) 2026-03-26 07:52:28 -07:00
Dominic Cooney db1b1c45bd chore: update dependencies flagged by npm audit (#9980) 2026-03-26 21:03:35 +09:00
Saoud Rizwan a4131e57d8 fix(cli): replace --tui hint with kanban description in migration view
The migration announcement didn't explain what Kanban is. Swap the
"run cline --tui" line for a one-liner describing the product so users
know what they're opting into. The --tui escape hatch is still
discoverable via the Exit menu item.
2026-03-25 22:52:22 -07:00
Saoud Rizwan 683dd9a741 chore(cli): bump version to 2.11.0 2026-03-25 19:24:48 -07:00
Saoud Rizwan 0d437f71b0 fix(cli): remove noisy install message when launching kanban 2026-03-25 19:24:20 -07:00
Saoud Rizwan fc77c0faea chore(cli): bump version to 2.10.0 2026-03-25 18:38:24 -07:00
Saoud Rizwan 12c85c4233 Update tips about kanban 2026-03-25 18:26:06 -07:00
Saoud Rizwan d6b7a1ab41 fix(cli): launch kanban directly with package manager fallback 2026-03-25 18:26:06 -07:00
CandiedUniverse 3926e7b404 Stabilize auth e2e onboarding tests and clean up mock server URL parsing (#9974)
* Fix tests that fail when kanban modal is visible

* Fix deprecation warning

* Improve isVisible() as per Greptile suggestion
2026-03-25 16:35:09 -07:00
Tony Loehr 5ba4314b9a feat: add toggle to disable feature tips in chat (#9973) 2026-03-25 15:28:09 -07:00
9ccad7f764 refactor: replace hand-rolled YAML parser in refreshSkills with shared helper (#9956)
* refactor: replace hand-rolled YAML parser in refreshSkills with shared helper

The refreshSkills controller had its own line-by-line YAML parser that
only handled simple key: value pairs. Replace it with the shared
parseYamlFrontmatter helper already used by the skills discovery path.

Same output for name and description fields. The shared parser handles
edge cases (arrays, nested values, quoted colons) more robustly.

* refactor: inline parseYamlFrontmatter, remove redundant wrapper

Remove the parseFrontmatter wrapper since only `data` is used by
the caller. Inline the call directly at the use site.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: John Choi <johnchoi@MacBook-Pro.local>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 09:06:15 -07:00
Saoud Rizwan 3562f54dbf fix(cli): enable shell mode for kanban spawn on Windows
npx.cmd requires shell: true on Windows to resolve correctly.
2026-03-25 07:35:53 -07:00
OctopusandPR Bot d427d5d76a feat: upgrade MiniMax default model to M2.7 (#9886)
* feat: upgrade MiniMax default model to M2.7

- Add MiniMax-M2.7 and MiniMax-M2.7-highspeed to model list
- Set MiniMax-M2.7 as default model
- Keep all previous models as alternatives
- Update provider documentation

* fix: correct M2.7 cache pricing and update prompt caching docs

- Updated cacheReadsPrice from 0.015 to 0.03 for both MiniMax-M2.7 and
  MiniMax-M2.7-highspeed to match M2.5 pricing (same cache read rate
  across M2.x models)
- Updated prompt caching tip to explicitly mention highspeed variants

* fix: correct MiniMax model pricing to match official rates

- M2.7-highspeed/M2.5-highspeed/M2.1-lightning: $0.60/$2.40 (not $0.30/$1.20)
- M2.7 cache: reads $0.06/M (not $0.03), writes $0.375/M (not $0.0375)
- All models: cache writes $0.375/M (not $0.0375)

Ref: https://platform.minimax.io/docs/llms.txt

---------

Co-authored-by: PR Bot <pr-bot@minimaxi.com>
2026-03-25 05:17:13 -07:00
Saoud Rizwan 072e2887b0 fix(cli): add --tui flag to TUI e2e tests to bypass Kanban redirect
The Kanban launch-by-default feature (beb54a4) redirects bare `cline`
invocations to Kanban, which blocks all TUI tests that launch without
`--tui`. Adding the flag ensures tests reach the legacy TUI as expected.
2026-03-25 04:36:12 -07:00
Saoud Rizwan 9c71a6f021 feat(webview): add Cline Kanban launch modal and queue announcements (#9963)
* feat(webview): add dedicated Cline Kanban launch modal

* fix(ci): fetch LFS media assets in publish workflows
2026-03-25 04:32:09 -07:00
Saoud Rizwan 7627a382aa feat(cli): launch kanban by default with migration view (#9914)
* feat(cli): refresh welcome banner and kanban launcher

* feat(cli): launch kanban by default with migration view

* feat(cli): add kanban process lifecycle management

Detach the kanban child process on Unix so it gets its own process
group, forward signals to the group for graceful shutdown, and fall
back to SIGKILL after a 10s timeout. Resolves exit codes from signals
correctly (130 for SIGINT, 143 for SIGTERM).
2026-03-25 02:01:56 -07:00
Tomás Barreiro 978c633c90 Add KanBan to remote config [PF-632] (#9959)
* Add KanBan to remote config

* Update tests
2026-03-25 02:33:52 +01:00
Tony Loehrandgreptile-apps[bot] e807b520e0 feat(cli): add rotating feature tips during thinking/acting phases (#9874)
* feat(cli): add rotating feature tips during thinking/acting phases

Port the FeatureTip component from the VSCode extension (PR #9799) to the
CLI. Shows rotating educational tips below the ThinkingIndicator while Cline
is processing, appearing after a 2-second delay and cycling every 8 seconds.

Includes 17 tips covering:
- Core features: .clinerules, Plan/Act mode, checkpoints, MCP servers
- CLI-specific: /settings, /skills, /history, /compact, /reportbug
- Workflows: kanban, auto-approve toggle, history navigation, @ mentions
- Images via --images flag, browser testing, double-check completion

* Update FeatureTip.tsx

* Apply suggestions from code review

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

* fix: resolve FeatureTip.tsx syntax errors (duplicate export, missing tipIndex state)

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-03-24 17:57:35 -07:00
f3a3f30db5 feat: add repeated tool call loop detection (#9933)
* feat: add repeated tool call loop detection

Detect when the LLM calls the same tool with identical arguments
repeatedly, which wastes tokens without making progress. This is
the #1 active complaint in the Cline issue tracker (13+ open issues
including #9923, #9916, #9846, #9816).

Two-stage escalation:
- Stage 1 (3 identical calls): inject a warning nudging the LLM
  to try a different approach
- Stage 2 (5 identical calls): trigger the existing
  consecutiveMistakeCount escalation (asks user or fails in YOLO)

Detection uses JSON.stringify with a sorted key replacer for
deterministic comparison. Metadata params like task_progress
(which change every call even when actual tool arguments are
identical) are stripped from the comparison.

Complementary to fileReadCache, which deduplicates file content but
still allows the tool call to succeed and consume a turn. Loop
detection catches the repeated call pattern itself.

Changes:
- loop-detection.ts: shared helper (toolCallSignature, checkRepeatedToolCall)
- TaskState.ts: add lastToolParams, consecutiveIdenticalToolCount
- ToolExecutor.ts: call checkRepeatedToolCall in handleCompleteBlock
- responses.ts: add formatResponse.repeatedToolCall
- loop-detection.test.ts: 5 tests

Manually verified: CLI test confirms soft warning at call 3,
YOLO mode failure at call 5.

Refs: #9923, #9916, #9846, #9816

* fix: address review feedback on loop detection

- Change hardEscalation threshold from >= to === so it fires exactly
  once at count 5, matching softWarning behavior
- Widen toolCallSignature param type to Partial<Record<string, string>>
  to match actual block.params type from ToolExecutor
- Add negative boundary assertions to verify no false positives at
  calls 0, 1, 3, 4

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: reset loop detection state when user continues after mistake_limit_reached

When hard escalation fired at count === 5 and the user clicked
"continue", consecutiveIdenticalToolCount was never reset. The count
would exceed 5 but === 5 never matched again, silently disabling
loop detection for the rest of the task.

Reset consecutiveIdenticalToolCount, lastToolName, and lastToolParams
alongside the existing consecutiveMistakeCount reset so the detector
fully re-arms after the user continues.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: John Choi <johnchoi@MacBook-Pro.local>
2026-03-24 13:40:31 -07:00
Igor TceglevskiiandCopilot Autofix powered by AI f1c7934064 fix: prevent OOM crash from globby's eager .gitignore scanning in lis… (#9917)
* fix: prevent OOM crash from globby's eager .gitignore scanning in listFiles

Replace globby's gitignore:true (which reads ALL .gitignore files in the
entire tree upfront, including inside gitignored directories) with
incremental .gitignore reading during BFS traversal.

In projects with large gitignored vendored dependencies containing many
nested repos, globby collects thousands of patterns, builds a massive
regex, and V8 runs out of memory during regex compilation (~488MB).

The fix reads .gitignore files only from directories the BFS actually
enters. Gitignored directories are never entered, so their .gitignore
files are never parsed and the pattern count stays small.

- Set gitignore:false, handle .gitignore ourselves
- Read root .gitignore in buildIgnorePatterns() to seed initial patterns
- Read subdirectory .gitignore lazily during globbyLevelByLevel BFS
- Accumulate patterns in currentIgnore so deeper levels respect them
- Add 4 tests: root patterns, file patterns, subdirectory .gitignore,
  and OOM-prevention (no reading inside gitignored dirs)

* Code review followup

* Potential fix for code scanning alert no. 147: Incomplete string escaping or encoding

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>

---------

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
2026-03-22 15:20:57 -07:00
Robin Newhouse 1d42da5248 Add tool-specific error messages for replace_in_file and execute_command (#9710)
Replace generic "missing parameter" errors with targeted guidance for
the two tools observed failing most in SWE-bench (6% of failures).
The new messages include the expected format (SEARCH/REPLACE blocks,
XML example) without the 30-line boilerplate reminder.

Made-with: Cursor
2026-03-21 11:57:00 -07:00
7f3974a827 fix: read cache_write_tokens from OpenRouter API instead of hardcoding 0 (#9871)
* fix: read cache_write_tokens from OpenRouter API instead of hardcoding 0

- Read prompt_tokens_details.cache_write_tokens from OpenRouter stream usage
  chunks instead of hardcoding cacheWriteTokens to 0
- Read native_tokens_cache_write from generation endpoint fallback
- Replace fragile hardcoded model ID switch statement for cache_control blocks
  with prefix-based matching (anthropic/, minimax/) so new models automatically
  get prompt caching enabled
- Add unit test verifying cache_write_tokens are correctly extracted

Co-authored-by: Ara <arafat.da.khan@gmail.com>

* fix: read cache_write_tokens from OpenRouter API instead of hardcoding 0

- Read prompt_tokens_details.cache_write_tokens from OpenRouter stream usage
  chunks instead of hardcoding cacheWriteTokens to 0
- Read native_tokens_cache_write from generation endpoint fallback
- Replace fragile hardcoded model ID switch statement for cache_control blocks
  with prefix-based matching (anthropic/, minimax/) so new models automatically
  get prompt caching enabled
- Add unit test verifying cache_write_tokens are correctly extracted

Co-authored-by: Ara <arafat.da.khan@gmail.com>

* Release v3.74.0 Notes

* Release v3.74.0 Notes

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: alex-lum <alex@cline.bot>
2026-03-20 15:46:29 -07:00
CandiedUniverse d40ab56aff Changelog and version bump for release (#9910) 2026-03-20 15:29:45 -07:00
CandiedUniverse bbdf445db7 fix: prevent resume asks from unblocking on abort (#9908)
* fix: prevent resume asks from unblocking on abort

* Add test case for resume_completed_task

* Prevent flaky tests
2026-03-20 10:51:16 -07:00
CandiedUniverse d992a3bf21 Stabilize hooks fixture tests with per-scenario isolation (fix flaking tests) (#9868)
* Refactor flaking hooks tests

Fix flaking hooks tests

Strengthen hooks fixture test isolation

* Address Greptile hooks test review

* Harden Windows hook timing tests

* Loosen hook cancellation timing margins

* Fixing more flaking tests
2026-03-20 09:50:14 -07:00
ace95988f8 fix: resolve deadlock when clicking New Task/Exit after task completion (#9905)
The presentation scheduler introduced in ff05ec3bb awaits in-flight
flushes during dispose(), but ask() blocks indefinitely on pWaitFor
waiting for user input. When abortTask() sets abort=true and then
awaits scheduler.dispose(), the in-flight flush (blocked on
ask("completion_result")) never resolves, deadlocking the UI.

Add abort flag check to ask()'s pWaitFor condition so blocked asks
unblock immediately when the task is aborted.

Co-authored-by: Max Paulus 🥪 <max@cline.bot>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 08:53:21 -07:00
Tony Loehrandgreptile-apps[bot] 54726f1677 docs: fix SDK documentation accuracy and completeness (#9856)
* docs: fix SDK documentation accuracy and completeness

- Fix setPermissionHandler API Reference to use correct async/return signature
  (was showing old callback pattern with (request, resolve) => void)
- Remove non-existent PermissionResolver type from Exported Types table
- Fix PermissionHandler type description to match actual signature
- Add missing hooksDir option to ClineAgentOptions documentation
- Fix newSession() example to use real model IDs
- Replace developer personal path in Full Example with generic path
- Use placeholder for version in initialize() example to avoid staleness
- Expand Stop Reasons table and add note about current implementation
- Add missing key exported types: AcpSessionStatus, AcpSessionState,
  RequestPermissionRequest/Response, PermissionOption, SessionUpdatePayload,
  SessionModelState, ModelInfo, TextContent/ImageContent/AudioContent,
  SetSessionMode/Model request/response types, TranslatedMessage

* docs: improve SDK visibility and disambiguate from API code examples

- Move SDK page higher in Cline CLI nav (after Installation, before Interactive Mode)
- Add sidebarTitle 'SDK (Programmatic Use)' for clearer nav label
- Rename api/sdk-examples to 'Code Examples' to avoid naming confusion with the Cline SDK
- Update API overview card title to match

* Apply suggestions from code review

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

* docs: add ClientCapabilities, Error Handling, and BYO API key docs to SDK

- Document clientCapabilities object and its effect on agent behavior
- Add Error Handling section with all throwable errors per method
- Expand BYO API key setup with concrete CLI auth examples

* docs: fix duplicated Stop Reasons table rows from code review

* docs: fix 3 accuracy issues found in source code audit

- Fix protocolVersion: was '0.9.0' (fabricated), actually 1 (number) from @agentclientprotocol/sdk
- Fix clientCapabilities: was claiming they change SDK behavior, but ClineAgent always uses standalone providers (capabilities only matter via AcpAgent stdio wrapper)
- Fix permission options: remove reject_always (never sent by agent, only allow_once/allow_always/reject_once are used)

* docs: clarify custom clineDir usage with CLI --config flag

Address PR review feedback: the BYO auth section mentioned custom
clineDir without showing how to target it from the CLI. Remove the
vague reference and add explicit --config flag documentation with
side-by-side SDK and CLI examples.

* docs: remove misleading 'by default' qualifier from SDK BYO auth section

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-03-19 21:01:24 -07:00
Saoud Rizwan 6308fef0a9 fix(cli): use kanban@latest to always fetch newest version (#9898)
* fix(cli): use kanban@latest to always fetch newest version

npx -y kanban may use a cached version. Using @latest ensures
users always get the most recent kanban release.

* chore(cli): bump version to 2.8.2 and update changelog
2026-03-19 19:37:53 -07:00
CandiedUniverse b3fc79b8ce Remove example hooks (#9896) 2026-03-19 18:42:55 -07:00
CandiedUniverse ff05ec3bbe Latency improvements for remote workspaces (#9858)
* Implement minimal change set for latency improvements

Cline's code review improvements

Further code review improvements

* fix: address code review issues for presentation scheduler

- Add reset() method to TaskPresentationScheduler to prevent stale
  timer leaks between API request retries within the same task
- Call presentationScheduler.reset() in streaming state reset section
- Fix hadVisibleAssistantContent to track reasoning content, not just
  text, preventing every reasoning chunk from getting immediate priority
- Remove unused 'low' priority from PresentationPriority type and
  associated cadence configuration (YAGNI)
- Trim TaskLatencyTrigger to only values actually used: text/reasoning/tool
- Add JSDoc to isRemoteWorkspaceEnvironment documenting the heuristic
  fallback and its false-positive risk
- Add Logger.warn for invalid cadence env-var overrides
- Add comment at scheduler construction explaining detection promise
  dependency on remoteWorkspaceDetectionPromise
- Add comments to CLI host bridge about intentional remoteName omission
- Expand test coverage: reset(), coalescing, priority upgrade tests

* fix: address code review issues in latency/presentation scheduler

- Fix #1/#3: Set didPresentAnyContent=true for text and tool_calls chunks
  so coalescing actually activates for text-only and tool streams (was
  only set for reasoning chunks, defeating the 40/90ms cadence goal)

- Fix #2: Re-check flushInProgress after awaiting in-flight flush in
  runFlushCycle to prevent two concurrent callers from both proceeding
  past the guard and starting concurrent flushes

- Fix #4: Document flushNow() disposed no-op contract so callers
  understand the finalization guarantee

- Fix #5: Add disposed guard to reset() to prevent post-dispose state
  mutation

- Fix #6: Remove platform/version heuristic from isRemoteWorkspaceEnvironment
  — only use remoteName. The substring match on 'remote' produced false
  positives for version strings like '1.0.0-remote-fix'. Non-VSCode hosts
  should populate remoteName explicitly to opt in to the higher cadence.

- Fix #7: Add remoteWorkspaceDetectionSettled flag and warn if getDelayMs
  is called before detection resolves, making the timing dependency
  explicit and detectable in production logs

- Fix #8: Upgrade disabled-scheduler bypass error from Logger.debug to
  Logger.warn so silent flush failures are visible

- Update latency.test.ts: replace heuristic test with false-positive
  regression tests and add null/empty-field coverage

* fix: address code review issues in latency/presentation scheduler

- Fix flushNow race condition: wait for all in-flight flushes to drain
  before setting pendingPriority so the post-flush continuation cannot
  steal it, guaranteeing at least one flush runs after flushNow() returns
- Add regression test for the flushNow race condition
- Fix disabled-path (CLINE_DISABLE_PRESENTATION_SCHEDULER): use
  presentationScheduler.flushNow() instead of a fire-and-forget void
  call so the presentAssistantMessage lock/pending-updates mechanism
  is respected when the scheduler is bypassed
- Rename didPresentAnyContent -> didScheduleAnyContent to accurately
  reflect that content has been scheduled, not necessarily flushed
- Update stale comment in streaming loop to match new variable semantics
- Upgrade remote workspace detection failure log level from debug to warn
- Document reset() interaction with in-flight flush on already-reset state
- Document empty-string remoteName edge case in getHostVersion.ts

* refactor: polish presentation scheduler for production readiness

- Extract PresentationPriority type to shared presentation-types.ts,
  breaking the latency.ts → TaskPresentationScheduler.ts type coupling
- Remove redundant pendingWhileFlushing flag from TaskPresentationScheduler;
  pendingPriority alone is sufficient for the post-flush continuation check
- Cache env var reads in latency.ts at module load (hot path optimization)
- Inline flushAssistantPresentation() one-liner into the scheduler constructor
- Make processNativeToolCalls protected to enable type-safe test access
- Remove as any cast in Task.processNativeToolCalls.test.ts

* Fixes as per Greptile review feedback

* Further fixes as per Greptile feedback

* Further fixes as per Greptile feedback
2026-03-19 17:05:49 -07:00
Ara bde7049c01 Release v3.74.0 Notes (#9879) 2026-03-18 19:08:50 -07:00
Ara 7cd06744ad feat: implement dynamic free model detection for Cline API (#9878)
Replace hardcoded free models list with runtime resolution from
recommended models. The handler now dynamically fetches free model
IDs using refreshClineRecommendedModels with fallback to static
defaults, and normalizes model IDs for consistent comparison.
2026-03-18 18:55:06 -07:00
shey-cline c88d3238cf Replace Error Message When Not Logged In to Cline (#9632) 2026-03-18 16:01:06 -07:00
jessy-clineandSaoud Rizwan 852ca2348f fix(ui): align ClineRulesToggleModal padding with ServersToggleModal (#9870)
Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-03-18 14:13:39 -07:00
CandiedUniverse 7091ccf2c7 Fix flaky CLI Enter-key handling across Windows/test environments (#9867)
* fix(cli): normalize enter key handling in ink inputs

* fix(cli): pass input through auth select enter handling
2026-03-18 12:20:25 -07:00
Mathisandgreptile-apps[bot] ad6c33ac5b feat: add file read deduplication cache to prevent repeated reads (#9836)
* feat: add file read deduplication cache to prevent repeated reads

- Add fileReadCache to TaskState for tracking read files per task
- ReadFileToolHandler checks cache before reading, returns cached content on repeat reads
- Warns model after 3+ reads of same file to stop re-reading
- WriteToFileToolHandler and ApplyPatchHandler invalidate cache on file changes
- Reduces wasted API tokens from models reading same files repeatedly

* fix: address file read cache gaps - image blocks, execute_command, redundant invalidation

* Update src/core/task/tools/handlers/ExecuteCommandToolHandler.ts

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

* PR changes

- __`TaskState.ts`__ — Simplified cache type from `{ content: string; readCount: number; imageBlock? }` to `{ readCount: number; mtime: number; imageBlock? }`. Dropped `content` to save memory; added `mtime` for external change detection.

- __`ReadFileToolHandler.ts`__ — Four improvements:

  - __Removed redundant `.set()` call__ — reviewer was correct that objects are modified by reference in Map
  - __Added mtime-based cache validation__ — on cache hit, `stat()` the file and compare mtime. If the file was modified externally (user edited in their editor), the cache entry is evicted and a fresh read occurs
  - __Dropped content from cache__ — cache now only stores metadata (readCount, mtime, imageBlock). On cache hits, the file is re-read from disk, addressing memory concerns
  - __Softened readCount >= 3 warning__ — removed the aggressive "Do NOT read this file again" language; now says "Please use the information you already have and proceed with your task"

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-03-18 10:38:23 -07:00
Tony Loehrandgreptile-apps[bot] 71e312e92a feat: add feature tips tooltip during thinking state (#9799)
* feat: add feature tips tooltip during thinking state

Show rotating feature tips below the Thinking indicator to keep users engaged and educate them on features like Double-Check Completion, .clinerules, Plan Mode, MCP Servers, checkpoints, and more.

- New FeatureTip component with 12 curated tips
- 2s delayed appearance, 8s cycling with fade transitions
- Visible throughout entire thinking/reasoning phase
- Proper timer cleanup on unmount

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

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

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

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

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

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

* fix: remove unused useMemo import from FeatureTip

* fix: increase test delays in QuitCommand.test.tsx for Windows CI reliability

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

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

* fix: add smooth fade-in for first tooltip appearance

The first tooltip was appearing abruptly because the element went from
not-in-DOM (return null) to opacity-100 instantly. Added hasFadedIn state
with requestAnimationFrame to ensure the CSS transition applies on initial
render, giving the first tip a smooth 300ms fade-in.

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-03-17 13:51:37 -07:00
5903840f79 Added Fields to Responses API for the Oracle Code Assist (OCA) Provider (#9852)
* added fields to responses api

* fix temp problem

* cleaning up

* clean modelId

* Apply suggestion from @greptile-apps[bot]

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

---------

Co-authored-by: Charlotte Stinson <charlottestinson@Charlottes-MacBook-Pro.local>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-03-17 13:41:39 -07:00
ClineXDiego 0c677b63db fix(browser): skip WebP for GLM and Devstral models running through llama.cpp (#9837)
llama.cpp's STB image library doesn't support WebP format. Users running
GLM 4.6V, GLM 4.5, and Devstral models via llama.cpp server (openai-compatible
endpoint) were hitting a 400 error when using the browser tool because Cline
sends screenshots as WebP by default.

modelDoesntSupportWebp() only checked for Grok models. Extend it to also
cover GLM and Devstral model families using the existing family detection
functions. Also update isGLMModelFamily() to handle space-separated model IDs
like 'GLM 4.6V' (the format llama.cpp server reports for this model).

Fixes #8203
2026-03-17 14:57:40 -03:00
ClineXDiego b741135f85 fix(litellm): respect user-configured context window in getModel() (#9834) 2026-03-17 05:36:44 -07:00
Ara 2eab216815 fix(wandb): honor explicit model IDs outside static catalog (#9839) 2026-03-16 16:40:28 -07:00
Ara e57174eec8 fix(fireworks): add missing serverless models and pricing (#9810)
* feat: refresh Fireworks serverless model defaults

* fix(fireworks): handle cached token usage and write pricing

* fixing maxtokens for gemini family

* feat(fireworks): add missing serverless models and pricing
2026-03-16 14:28:23 -07:00
Max ad4631c682 bump cline version (#9833) 2026-03-16 13:36:41 -07:00
Mathisandgreptile-apps[bot] aae23e8685 fix: Claude Code provider failing with 4.6 models and newer CLI versions (#9783)
* fix: Claude Code provider failing with 4.6 models and newer CLI versions

- Update --disallowedTools list to match current Claude Code CLI tools
  (12 new tools were unblocked, causing models to use native tool_use
  instead of Cline's XML tools)
- Fix rate_limit_event handling for new CLI format (top-level type
  instead of system subtype)
- Handle unknown content block types and new message types gracefully
- Fix assistantHasContent check to account for tool calls accumulated
  via toolUseHandler even when useNativeToolCalls is false

* resolved .include mismatch to .containEql

* Update src/integrations/claude-code/types.ts

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

* Removed `LegacyRateLimitEvent` type and its union reference from `types.ts`

* Fixed loop with async tool calls in new claude code.

* PR review feedback fixes

__Fix #2 (claude-code.ts):__ Cleaned up the error field check — replaced verbose `"error" in message` guard + ternary chain with a simpler `message.error` check using optional chaining and nullish coalescing (`message.content?.[0]` + `?? fallback`).

__Fix #3 (claude-code.ts):__ Replaced `message.content.length > 0 ? message.content[0] : undefined` with `message.content?.[0]` using optional chaining for the `stop_reason` block.

__Fix #4 (claude-code.ts):__ Replaced repeated `(content as any)` casts in the `default` switch case with a single typed cast: `const unknownBlock = content as { type: string; text?: string }`, making the code cleaner and safer.

__Fix #5 (ApplyPatchHandler.ts):__ Replaced both `await import("node:path")` and `require("node:path")` dynamic imports with a static `import { resolve as resolvePath } from "node:path"` at the top of the file.

* Remove file read deduplication feature (moved to separate PR)

* Remove ReadFileToolHandler file-not-found test (moved to separate PR)

* Add LegacyRateLimitEvent type for older CLI format

* Restore ReadFileToolHandler.ts and test from upstream/main (fix stale local main revert)

* Revert ReadFileToolHandler.ts to match fork main (no try/catch, no test file)

* manually reverting back

* Update src/core/api/providers/claude-code.ts

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

* Restore ReadFileToolHandler.ts and test to match cline/cline upstream main

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-03-16 13:01:55 -07:00
dependabot[bot] 0ff27591d5 chore(deps-dev): bump lodash (#8803)
Bumps the npm_and_yarn group with 1 update in the /testing-platform directory: [lodash](https://github.com/lodash/lodash).


Updates `lodash` from 4.17.21 to 4.17.23
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.21...4.17.23)

---
updated-dependencies:
- dependency-name: lodash
  dependency-version: 4.17.23
  dependency-type: direct:development
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-16 12:25:46 -07:00
dependabot[bot] 23127f22de chore(deps): bump qs from 6.14.1 to 6.15.0 (#9343)
Bumps [qs](https://github.com/ljharb/qs) from 6.14.1 to 6.15.0.
- [Changelog](https://github.com/ljharb/qs/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ljharb/qs/compare/v6.14.1...v6.15.0)

---
updated-dependencies:
- dependency-name: qs
  dependency-version: 6.15.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-16 12:00:02 -07:00
CandiedUniverse 57ed14d1b1 Fix issue with Windows notification (#9743)
* Fix issue with Windows notification

Fix Windows proto tooling

Fix Windows unit test path normalization

Revert "Fix Windows unit test path normalization"

This reverts commit 73400a3ca6f0300d009f7c8238a016d769186f3a.

Remove package-lock.json changes

* Remove unnecessary changes

* Use command approval string for notifications

* Address PR feedback on Windows notifications

* Fix Windows protoc path for CI

* Polish notification safety and test coverage

* Fix unfound tests in CI

* Harden Windows notifications and protoc execution

* Fix Windows path normalization in glob test

* Fix as per Greptile feedback
2026-03-16 09:44:20 -07:00
aikido-autofix[bot]andMax a769585406 fix(security): update dependencies (#9812)
Co-authored-by: aikido-autofix[bot] <119856028+aikido-autofix[bot]@users.noreply.github.com>
Co-authored-by: Max <maxpaulus43@gmail.com>
2026-03-16 09:15:40 -07:00
CandiedUniverse c44b29b002 Fix Windows CLI tests related to /q and /exit (#9747)
* Fix flaky Windows CLI quit slash tests

* Refine CLI slash command handling and test stability

* Address Greptile cleanup feedback
2026-03-16 07:59:26 -07:00
dependabot[bot] bb4e397a51 chore(deps): bump undici from 7.20.0 to 7.24.3 (#9825)
Bumps [undici](https://github.com/nodejs/undici) from 7.20.0 to 7.24.3.
- [Release notes](https://github.com/nodejs/undici/releases)
- [Commits](https://github.com/nodejs/undici/compare/v7.20.0...v7.24.3)

---
updated-dependencies:
- dependency-name: undici
  dependency-version: 7.24.3
  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-03-16 07:52:50 -07:00
Saoud Rizwan 9824d8d476 Bump CLI version from 2.7.0 to 2.7.1 2026-03-15 20:36:02 -07:00
Saoud Rizwan a46c5288ca Fix Notification hook getting called by command output asks 2026-03-15 20:08:06 -07:00
Mohammad Bakirandgreptile-apps[bot] 91b947de69 feat: Add W&B Inference by Coreweave as provider (#9800)
* feat(wandb): add W&B Inference by CoreWeave provider

Adds support for W&B Inference as an API provider using a W&B API key.
Implements a provider handler with OpenAI-compatible streaming and a static
model catalog, and wires the provider through the API layer, configuration
schema, storage, CLI model picker, and settings UI.

* Updated input/output price of NVIDIA-Nemotron

* Updated helpText

* handle reasoning tokens in streaming respons

* Added clarifying comment on how W&B token usage is reported and why cached tokens

* fix: restore proto field numbers changed by generation script

* Update src/core/api/providers/wandb.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-03-13 16:06:35 -07:00
Saoud Rizwan 7b25a21b26 fix(cli): add -y flag to npx kanban to auto-confirm install 2026-03-13 15:47:18 -07:00
Ara 1d1071dcf5 fix: consolidate Parallel tool-calling fixes (#9738)
* fix: consolidate parallel tool-calling fixes

* test(snapshot): fix vertex gemini3 snapshot newline

* fix gemini toolcall id collision (#9768)

* test(snapshot): fix vertex gemini3 snapshot newline

* fix(gemini): prevent native tool-call ID collisions

* fixing maxtokens for gemini family

* test(gemini): assert fallback tool call ids
2026-03-13 09:20:23 -07:00
Dominic Cooney 8d07b7d6cb fix: catch errors in path-based tool handlers instead of crashing (#9732)
* fix: catch errors in path-based tool handlers instead of crashing

ListCodeDefinitionNamesToolHandler, ListFilesToolHandler, and
SearchFilesToolHandler let exceptions from their core operations
propagate through ToolExecutor's re-throw path, crashing the CLI
process. This is the same class of bug fixed for ReadFileToolHandler
in #9730.

Changes per handler:
- Wrap the core operation in try/catch, returning formatResponse.toolError()
  on failure so the model can see the error and recover gracefully.
- Move consecutiveMistakeCount reset to after a successful operation so
  repeated failures accumulate toward the yolo-mode mistake limit.
- Increment consecutiveMistakeCount on caught errors.

Add end-to-end tests exercising each handler with a mock TaskConfig,
covering: non-existent paths, missing parameters, failure accumulation,
and success-based counter reset.

* address review: expand try/catch scope, add stub-based tests

- Include resolveWorkspacePath inside try/catch in
  ListCodeDefinitionNamesToolHandler and ListFilesToolHandler (matching
  SearchFilesToolHandler's pattern) so path resolution failures are
  also caught gracefully.
- Fix trivially-true assertion in file-not-a-directory test.
- Add 6 new stub-based tests that force core operations to throw:
  parseSourceCodeForDefinitionsTopLevel, listFiles, and
  determineSearchPaths — verifying the catch paths return
  formatResponse.toolError() and increment consecutiveMistakeCount.
- Total: 19 passing tests (up from 13).

* address review: move clineignore check before IO in ListFilesToolHandler

Move the .clineignore access validation before resolveWorkspacePath and
listFiles so blocked paths are rejected without incurring IO cost.
Also ensures consecutiveMistakeCount is only reset after all
validations and the core operation succeed.

* address review: increment counter on clineignore denial

Clineignore denial in ListFilesToolHandler now increments
consecutiveMistakeCount so repeated attempts at blocked paths
accumulate toward the yolo-mode mistake limit. Added 2 tests
verifying single and repeated clineignore denials.
Total: 21 passing tests.

* fix: increment consecutiveMistakeCount when SearchFilesToolHandler searches fail

Previously, SearchFilesToolHandler's executeSearch() caught regexSearchFiles
errors and returned {success: false}, but the handler unconditionally reset
consecutiveMistakeCount to 0 even when ALL searches failed. This contradicted
the PR's goal of accumulating failures toward the yolo-mode mistake limit.

Now we check if any search succeeded before resetting the counter:
- If at least one search succeeded: reset to 0 (existing behavior for successes)
- If all searches failed: increment the counter (new fix)

Also added comprehensive test coverage for this scenario, including tests for:
- regexSearchFiles throwing errors
- Repeated search failures accumulating
- Successful search resetting the counter after failures

* fix: detect error strings in ListCodeDefinitionNamesToolHandler

parseSourceCodeForDefinitionsTopLevel returns error strings instead of
throwing exceptions for file paths and non-existent directories. The
handler now detects these error conditions and increments
consecutiveMistakeCount so repeated failures accumulate correctly.

This addresses Greptile's feedback that the counter was unconditionally
resetting to 0 for all real-world failure modes of this handler.
2026-03-12 20:58:06 -07:00
Dominic Cooney aacc69a558 fix: catch file-not-found in ReadFileToolHandler instead of crashing CLI (#9730)
* fix: catch extractFileContent errors in ReadFileToolHandler

When extractFileContent throws (e.g. file not found), the exception
propagated through ToolExecutor which re-threw it, crashing the CLI
process with exit code 1.

Now file read errors are caught and returned as formatResponse.toolError()
so the model can see the error and recover gracefully (e.g. try a
different file path) instead of terminating the entire task.

Also increments consecutiveMistakeCount so the yolo-mode mistake limit
still functions correctly.
2026-03-13 10:52:32 +09:00
MaxandMax Paulus 🥪 a27bedffb6 max/cli tui e2e tests (#9753)
* add tui UI tests

using microsoft/tui-test library, can run many headless versions of
cline and execute ui tests (requires Node <= 20)

improve brittle sleep calls

* add cli-tui-tests github action

---------

Co-authored-by: Max Paulus 🥪 <max@cline.bot>
2026-03-12 16:06:25 -07:00
Diego Ferreyraandgatof81 bbeecefe84 docs: fix broken Oracle Code Assist website link (#9782)
The Oracle Code Assist URL moved from /artificial-intelligence/code-assist/
to /application-development/code-assist/. The old URL returns a 404.

Fixes #9776

Co-authored-by: gatof81 <gatof81@users.noreply.github.com>
2026-03-12 11:56:34 -03:00
alex-lum 50b57f472f feat(telemetry): add provider to task.tokens event (#9762) 2026-03-11 17:30:03 -07:00
CandiedUniverse 0e7e0099cd Changelog and version bump for release (#9775) 2026-03-11 17:21:04 -07:00
AJ JuaireandSaoud Rizwan 44cbfe9d9c Update Jupyter Notebook gifs (#9772)
Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-03-11 16:25:46 -07:00
Saoud Rizwan 4ecccf5105 feat(cli): add mcp add shortcuts for stdio and http servers (#9773) 2026-03-11 16:17:43 -07:00
Ara 6d29bc6551 fix(prompt): resolve native tool placeholder interpolation (#9764)
* fix(prompt): resolve CWD and MULTI_ROOT_HINT in native tool schemas

* refactor(prompt): share multi-root hint constant
2026-03-11 08:10:58 -07:00
Saoud Rizwan 820057308a feat(cli): add kanban alias command (#9763) 2026-03-10 21:57:16 -07:00
Ara 7355f7e3b9 fix(gemini): cap flash output tokens to 8192 across providers (#9749) 2026-03-10 14:10:14 -07:00
MaxandMax Paulus 🥪 156f18f7b2 call telemetry service after initializeCli (#9752)
- calling telemetry service before initializeCli call causes a
"hostprovider not initialized error", which invokes errorservice, which
causes another "hostprovider not initialized error", which was breaking
this cline use case: echo "say hello" | cline

Co-authored-by: Max Paulus 🥪 <max@cline.bot>
2026-03-10 11:50:34 -07:00
565 changed files with 27001 additions and 28113 deletions
-5
View File
@@ -1,5 +0,0 @@
---
"cline": patch
---
Adds a new "Prompts Library" view
-19
View File
@@ -1,19 +0,0 @@
#!/usr/bin/env bash
echo "PostToolUse running inside local cline/.clinerules/hooks/ directory"
input=$(cat)
echo $input | jq .
for i in {1..5}; do
sleep 1
echo "$i"
done
cat <<EOF
{
"cancel": false,
"contextModification": "PostToolUse response from the local cline/.clinerules/hooks/ directory.",
"errorMessage": "PostToolUse hook custom errorMessage"
}
EOF
-19
View File
@@ -1,19 +0,0 @@
#!/usr/bin/env bash
echo "PreToolUse running inside local cline/.clinerules/hooks/ directory"
input=$(cat)
echo $input | jq .
for i in {1..5}; do
sleep 1
echo "$i"
done
cat <<EOF
{
"cancel": false,
"contextModification": "PreToolUse response from the local cline/.clinerules/hooks/ directory.",
"errorMessage": "PreToolUse hook custom errorMessage"
}
EOF
-19
View File
@@ -1,19 +0,0 @@
#!/usr/bin/env bash
echo "TaskCancel running inside local cline/.clinerules/hooks/ directory"
input=$(cat)
echo $input | jq .
for i in {1..5}; do
sleep 1
echo "$i"
done
cat <<EOF
{
"cancel": false,
"contextModification": "TaskCancel response from the local cline/.clinerules/hooks/ directory.",
"errorMessage": "TaskCancel hook custom errorMessage"
}
EOF
-19
View File
@@ -1,19 +0,0 @@
#!/usr/bin/env bash
echo "TaskResume running inside local cline/.clinerules/hooks/ directory"
input=$(cat)
echo $input | jq .
for i in {1..5}; do
sleep 1
echo "$i"
done
cat <<EOF
{
"cancel": false,
"contextModification": "TaskResume response from the local cline/.clinerules/hooks/ directory.",
"errorMessage": "TaskResume hook custom errorMessage"
}
EOF
-19
View File
@@ -1,19 +0,0 @@
#!/usr/bin/env bash
echo "TaskStart running inside local cline/.clinerules/hooks/ directory"
input=$(cat)
echo $input | jq .
for i in {1..5}; do
sleep 1
echo "$i"
done
cat <<EOF
{
"cancel": false,
"contextModification": "TaskStart response from the local cline/.clinerules/hooks/ directory.",
"errorMessage": "TaskStart hook custom errorMessage"
}
EOF
@@ -1,19 +0,0 @@
#!/usr/bin/env bash
echo "UserPromptSubmit running inside local cline/.clinerules/hooks/ directory"
input=$(cat)
echo $input | jq .
for i in {1..5}; do
sleep 1
echo "$i"
done
cat <<EOF
{
"cancel": false,
"contextModification": "UserPromptSubmit response from the local cline/.clinerules/hooks/ directory.",
"errorMessage": "UserPromptSubmit hook custom errorMessage"
}
EOF
+2
View File
@@ -1,4 +1,6 @@
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
+6
View File
@@ -26,6 +26,12 @@ body:
placeholder: 'e.g., 1.2.3'
validations:
required: true
- type: checkboxes
id: beta
attributes:
label: Beta version
options:
- label: I am using a beta version of Cline
- type: textarea
id: what-happened
attributes:
+83
View File
@@ -0,0 +1,83 @@
name: CLI TUI Tests
on:
pull_request:
branches:
- main
workflow_dispatch:
workflow_call:
permissions:
contents: read
jobs:
cli-tui-tests:
name: CLI TUI Tests
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 22
- name: Install dependencies
run: npm ci
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Build CLI
run: npm run cli:build
- name: Run TUI Tests
id: tui_tests
run: |
npm run test:e2e:cli:tui 2>&1 | tee tui-test-output.log
exit_code=${PIPESTATUS[0]}
echo "tui_exit_code=$exit_code" >> $GITHUB_OUTPUT
exit $exit_code
- name: Write failure summary
if: always() && steps.tui_tests.outcome != 'success' && steps.tui_tests.outcome != 'skipped'
run: |
echo "## ❌ CLI TUI Tests Failed" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Step outcome:** \`${{ steps.tui_tests.outcome }}\`" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Test Output" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
if [ -f tui-test-output.log ]; then
cat tui-test-output.log >> $GITHUB_STEP_SUMMARY
else
echo "(no test output captured — process may have been killed before output was flushed)" >> $GITHUB_STEP_SUMMARY
fi
echo '```' >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Debugging" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "- **TUI traces** are attached as artifacts below — download and inspect them to see terminal state at the point of failure." >> $GITHUB_STEP_SUMMARY
echo "- **To view a trace replay/Run a TUI Trace: ** run \`npx tui-test show-trace path/to/trace/file\` in your terminal" >> $GITHUB_STEP_SUMMARY
echo "- **Full test log** is also attached as an artifact." >> $GITHUB_STEP_SUMMARY
echo "- Tests run with \`retries: 2\` so any failure shown is a consistent failure, not a flake." >> $GITHUB_STEP_SUMMARY
- name: Upload TUI traces
if: always() && steps.tui_tests.outcome != 'success' && steps.tui_tests.outcome != 'skipped'
uses: actions/upload-artifact@v4
with:
name: tui-test-traces
path: tests/e2e/cli/tui-traces/
retention-days: 14
if-no-files-found: warn
- name: Upload test log
if: always() && steps.tui_tests.outcome != 'success' && steps.tui_tests.outcome != 'skipped'
uses: actions/upload-artifact@v4
with:
name: tui-test-log
path: tui-test-output.log
retention-days: 14
if-no-files-found: warn
@@ -51,3 +51,15 @@ jobs:
});
}
}
// 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']
});
}
}
+18
View File
@@ -91,3 +91,21 @@ jobs:
echo ""
echo "📦 Install with: npm install -g cline"
echo "🔗 NPM: https://www.npmjs.com/package/cline/v/${{ steps.version.outputs.version }}"
- 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: "context"
elements:
- type: "mrkdwn"
text: "<https://www.npmjs.com/package/cline/v/${{ steps.version.outputs.version }}|View on npm>"
+3 -1
View File
@@ -31,8 +31,10 @@ jobs:
- name: Check for recent commits
id: check_commits
env:
FORCE_PUBLISH: ${{ inputs.force_publish }}
run: |
if [ "${{ inputs.force_publish }}" = "true" ]; then
if [ "$FORCE_PUBLISH" = "true" ]; then
echo "force_publish enabled, proceeding with publish"
echo "skip=false" >> $GITHUB_OUTPUT
exit 0
@@ -30,7 +30,11 @@ permissions:
pull-requests: write # Required by nested reusable test workflow
jobs:
cli-tui-tests:
uses: ./.github/workflows/cli-tui-tests.yml
publish-main:
needs: cli-tui-tests
if: |
github.repository == 'cline/cline' && (
github.event_name == 'workflow_dispatch' &&
@@ -44,6 +48,7 @@ jobs:
confirm_publish: ${{ github.event.inputs.confirm_publish }}
publish-nightly:
needs: cli-tui-tests
if: |
github.repository == 'cline/cline' && (
github.event_name == 'schedule' ||
+72
View File
@@ -0,0 +1,72 @@
name: "Publish SDK Nightly Release"
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 (Nightly SDK) Extension
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
+12 -3
View File
@@ -1,8 +1,6 @@
name: "Publish Nightly Release"
on:
schedule:
- cron: '0 12 * * *' # 4 AM PST (UTC-8) = 12 UTC
workflow_dispatch:
permissions:
@@ -24,6 +22,8 @@ jobs:
steps:
- uses: actions/checkout@v4
with:
lfs: true
- name: Check for recent commits
run: |
@@ -49,7 +49,16 @@ jobs:
- name: Install Publishing Tools
run: npm install -g @vscode/vsce ovsx
- name: Publish Extension as Pre-release
- 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 }}
+37 -3
View File
@@ -43,12 +43,14 @@ jobs:
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: |
TAG="${{ github.event.inputs.tag }}"
AUTO_CREATE="${{ github.event.inputs.auto_create_tag_from_main }}"
TESTED_SHA="${{ github.sha }}"
WORKFLOW_REF="${{ github.ref }}"
@@ -133,6 +135,15 @@ jobs:
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 }}
@@ -147,11 +158,12 @@ jobs:
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: |
# Required to generate the .vsix
vsce package --allow-package-secrets sendgrid --out "cline-${{ steps.get_version.outputs.version }}.vsix"
if [ "${{ github.event.inputs.release-type }}" = "pre-release" ]; then
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
@@ -187,3 +199,25 @@ jobs:
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 }}"
+24 -1
View File
@@ -46,6 +46,8 @@ jobs:
test:
needs: quality-checks
env:
VSCODE_TEST_VERSION: 1.103.0
strategy:
fail-fast: false
matrix:
@@ -81,6 +83,13 @@ jobs:
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
@@ -106,7 +115,21 @@ jobs:
- name: Extension Integration Tests - Non-Linux
id: integration_tests_non_linux
if: ${{ !cancelled() && steps.build_step.outcome == 'success' && runner.os != 'Linux' }}
run: npm run test:integration
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
+5
View File
@@ -51,3 +51,8 @@ test-results
# Smoke test results (generated)
evals/smoke-tests/results/
.tui-test
secrets.json
tui-traces
tests/**/cache
+2 -1
View File
@@ -3,7 +3,8 @@
"ts"
],
"spec": [
"src/**/__tests__/*.ts"
"src/**/__tests__/*.ts",
"src/test/services/**/*.test.ts"
],
"require": [
"ts-node/register",
+3 -1
View File
@@ -1,6 +1,8 @@
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: {
@@ -12,7 +14,7 @@ export default defineConfig({
require: ["./test-setup.js"],
},
workspaceFolder: "test-workspace",
version: "stable",
version: vscodeTestVersion,
extensionDevelopmentPath: path.resolve("./"),
launchArgs: ["--disable-extensions"],
})
+174
View File
@@ -1,5 +1,179 @@
# Changelog
## [3.82.0]
### Added
- Restore VS Code foreground terminal support and settings.
- Add latest OpenAI, SAP AI Core, and Z AI models.
### Fixed
- Fix hook template JSON escaping.
- Improve ripgrep file search error handling.
### Changed
- Remove hardcoded model lists from docs.
## [3.81.0]
### Added
- Add GPT-5.5 model support for OpenAI Codex subscription users.
### Fixed
- Remove hardcoded "Whats New" fallback items in webview; only remote-configured welcome banners are shown.
### Changed
- Improve cline-core memory diagnostics used by the extension runtime:
- enable near-heap-limit heap snapshots
- add periodic memory usage logging
- log discovered heap snapshots on abnormal exits for easier OOM debugging
## [3.80.0]
### Added
- Wire up remote `globalSkills` from enterprise remote config with full UI, toggle support, and system prompt integration — enterprise-managed skills now appear under a dedicated "Enterprise Skills" section and support `alwaysEnabled` enforcement
- Onboarding flow now uses dynamically fetched recommended models instead of a hardcoded list, with a fallback to the welcome view on failure
- Add dedicated "Quota Exceeded" error message in the chat error UI when Cline account spend caps are hit
### Fixed
- Fix OOM crashes during long conversations by setting `--max-old-space-size=8192` for the cline-core Node.js process (was defaulting to ~2 GB)
- Show detailed error information in the chat error row instead of a generic caught error message
- Update `axios` to 1.15.0 across all packages
### Changed
- Remove foreground terminal mode — all task command execution now defaults to background mode, removing the VS Code integrated terminal dependency and related settings UI
- Remove old hardcoded announcement banners
## [3.79.0]
### Added
- Add Claude Opus 4.7 model support
- Add Azure Blob Storage as a storage provider
- Add `globalSkills` to remote config
- Inline value reuse in user-level remote-config discovery
### Fixed
- Fix cache reflection for Cline and Vercel API handlers
- Fix stuck `command_output` ask when terminal command ends unexpectedly
- Add `use_subagents` to system prompt for GLM, Hermes, and XS models
- Fix action injection security risk
### Changed
- Remove deprecated evals tool
## [3.78.0]
### Added
- Add a dedicated "Spend Limit Reached" error UI when spend caps are hit
- Docs updates
### Fixed
- Show actual `read_file` line ranges in chat UI
## [3.77.0]
### Added
- Add "Lazy Teammate Mode" experimental toggle
- `read_file` tool now supports chunked reading for targeted file access
### Fixed
- Exclude `new_task` tool from system prompt in yolo/headless mode
- Fix Kanban demo video formatting
### Changed
- Polish `Notification` hook functionality
## [3.76.0]
### Added
- Add Cline Kanban launch modal in webview; CLI now launches Kanban by default with a migration view
- Add toggle to disable feature tips in chat
- Add repeated tool call loop detection to prevent infinite loops wasting tokens
### Fixed
- Fix CLI Kanban spawn on Windows by enabling shell mode for `npx.cmd`
## [3.75.0]
### Added
- Latency improvements for remote workspaces
### Fixed
- Stabilize flaky hooks tests
### Changed
- Remove example hooks in favor of reading the docs
## [3.74.0]
### Added
- Implement dynamic free model detection for Cline API
- Add file read deduplication cache to prevent repeated reads
- Add feature tips tooltip during thinking state
### Fixed
- Replace error message when not logged in to Cline
- Align ClineRulesToggleModal padding with ServersToggleModal
- Skip WebP for GLM and Devstral models running through llama.cpp
- Respect user-configured context window in LiteLLM getModel()
- Honor explicit model IDs outside static catalog in W&B provider
- Add missing Fireworks serverless models and pricing
## [3.73.0]
### Added
- Added W&B Inference by CoreWeave as a new API provider with 17 models
- Improved parallel tool calling support for OpenRouter and Cline providers
### Fixed
- Claude Code Provider: handle rate limit events, empty content arrays, error results, and unknown content types without crashing
- Tool handlers (`read_file`, `list_files`, `list_code_definition_names`, `search_files`) now return graceful errors instead of crashing
## [3.72.0]
### Added
- Added Anthropic Opus 4.6 fast mode variants
### Fixed
- Resolved native tool placeholder interpolation in prompts
- Gemini: capped Flash output tokens to 8192 across providers
- Fixed Windows unit test path normalization
- Fixed flaky hooks tests on Windows
- Bedrock: handle thinking and redacted_thinking blocks correctly in message conversion and streaming
- Prevent crash when `list_files` or `list_code_definition_names` receives a file path
### Changed
- Updated Jupyter Notebook GIFs
- Markdown image loading now requires user consent
- Added `.github/copilot-instructions.md` for coding agents
- Hooks: reintroduced feature toggle
## [3.71.0]
### Added
-5
View File
@@ -3,11 +3,6 @@ English | <a href="https://github.com/cline/cline/blob/main/locales/es/README.md
</sub></div>
# Cline
<p align="center">
<img src="https://media.githubusercontent.com/media/cline/cline/main/assets/docs/demo.gif" width="100%" />
</p>
<div align="center">
<table>
<tbody>
+3 -5
View File
@@ -8,9 +8,7 @@ We actively patch only the most recent minor release of Cline. Older versions re
We appreciate your efforts to responsibly disclose your findings and will make every effort to acknowledge your contributions.
To report a security issue, please use the GitHub Security Advisory ["Report a Vulnerability"](https://github.com/cline/cline/security/advisories/new) tab.
The team will send a response indicating the next steps in handling your report. After the initial reply, the security team will keep you informed of the progress towards a fix and full announcement, and may ask for additional information or guidance.
To report a security issue, please submit your report through our [Bugcrowd Vulnerability Disclosure Program](https://bugcrowd.com/engagements/clinebot-vdp-ess). Bugcrowd will manage communication and triage on our behalf.
When reporting, please include:
@@ -18,10 +16,10 @@ When reporting, please include:
- Steps to reproduce or a proof of concept
- Any logs, stack traces, or screenshots that might help us understand the problem
We acknowledge reports within 48 hours and aim to release a fix or mitigation within 30 days. While we work on a resolution, please keep the details private.
Please keep the details private until a resolution has been reached.
## Escalation
If you do not receive an acknowledgement of your report within 5 business days, you may send an email to security@cline.bot.
If you are unable to submit through Bugcrowd, you may send an email to security@cline.bot.
Thank you for helping us keep Cline users safe.
+12
View File
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg id="Layer_1" xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 512 535">
<!-- Generator: Adobe Illustrator 29.8.5, SVG Export Plug-In . SVG Version: 2.1.1 Build 2) -->
<defs>
<style>
.st0 {
fill: #fff;
}
</style>
</defs>
<path class="st0" d="M500.6,300.5c-9-20.7-17.9-41.4-26.9-62.1-.7-2-.3-4.4-.3-6.4.4-9,1.1-18,1.4-27,2.8-28.4-6.5-58-25.2-79.6-15.1-18.1-36.6-30.7-59.6-35.5-8.1-1.8-16.6-1.6-25-2.1-10-.7-20-1-30-1.7,2-11.9,1-24.1-3.7-35.3-5.8-14.1-16.8-25.9-30.6-32.5-14.4-7-31.5-8.2-46.7-3.1-16,5.2-29.5,17-36.8,32.1-4.9,10-6.8,21.2-6.1,32.2-19.7-1-39.4-2.2-59.1-3.1-26.8.5-53,11.7-72,30.6-20.2,19.5-31.7,47-32.3,75-.5,9.3-1,18.7-1.5,28-.2,2.1,0,4.1-1.2,6-9.8,16.8-19.5,33.7-29.4,50.6-2.2,4.1-4.9,8-6.6,12.3-2,5.7-1.2,12.2,1.3,17.6,8.9,19.5,17.6,39.2,26.5,58.7.8,1.9,1.5,3.7,1.3,5.8-.6,10.3-1.1,20.7-1.7,31-1.5,21.2,3,42.6,13.5,61.1,8.8,15.8,21.6,29.4,37.1,38.9,13.9,8.7,29.7,13.9,46,15.4,72,3.9,144,7.7,216,11.5,20.1,1.8,40.8-2.8,58.5-12.5,18.8-10.1,34.2-26,44.1-44.9,6.5-12.6,10.5-26.4,11.7-40.5.7-12.4,1.2-24.7,2-37.1,0-3.3,1.9-5.5,3.3-8.2,6.6-11.8,13.5-23.4,20.1-35.2,3.7-6.9,8.1-13.4,11.6-20.4,3.2-6.1,3.2-13.5.3-19.7ZM218.5,316.5c-9.7,7.1-21.3,12.3-33.5,12.5-17.6,1-35.1-5.3-49-16-4.6-3.2-8.1-7.5-9.6-13,0-1.8-.7-3.6,1.7-3.8,4,1,7.9,2.6,12,3.5,22.8,5.6,47.6,5.9,71,4.8,6.5-.2,13-1.3,19.5-.9-2.7,5.6-7.1,9.2-12,12.9ZM276,449.7c-14,.5-28,.1-42-.2-2.1,0-4.3,0-6.4-.4-.9-2.1.6-3.2,1.7-4.8,4.8-5.9,11-11,18.7-12.4,8.4-1.6,16.5,1.2,23.5,5.5,4.7,3,9.2,6.3,12.6,10.8-2.6,1.1-5.3,1.4-8.1,1.4ZM390.4,319.4c-16.4,14.2-38.8,21.8-60.4,18.4-13.2-1.6-24.7-8.6-34.1-17.7-3-3-6.2-6.5-8.1-10.4.5-1,1.2-1.6,2.2-1.6,2.8-.2,5.7.7,8.5,1.1,16,2.9,32.3,4.9,48.5,5.5,14.3.4,28.2-.2,42.2-3.6,2.2-.6,3.7-.3,5.8.5-1.1,2.9-2.2,5.7-4.6,7.8Z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

+144
View File
@@ -1,5 +1,149 @@
# cline
## [2.18.0]
### Added
- Restore foreground terminal support and settings.
- Add latest OpenAI, SAP AI Core, and Z AI models.
### Fixed
- Fix hook template JSON escaping.
- Improve ripgrep file search error handling.
### Changed
- Remove hardcoded model lists from docs.
## [2.17.0]
### Added
- Add GPT-5.5 model support for OpenAI Codex subscription users.
### Changed
- Improve `cline-core` runtime memory diagnostics used by CLI:
- enable near-heap-limit heap snapshots
- add periodic memory usage logging
- log discovered heap snapshots on abnormal exits for easier OOM debugging
## [2.16.0]
### Added
- Wire up remote `globalSkills` from enterprise remote config with full toggle support and system prompt integration — enterprise-managed skills now support `alwaysEnabled` enforcement
- Add dedicated "Quota Exceeded" error message when Cline account spend caps are hit
### Fixed
- Fix OOM crashes during long conversations by setting `--max-old-space-size=8192` for the cline-core Node.js process (was defaulting to ~2 GB)
- Show detailed error information instead of a generic caught error message
- Update `axios` to 1.15.0 across all packages
### Changed
- Remove dead ACP terminal setter stubs as part of foreground terminal mode removal
## [2.15.0]
### Added
- Add Claude Opus 4.7 model support
- Inline value reuse in user-level remote-config discovery
- Add `globalSkills` to remote config
### Fixed
- Stabilize Windows CI test path handling
## [2.14.0]
### Added
- Simplify unified `cline update` flow for `cline` and `kanban`
- Docs updates
### Fixed
- Update Kanban migration view copy
## [2.12.0]
### Added
- `read_file` tool now supports chunked reading for targeted file access
### Fixed
- Exclude `new_task` tool from system prompt in yolo/headless mode
### Changed
- Polish `Notification` hook functionality
## [2.9.0]
### Added
- Latency improvements for remote workspaces
## [2.8.2]
### Fixed
- Use `kanban@latest` in `cline kanban` to always fetch the newest version
## [2.8.1]
### Added
- Implement dynamic free model detection for Cline API
- Add file read deduplication cache to prevent repeated reads
- Add feature tips tooltip during thinking state
### Fixed
- Fix flaky CLI Enter-key handling across Windows/test environments
- Replace error message when not logged in to Cline
- Align ClineRulesToggleModal padding with ServersToggleModal
- Skip WebP for GLM and Devstral models running through llama.cpp
- Respect user-configured context window in LiteLLM getModel()
- Honor explicit model IDs outside static catalog in W&B provider
- Add missing Fireworks serverless models and pricing
## [2.8.0]
### Added
- Added W&B Inference by CoreWeave as a new API provider with 17 models including DeepSeek-V3.1, Llama 4, and Qwen3-Coder
- Added CLI TUI end-to-end test suite
### Fixed
- Claude Code: handle rate limit events, empty content arrays, error results, and unknown content types without crashing
- CLI: `/q` and `/exit` slash commands now execute immediately on Enter without requiring the slash menu to be visible
- CLI: slash command filtering now prioritizes exact and prefix matches over fuzzy matches
## [2.7.0]
### Added
- Added MCP add shortcuts for stdio and HTTP servers
- Added `--continue` for the current directory
- Added `--auto-condense` flag for AI-powered context compaction
- Added `--hooks-dir` flag for runtime hook injection
- Enabled error autocapture
- Prompt rules now include test verification guidance and make `CLI_RULES` language-agnostic
### Fixed
- Fixed remount behavior so TUI remounts only on width resize
- Fixed startup prompt replay on resize remount
- Fixed task flags so they are applied before the welcome TUI mounts
### Changed
- Hooks: reintroduced feature toggle
## [2.6.1]
### Added
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "cline",
"version": "2.6.1",
"version": "2.18.0",
"description": "Autonomous coding agent CLI - capable of creating/editing files, running commands, using the browser, and more",
"main": "dist/lib.mjs",
"types": "dist/lib.d.ts",
+2 -2
View File
@@ -7,7 +7,7 @@ import { Box, Text, useInput } from "ink"
import React from "react"
import { COLORS } from "../constants/colors"
import { useStdinContext } from "../context/StdinContext"
import { isMouseEscapeSequence } from "../utils/input"
import { isEnterKey, isMouseEscapeSequence } from "../utils/input"
interface ApiKeyInputProps {
providerName: string
@@ -39,7 +39,7 @@ export const ApiKeyInput: React.FC<ApiKeyInputProps> = ({
onCancel()
return
}
if (key.return) {
if (isEnterKey(input, key)) {
onSubmit(value)
return
}
+43 -44
View File
@@ -9,7 +9,7 @@ import React, { useCallback, useEffect, useRef, useState } from "react"
import { useStdinContext } from "../context/StdinContext"
import { useTaskController } from "../context/TaskContext"
import { useLastCompletedAskMessage } from "../hooks/useStateSubscriber"
import { isMouseEscapeSequence } from "../utils/input"
import { isEnterKey, isMouseEscapeSequence } from "../utils/input"
import { jsonParseSafe } from "../utils/parser"
interface AskPromptProps {
@@ -136,7 +136,7 @@ export const AskPrompt: React.FC<AskPromptProps> = ({ onRespond }) => {
} else if (promptType === "options") {
// Number selection for options, or free text input
const parts = jsonParseSafe(text, { options: [] as string[] })
if (key.return) {
if (isEnterKey(input, key)) {
// Submit free text on Enter
if (textInput.trim()) {
sendResponse("messageResponse", textInput.trim())
@@ -145,7 +145,7 @@ export const AskPrompt: React.FC<AskPromptProps> = ({ onRespond }) => {
setTextInput((prev) => prev.slice(0, -1))
} else if (input && !key.ctrl && !key.meta) {
// Check if it's a number for option selection (only when no text typed yet)
const num = parseInt(input, 10)
const num = Number.parseInt(input, 10)
if (textInput === "" && !Number.isNaN(num) && num >= 1 && num <= parts.options.length) {
const selectedOption = parts.options[num - 1]
sendResponse("messageResponse", selectedOption)
@@ -156,7 +156,7 @@ export const AskPrompt: React.FC<AskPromptProps> = ({ onRespond }) => {
}
} else if (promptType === "text") {
// Text input mode
if (key.return) {
if (isEnterKey(input, key)) {
// Submit on Enter
if (textInput.trim()) {
sendResponse("messageResponse", textInput.trim())
@@ -169,7 +169,7 @@ export const AskPrompt: React.FC<AskPromptProps> = ({ onRespond }) => {
}
} else if (promptType === "plan_mode_text") {
// Plan mode text input - allows text response or toggle to Act mode
if (key.return) {
if (isEnterKey(input, key)) {
// Submit on Enter
if (textInput.trim()) {
sendResponse("messageResponse", textInput.trim())
@@ -185,7 +185,7 @@ export const AskPrompt: React.FC<AskPromptProps> = ({ onRespond }) => {
}
} else if (promptType === "completion") {
// Task completed - allow follow-up question or exit
if (key.return) {
if (isEnterKey(input, key)) {
if (textInput.trim()) {
// Send follow-up question
sendResponse("messageResponse", textInput.trim())
@@ -401,43 +401,42 @@ function getCliMessagePrefixIcon(message: ClineMessage): string {
default:
return "❔"
}
} else {
switch (message.say) {
case "task":
return "📋"
case "error":
return "❌"
case "text":
return "💬"
case "reasoning":
return "🧠"
case "completion_result":
return "✅"
case "user_feedback":
return "👤"
case "command":
case "command_output":
return "⚙️"
case "tool":
return "🔧"
case "browser_action":
case "browser_action_launch":
case "browser_action_result":
return "🌐"
case "mcp_server_request_started":
case "mcp_server_response":
return "🔌"
case "api_req_started":
case "api_req_finished":
return "🔄"
case "checkpoint_created":
return "💾"
case "info":
return "️"
case "generate_explanation":
return "📝"
default:
return " "
}
}
switch (message.say) {
case "task":
return "📋"
case "error":
return "❌"
case "text":
return "💬"
case "reasoning":
return "🧠"
case "completion_result":
return "✅"
case "user_feedback":
return "👤"
case "command":
case "command_output":
return "⚙️"
case "tool":
return "🔧"
case "browser_action":
case "browser_action_launch":
case "browser_action_result":
return "🌐"
case "mcp_server_request_started":
case "mcp_server_response":
return "🔌"
case "api_req_started":
case "api_req_finished":
return "🔄"
case "checkpoint_created":
return "💾"
case "info":
return "️"
case "generate_explanation":
return "📝"
default:
return " "
}
}
+7 -7
View File
@@ -19,7 +19,7 @@ import { useClineFeaturedModels } from "../hooks/useClineFeaturedModels"
import { useOcaAuth } from "../hooks/useOcaAuth"
import { useScrollableList } from "../hooks/useScrollableList"
import { type DetectedSources, detectImportSources, type ImportSource } from "../utils/import-configs"
import { isMouseEscapeSequence } from "../utils/input"
import { isEnterKey, isMouseEscapeSequence } from "../utils/input"
import { applyBedrockConfig, applyProviderConfig } from "../utils/provider-config"
import { useValidProviders } from "../utils/providers"
import { ApiKeyInput } from "./ApiKeyInput"
@@ -79,12 +79,12 @@ const Select: React.FC<{
const [selectedIndex, setSelectedIndex] = useState(0)
useInput(
(_, key) => {
(input, key) => {
if (key.upArrow) {
setSelectedIndex((prev) => (prev > 0 ? prev - 1 : items.length - 1))
} else if (key.downArrow) {
setSelectedIndex((prev) => (prev < items.length - 1 ? prev + 1 : 0))
} else if (key.return) {
} else if (isEnterKey(input, key)) {
onSelect(items[selectedIndex].value)
}
},
@@ -130,7 +130,7 @@ const TextInput: React.FC<{
return
}
if (key.return) {
if (isEnterKey(input, key)) {
onSubmit(value)
} else if (key.backspace || key.delete) {
onChange(value.slice(0, -1))
@@ -853,7 +853,7 @@ export const AuthView: React.FC<AuthViewProps> = ({ controller, onComplete, onEr
setMenuIndex((prev) => (prev > 0 ? prev - 1 : mainMenuItems.length - 1))
} else if (key.downArrow) {
setMenuIndex((prev) => (prev < mainMenuItems.length - 1 ? prev + 1 : 0))
} else if (key.return) {
} else if (isEnterKey(input, key)) {
handleMainMenuSelect(mainMenuItems[menuIndex].value)
}
} else if (step === "provider") {
@@ -861,7 +861,7 @@ export const AuthView: React.FC<AuthViewProps> = ({ controller, onComplete, onEr
setProviderIndex((prev) => (prev > 0 ? prev - 1 : providerItems.length - 1))
} else if (key.downArrow) {
setProviderIndex((prev) => (prev < providerItems.length - 1 ? prev + 1 : 0))
} else if (key.return) {
} else if (isEnterKey(input, key)) {
if (providerItems[providerIndex]) {
handleProviderSelect(providerItems[providerIndex].value)
}
@@ -877,7 +877,7 @@ export const AuthView: React.FC<AuthViewProps> = ({ controller, onComplete, onEr
setClineModelIndex((prev) => (prev > 0 ? prev - 1 : maxIndex))
} else if (key.downArrow) {
setClineModelIndex((prev) => (prev < maxIndex ? prev + 1 : 0))
} else if (key.return) {
} else if (isEnterKey(input, key)) {
if (isBrowseAllSelected(clineModelIndex, featuredModels)) {
setStep("modelid")
} else {
@@ -9,6 +9,7 @@ import { Box, Text, useInput } from "ink"
import React, { useCallback, useState } from "react"
import { COLORS } from "../constants/colors"
import { useStdinContext } from "../context/StdinContext"
import { isEnterKey } from "../utils/input"
import { getModelList } from "./ModelPicker"
import { SearchableList } from "./SearchableList"
@@ -43,7 +44,7 @@ export const BedrockCustomModelFlow: React.FC<BedrockCustomModelFlowProps> = ({
if (step === "arn_input") {
if (key.escape) {
onCancel()
} else if (key.return) {
} else if (isEnterKey(input, key)) {
handleArnSubmit()
} else if (key.backspace || key.delete) {
setCustomArn((prev) => prev.slice(0, -1))
+11 -7
View File
@@ -12,7 +12,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"
import { ChatView } from "./ChatView"
// Helper to wait for async state updates
const delay = (ms: number = 60) => new Promise((resolve) => setTimeout(resolve, ms))
const delay = (ms = 60) => new Promise((resolve) => setTimeout(resolve, ms))
// Type for our exit mock function
type ExitMockFn = ReturnType<typeof vi.fn> & (() => void)
@@ -126,12 +126,16 @@ vi.mock("../utils/file-search", () => ({
searchWorkspaceFiles: vi.fn(async () => []),
}))
vi.mock("../utils/slash-commands", () => ({
extractSlashQuery: vi.fn(() => ({ inSlashMode: false, query: "", slashIndex: -1 })),
filterCommands: vi.fn(() => []),
insertSlashCommand: vi.fn((text: string) => text),
sortCommandsWorkflowsFirst: vi.fn((cmds: unknown[]) => cmds),
}))
vi.mock("../utils/slash-commands", async (importOriginal) => {
const actual = await importOriginal<typeof import("../utils/slash-commands")>()
return {
...actual,
extractSlashQuery: vi.fn(() => ({ inSlashMode: false, query: "", slashIndex: -1 })),
filterCommands: vi.fn(() => []),
insertSlashCommand: vi.fn((text: string) => text),
sortCommandsWorkflowsFirst: vi.fn((cmds: unknown[]) => cmds),
}
})
vi.mock("../utils/input", () => ({
isMouseEscapeSequence: vi.fn(() => false),
+100 -71
View File
@@ -108,7 +108,6 @@ import type { ClineAsk, ClineMessage } from "@shared/ExtensionMessage"
import { getApiMetrics, getLastApiReqTotalTokens } from "@shared/getApiMetrics"
import { EmptyRequest, StringRequest } from "@shared/proto/cline/common"
import type { SlashCommandInfo } from "@shared/proto/cline/slash"
import { CLI_ONLY_COMMANDS } from "@shared/slashCommands"
import { getProviderDefaultModelId, getProviderModelIdKey } from "@shared/storage"
import type { Mode } from "@shared/storage/types"
import { execSync } from "child_process"
@@ -137,7 +136,14 @@ import {
} from "../utils/file-search"
import { isMouseEscapeSequence } from "../utils/input"
import { jsonParseSafe, parseImagesFromInput } from "../utils/parser"
import { extractSlashQuery, filterCommands, insertSlashCommand, sortCommandsWorkflowsFirst } from "../utils/slash-commands"
import {
createCliOnlySlashCommands,
extractSlashQuery,
filterCommands,
getStandaloneSlashCommandToExecute,
insertSlashCommand,
sortCommandsWorkflowsFirst,
} from "../utils/slash-commands"
import { waitFor } from "../utils/timeout"
import { isFileEditTool, parseToolFromMessage } from "../utils/tools"
import { shutdownEvent } from "../vscode-shim"
@@ -403,7 +409,7 @@ export const ChatView: React.FC<ChatViewProps> = ({
const PASTE_UPDATE_DEBOUNCE_MS = 50 // Debounce visual updates to avoid flicker
// Slash command state
const [availableCommands, setAvailableCommands] = useState<SlashCommandInfo[]>([])
const [availableCommands, setAvailableCommands] = useState<SlashCommandInfo[]>(() => createCliOnlySlashCommands())
const [selectedSlashIndex, setSelectedSlashIndex] = useState(0)
const [slashMenuDismissed, setSlashMenuDismissed] = useState(false)
const lastSlashIndexRef = useRef<number>(-1)
@@ -614,16 +620,15 @@ export const ChatView: React.FC<ChatViewProps> = ({
try {
const response = await getAvailableSlashCommands(ctrl, EmptyRequest.create())
const cliCommands = response.commands.filter((cmd) => cmd.cliCompatible !== false)
// Add CLI-only commands (like /settings) that are handled locally
const cliOnlyCommands: SlashCommandInfo[] = CLI_ONLY_COMMANDS.map((cmd) => ({
name: cmd.name,
description: cmd.description || "",
section: cmd.section || "default",
cliCompatible: true,
}))
// Add CLI-only commands (like /settings) that are handled locally.
// Seed these synchronously on first render so locally handled commands like
// /q and /exit are immediately available, even before the async command
// fetch completes. This avoids a race that can make the quit command tests
// flaky on slower Windows CI runners.
const cliOnlyCommands = createCliOnlySlashCommands()
setAvailableCommands([...cliOnlyCommands, ...sortCommandsWorkflowsFirst(cliCommands)])
} catch {
// Fallback: commands will be empty, menu won't show
// Keep CLI-only commands available even if backend command loading fails.
}
}
loadCommands()
@@ -843,6 +848,77 @@ export const ChatView: React.FC<ChatViewProps> = ({
}, 150)
}, [inkExit, onExit])
const handleCliOnlySlashCommand = useCallback(
(commandName: string): boolean => {
if (commandName === "help") {
setActivePanel({ type: "help" })
setTextInput("")
setCursorPos(0)
setSelectedSlashIndex(0)
setSlashMenuDismissed(true)
return true
}
if (commandName === "settings") {
setActivePanel({ type: "settings" })
setTextInput("")
setCursorPos(0)
setSelectedSlashIndex(0)
setSlashMenuDismissed(true)
return true
}
if (commandName === "models") {
const apiConfig = StateManager.get().getApiConfiguration()
const provider =
mode === "act"
? apiConfig.actModeApiProvider || apiConfig.planModeApiProvider
: apiConfig.planModeApiProvider || apiConfig.actModeApiProvider
const initialMode = !provider ? undefined : provider === "cline" ? "featured-models" : "model-picker"
const initialModelKey = mode === "act" ? "actModelId" : "planModelId"
setActivePanel({ type: "settings", initialMode, initialModelKey })
setTextInput("")
setCursorPos(0)
setSelectedSlashIndex(0)
setSlashMenuDismissed(true)
return true
}
if (commandName === "history") {
setActivePanel({ type: "history" })
setTextInput("")
setCursorPos(0)
setSelectedSlashIndex(0)
setSlashMenuDismissed(true)
return true
}
if (commandName === "skills") {
setActivePanel({ type: "skills" })
setTextInput("")
setCursorPos(0)
setSelectedSlashIndex(0)
setSlashMenuDismissed(true)
return true
}
if (commandName === "clear") {
void clearViewAndResetTask()
setSelectedSlashIndex(0)
setSlashMenuDismissed(true)
return true
}
if (commandName === "exit" || commandName === "q") {
handleExit()
return true
}
return false
},
[clearViewAndResetTask, handleExit, mode, setCursorPos, setTextInput],
)
// Get button config based on the last message state
const buttonConfig = useMemo(() => {
const lastMsg = messages[messages.length - 1] as ClineMessage | undefined
@@ -1102,6 +1178,17 @@ export const ChatView: React.FC<ChatViewProps> = ({
const inSlashMenu = slashInfo.inSlashMode && filteredCommands.length > 0 && !slashMenuDismissed
const inFileMenu = mentionInfo.inMentionMode && fileResults.length > 0 && !inSlashMenu
const standaloneSlashCommand = getStandaloneSlashCommandToExecute({
prompt,
inSlashMode: slashInfo.inSlashMode,
hasSlashMenu: inSlashMenu,
hasPendingAsk: !!pendingAsk,
isSpinnerActive,
})
if (key.return && standaloneSlashCommand && handleCliOnlySlashCommand(standaloneSlashCommand)) {
return
}
// 5. Slash command menu navigation (takes priority over file menu)
if (inSlashMenu) {
@@ -1116,64 +1203,7 @@ export const ChatView: React.FC<ChatViewProps> = ({
if (key.tab || key.return) {
const cmd = filteredCommands[selectedSlashIndex]
if (cmd) {
// Handle CLI-only commands locally
if (cmd.name === "help") {
setActivePanel({ type: "help" })
setTextInput("")
setCursorPos(0)
setSelectedSlashIndex(0)
setSlashMenuDismissed(true)
return
}
if (cmd.name === "settings") {
setActivePanel({ type: "settings" })
setTextInput("")
setCursorPos(0)
setSelectedSlashIndex(0)
setSlashMenuDismissed(true)
return
}
if (cmd.name === "models") {
const apiConfig = StateManager.get().getApiConfiguration()
// Use current mode's provider to determine picker type
const provider =
mode === "act"
? apiConfig.actModeApiProvider || apiConfig.planModeApiProvider
: apiConfig.planModeApiProvider || apiConfig.actModeApiProvider
const initialMode = !provider ? undefined : provider === "cline" ? "featured-models" : "model-picker"
// Set model for current mode (plan or act)
const initialModelKey = mode === "act" ? "actModelId" : "planModelId"
setActivePanel({ type: "settings", initialMode, initialModelKey })
setTextInput("")
setCursorPos(0)
setSelectedSlashIndex(0)
setSlashMenuDismissed(true)
return
}
if (cmd.name === "history") {
setActivePanel({ type: "history" })
setTextInput("")
setCursorPos(0)
setSelectedSlashIndex(0)
setSlashMenuDismissed(true)
return
}
if (cmd.name === "skills") {
setActivePanel({ type: "skills" })
setTextInput("")
setCursorPos(0)
setSelectedSlashIndex(0)
setSlashMenuDismissed(true)
return
}
if (cmd.name === "clear") {
clearViewAndResetTask()
setSelectedSlashIndex(0)
setSlashMenuDismissed(true)
return
}
if (cmd.name === "exit" || cmd.name === "q") {
handleExit()
if (handleCliOnlySlashCommand(cmd.name)) {
return
}
const newText = insertSlashCommand(textInput, slashInfo.slashIndex, cmd.name)
@@ -1462,13 +1492,12 @@ export const ChatView: React.FC<ChatViewProps> = ({
if (item.type === "header") {
// Show static robot frame in header (first frame, looking straight ahead)
return (
<Box flexDirection="column" key="header">
<Box flexDirection="column" key="header" marginBottom={1}>
<StaticRobotFrame />
<Text> </Text>
<Text bold color="white">
{centerText("What can I do for you?")}
</Text>
<Text> </Text>
</Box>
)
}
+4 -3
View File
@@ -7,6 +7,7 @@ import type { ClineMessage } from "@shared/ExtensionMessage"
import { Box, Text, useInput } from "ink"
import React, { useState } from "react"
import { useStdinContext } from "../context/StdinContext"
import { isEnterKey } from "../utils/input"
export type RestoreType = "task" | "workspace" | "taskAndWorkspace"
@@ -101,7 +102,7 @@ export const CheckpointMenu: React.FC<CheckpointMenuProps> = ({ messages, onSele
setSelectedCheckpoint((i) => Math.max(0, i - 1))
} else if (key.downArrow) {
setSelectedCheckpoint((i) => Math.min(checkpoints.length - 1, i + 1))
} else if (key.return && checkpoints.length > 0) {
} else if (isEnterKey(input, key) && checkpoints.length > 0) {
setStage("restoreType")
}
} else if (stage === "restoreType") {
@@ -109,7 +110,7 @@ export const CheckpointMenu: React.FC<CheckpointMenuProps> = ({ messages, onSele
setSelectedRestoreType((i) => Math.max(0, i - 1))
} else if (key.downArrow) {
setSelectedRestoreType((i) => Math.min(RESTORE_TYPE_OPTIONS.length - 1, i + 1))
} else if (key.return) {
} else if (isEnterKey(input, key)) {
const checkpoint = checkpoints[selectedCheckpoint]
const restoreType = RESTORE_TYPE_OPTIONS[selectedRestoreType]
if (checkpoint && restoreType) {
@@ -120,7 +121,7 @@ export const CheckpointMenu: React.FC<CheckpointMenuProps> = ({ messages, onSele
// Quick number selection for checkpoints
if (stage === "checkpoint") {
const num = parseInt(input, 10)
const num = Number.parseInt(input, 10)
if (!Number.isNaN(num) && num >= 1 && num <= checkpoints.length) {
setSelectedCheckpoint(num - 1)
setStage("restoreType")
+7 -1
View File
@@ -56,7 +56,13 @@ export interface ObjectEditorState {
editValue: string
}
export const EXCLUDED_KEYS = new Set(["taskHistory", "primaryRootIndex", "welcomeViewCompleted", "isNewUser"])
export const EXCLUDED_KEYS = new Set([
"taskHistory",
"primaryRootIndex",
"welcomeViewCompleted",
"isNewUser",
"cliKanbanMigrationAnnouncementShown",
])
export const EDITABLE_TYPES: Set<ValueType> = new Set(["string", "number", "boolean", "object"])
export const MAX_VISIBLE = 12
+117
View File
@@ -0,0 +1,117 @@
/**
* Rotating feature tips shown during thinking/acting phases.
* Appears after a brief delay and cycles through tips to educate users
* about Cline features while they wait.
*/
import { Box, Text } from "ink"
import React, { useCallback, useEffect, useRef, useState } from "react"
interface FeatureTipItem {
text: string
}
const FEATURE_TIPS: FeatureTipItem[] = [
{
text: 'Enable "Double-Check Completion" in settings to have Cline verify its work before finishing a task.',
},
{
text: "Add a .clinerules file to your project root to give Cline project-specific instructions.",
},
{
text: "Press Tab to switch between Plan and Act mode — plan an approach before Cline takes action.",
},
{
text: "Use @ in the chat input to add files, folders, or URLs as context for your task.",
},
{
text: "Set up MCP Servers to give Cline access to external tools and APIs.",
},
{
text: "Cline creates checkpoints after changes — you can always restore to a previous state.",
},
{
text: "Use /compact to condense long conversations and free up context window space.",
},
{
text: "Enable auto-approve for read-only tools like file reads to speed up exploration.",
},
{
text: "Use /settings to configure your API provider and model without leaving the terminal.",
},
{
text: "You can pass images with --images flag or paste image file paths in the chat.",
},
{
text: "Cline can browse websites — ask it to test your local dev server in the browser.",
},
{
text: "Use /reportbug to quickly file a GitHub issue with diagnostic context included.",
},
{
text: "Try 'npm i -g cline' to manage tasks on a Kankan board — orchestrate coding agents across worktrees.",
},
{
text: "Use Shift+Tab to toggle auto-approve all — let Cline work uninterrupted on trusted tasks.",
},
{
text: "Press Up/Down arrows in an empty input to browse your previous task prompts.",
},
{
text: "Type / to see all available commands — /history, /compact, /settings, and more.",
},
{
text: "Use /skills to browse and attach reusable skill files that guide Cline's behavior.",
},
{
text: 'You can disable these tips in /settings → Features → "Feature tips".',
},
]
const SHOW_DELAY_MS = 2000
const CYCLE_INTERVAL_MS = 8000
/**
* Shows rotating feature tips below the thinking indicator.
* Appears after a brief delay and cycles through tips while Cline is thinking/acting.
*/
export const FeatureTip: React.FC = React.memo(() => {
const [isVisible, setIsVisible] = useState(false)
const [tipIndex, setTipIndex] = useState(Math.floor(Math.random() * FEATURE_TIPS.length))
const cycleTimerRef = useRef<ReturnType<typeof setInterval> | null>(null)
const showTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null)
const currentTip = FEATURE_TIPS[tipIndex]
const advanceTip = useCallback(() => {
setTipIndex((prev) => (prev + 1) % FEATURE_TIPS.length)
}, [])
useEffect(() => {
showTimerRef.current = setTimeout(() => {
setIsVisible(true)
cycleTimerRef.current = setInterval(advanceTip, CYCLE_INTERVAL_MS)
}, SHOW_DELAY_MS)
return () => {
if (showTimerRef.current) {
clearTimeout(showTimerRef.current)
}
if (cycleTimerRef.current) {
clearInterval(cycleTimerRef.current)
}
}
}, [advanceTip])
if (!isVisible) {
return null
}
return (
<Box paddingLeft={1}>
<Text color="gray">
💡 <Text bold>Tip:</Text> {currentTip.text}
</Text>
</Box>
)
})
+2 -2
View File
@@ -13,7 +13,7 @@ import { showTaskWithId } from "@/core/controller/task/showTaskWithId"
import { COLORS } from "../constants/colors"
import { useStdinContext } from "../context/StdinContext"
import { useTerminalSize } from "../hooks/useTerminalSize"
import { isMouseEscapeSequence } from "../utils/input"
import { isEnterKey, isMouseEscapeSequence } from "../utils/input"
import { Panel } from "./Panel"
interface TaskHistoryItem {
@@ -142,7 +142,7 @@ export const HistoryPanelContent: React.FC<HistoryPanelContentProps> = ({ onClos
return
}
if (key.return && items[selectedIndex]) {
if (isEnterKey(input, key) && items[selectedIndex]) {
handleSelect(items[selectedIndex])
return
}
+3 -2
View File
@@ -10,6 +10,7 @@ import { showTaskWithId } from "@/core/controller/task/showTaskWithId"
import { StringRequest } from "@/shared/proto/cline/common"
import { useStdinContext } from "../context/StdinContext"
import { useTerminalSize } from "../hooks/useTerminalSize"
import { isEnterKey } from "../utils/input"
interface TaskHistoryItem {
id: string
@@ -40,7 +41,7 @@ interface HistoryViewProps {
/**
* Format separator
*/
function formatSeparator(char: string = "─", width: number = 80): string {
function formatSeparator(char = "─", width = 80): string {
return char.repeat(Math.max(width, 10))
}
@@ -111,7 +112,7 @@ export const HistoryView: React.FC<HistoryViewProps> = ({
setSelectedIndex((prev) => Math.max(0, prev - 1))
} else if (key.downArrow || input === "j") {
setSelectedIndex((prev) => Math.min(pageItems.length - 1, prev + 1))
} else if (key.return && pageItems[selectedIndex]) {
} else if (isEnterKey(input, key) && pageItems[selectedIndex]) {
onSelect(pageItems[selectedIndex])
} else if (key.leftArrow && hasPrevPage) {
handlePageChange(currentPage - 1)
+4 -3
View File
@@ -16,6 +16,7 @@ import {
importFromCodex,
importFromOpenCode,
} from "../utils/import-configs"
import { isEnterKey } from "../utils/input"
import { applyProviderConfig } from "../utils/provider-config"
type ImportStep = "select" | "confirm" | "saving" | "error"
@@ -95,13 +96,13 @@ export const ImportView: React.FC<ImportViewProps> = ({ source, onComplete, onCa
setSelectedIndex((prev) => (prev > 0 ? prev - 1 : keys.length - 1))
} else if (key.downArrow) {
setSelectedIndex((prev) => (prev < keys.length - 1 ? prev + 1 : 0))
} else if (key.return) {
} else if (isEnterKey(input, key)) {
setStep("confirm")
}
} else if (step === "confirm") {
if (key.upArrow || key.downArrow) {
setConfirmIndex((prev) => (prev === 0 ? 1 : 0))
} else if (key.return) {
} else if (isEnterKey(input, key)) {
if (confirmIndex === 0) {
handleConfirm()
} else {
@@ -109,7 +110,7 @@ export const ImportView: React.FC<ImportViewProps> = ({ source, onComplete, onCa
}
}
} else if (step === "error") {
if (key.return) {
if (isEnterKey(input, key)) {
onCancel()
}
}
@@ -0,0 +1,27 @@
import { render } from "ink-testing-library"
import { createElement } from "react"
import { describe, expect, it, vi } from "vitest"
import { KanbanMigrationView } from "./KanbanMigrationView"
describe("KanbanMigrationView", () => {
it("renders the migration options", () => {
const onSelect = vi.fn()
const { lastFrame } = render(createElement(KanbanMigrationView, { isRawModeSupported: true, onSelect }))
expect(lastFrame()).toContain("Introducing Cline Kanban!")
expect(lastFrame()).toContain("Open the new experience")
expect(lastFrame()).toContain("Launch Cline Kanban and start there by default.")
expect(lastFrame()).toContain("cline --tui")
expect(lastFrame()).toContain("You can always run cline --tui for the terminal experience.")
expect(lastFrame()).toContain("Exit")
})
it("selects the highlighted option with Enter", () => {
const onSelect = vi.fn()
const { stdin } = render(createElement(KanbanMigrationView, { isRawModeSupported: true, onSelect }))
stdin.write("\r")
expect(onSelect).toHaveBeenCalledWith("kanban")
})
})
@@ -0,0 +1,95 @@
import { Box, Text, useApp, useInput } from "ink"
import React, { useMemo, useState } from "react"
import { COLORS } from "../constants/colors"
import { StdinProvider, useStdinContext } from "../context/StdinContext"
import { isEnterKey } from "../utils/input"
import { type KanbanMigrationAction } from "../utils/kanban"
import { StaticRobotFrame } from "./AsciiMotionCli"
import { ErrorBoundary } from "./ErrorBoundary"
interface KanbanMigrationViewProps {
isRawModeSupported: boolean
onSelect: (action: KanbanMigrationAction) => void
}
interface MigrationMenuItem {
label: string
description: string
value: KanbanMigrationAction
}
const InternalKanbanMigrationView: React.FC<Pick<KanbanMigrationViewProps, "onSelect">> = ({ onSelect }) => {
const { exit } = useApp()
const { isRawModeSupported } = useStdinContext()
const items = useMemo<MigrationMenuItem[]>(
() => [
{
label: "Open the new experience",
description: "Launch Cline Kanban and start there by default.",
value: "kanban",
},
{
label: "Exit",
description: "You can always run cline --tui for the terminal experience.",
value: "exit",
},
],
[],
)
const [selectedIndex, setSelectedIndex] = useState(0)
useInput(
(input, key) => {
if (key.escape) {
onSelect("exit")
exit()
} else if (key.upArrow) {
setSelectedIndex((prev) => (prev > 0 ? prev - 1 : items.length - 1))
} else if (key.downArrow) {
setSelectedIndex((prev) => (prev < items.length - 1 ? prev + 1 : 0))
} else if (isEnterKey(input, key)) {
onSelect(items[selectedIndex].value)
exit()
}
},
{ isActive: isRawModeSupported },
)
return (
<Box flexDirection="column" width="100%">
<StaticRobotFrame />
<Text> </Text>
<Text bold color="white">
Introducing Cline Kanban!
</Text>
<Text color="gray">A board for orchestrating coding agents across worktrees, right from your browser.</Text>
<Text> </Text>
{items.map((item, index) => {
const isSelected = index === selectedIndex
return (
<Box flexDirection="column" key={item.value} marginBottom={1}>
<Text color={isSelected ? COLORS.primaryBlue : undefined}>
{isSelected ? " " : " "}
{item.label}
</Text>
<Text color="gray"> {item.description}</Text>
</Box>
)
})}
<Text> </Text>
<Text color="gray">Use arrow keys to navigate, Enter to select, Esc or Ctrl+C to exit</Text>
</Box>
)
}
export const KanbanMigrationView: React.FC<KanbanMigrationViewProps> = ({ isRawModeSupported, onSelect }) => {
const { exit } = useApp()
return (
<ErrorBoundary exit={exit}>
<StdinProvider isRawModeSupported={isRawModeSupported}>
<InternalKanbanMigrationView onSelect={onSelect} />
</StdinProvider>
</ErrorBoundary>
)
}
+3
View File
@@ -62,6 +62,8 @@ import {
sapAiCoreModels,
vertexDefaultModelId,
vertexModels,
wandbDefaultModelId,
wandbModels,
xaiDefaultModelId,
xaiModels,
} from "@/shared/api"
@@ -101,6 +103,7 @@ export const providerModels: Record<string, { models: Record<string, unknown>; d
sambanova: { models: sambanovaModels, defaultId: sambanovaDefaultModelId },
sapaicore: { models: sapAiCoreModels, defaultId: sapAiCoreDefaultModelId },
vertex: { models: vertexModels, defaultId: vertexDefaultModelId },
wandb: { models: wandbModels, defaultId: wandbDefaultModelId },
xai: { models: xaiModels, defaultId: xaiDefaultModelId },
zai: { models: internationalZAiModels, defaultId: internationalZAiDefaultModelId },
}
+26 -100
View File
@@ -1,112 +1,38 @@
import { render } from "ink-testing-library"
// biome-ignore lint/correctness/noUnusedImports: React must be in scope for JSX in this test file.
import React from "react"
import { beforeEach, describe, expect, it, vi } from "vitest"
import { CLI_ONLY_COMMANDS } from "@shared/slashCommands"
import { describe, expect, it } from "vitest"
import { filterCommands, getStandaloneSlashCommandName, getStandaloneSlashCommandToExecute } from "../utils/slash-commands"
// Mock ink's useApp
const mockExit = vi.fn()
vi.mock("ink", async (importOriginal) => {
const actual = await importOriginal<typeof import("ink")>()
return {
...actual,
useApp: () => ({ exit: mockExit }),
}
})
// Mock child_process
vi.mock("child_process", () => ({
execSync: vi.fn().mockReturnValue(""),
exec: vi.fn(),
const cliOnlySlashCommands = CLI_ONLY_COMMANDS.map((cmd) => ({
name: cmd.name,
description: cmd.description || "",
section: cmd.section || "default",
cliCompatible: true,
}))
// Mock dependencies
vi.mock("@/core/controller/slash/getAvailableSlashCommands", () => ({
getAvailableSlashCommands: vi.fn().mockResolvedValue({ commands: [] }),
}))
vi.mock("@/core/storage/StateManager", () => ({
StateManager: {
get: () => ({
getGlobalSettingsKey: vi.fn().mockReturnValue("act"),
getGlobalStateKey: vi.fn().mockReturnValue([]),
getApiConfiguration: vi.fn().mockReturnValue({}),
}),
},
}))
vi.mock("@/services/telemetry", () => ({
telemetryService: {
captureHostEvent: vi.fn(),
},
}))
vi.mock("@shared/services/Session", () => ({
Session: {
get: () => ({
getStats: vi.fn().mockReturnValue({}),
}),
},
}))
vi.mock("../context/TaskContext", () => ({
useTaskContext: () => ({
controller: {},
clearState: vi.fn(),
}),
useTaskState: () => ({
clineMessages: [],
}),
}))
vi.mock("../hooks/useStateSubscriber", () => ({
useIsSpinnerActive: () => ({ isActive: false, startTime: 0 }),
}))
import { ChatView } from "./ChatView"
// Helper to wait for async state updates
const delay = (ms = 60) => new Promise((resolve) => setTimeout(resolve, ms))
describe("Quit Command (/q and /exit)", () => {
const mockOnExit = vi.fn()
it("prioritizes /q as the selected slash command for an exact q query", () => {
const result = filterCommands(cliOnlySlashCommands, "q")
beforeEach(() => {
vi.clearAllMocks()
expect(result[0]?.name).toBe("q")
})
it("should exit the application when /q is selected from slash menu", async () => {
const { stdin } = render(<ChatView onExit={mockOnExit} />)
await delay()
// Type /q
stdin.write("/q")
await delay()
// Press Enter
stdin.write("\r")
// handleExit has a 150ms timeout
await delay(200)
expect(mockExit).toHaveBeenCalled()
expect(mockOnExit).toHaveBeenCalled()
it("detects /q as a standalone slash command", () => {
expect(getStandaloneSlashCommandName("/q")).toBe("q")
})
it("should exit the application when /exit is selected from slash menu", async () => {
const { stdin } = render(<ChatView onExit={mockOnExit} />)
await delay()
it("detects /exit as a standalone slash command", () => {
expect(getStandaloneSlashCommandName("/exit")).toBe("exit")
})
// Type /exit
stdin.write("/exit")
await delay()
// Press Enter
stdin.write("\r")
// handleExit has a 150ms timeout
await delay(200)
expect(mockExit).toHaveBeenCalled()
expect(mockOnExit).toHaveBeenCalled()
it("resolves /q to direct execution when no slash menu is active", () => {
expect(
getStandaloneSlashCommandToExecute({
prompt: "/q",
inSlashMode: true,
hasSlashMenu: false,
hasPendingAsk: false,
isSpinnerActive: false,
}),
).toBe("q")
})
})
+2 -1
View File
@@ -8,6 +8,7 @@ import { Box, Text, useInput } from "ink"
import React, { useState } from "react"
import { COLORS } from "../constants/colors"
import { useStdinContext } from "../context/StdinContext"
import { isEnterKey } from "../utils/input"
export interface SelectListItem {
id: string
@@ -31,7 +32,7 @@ export function SelectList<T extends SelectListItem>({ items, onSelect, isActive
setSelectedIndex((i) => (i > 0 ? i - 1 : items.length - 1))
} else if (key.downArrow) {
setSelectedIndex((i) => (i < items.length - 1 ? i + 1 : 0))
} else if (key.return) {
} else if (isEnterKey(_input, key)) {
const item = items[selectedIndex]
if (item) {
onSelect(item)
@@ -129,6 +129,12 @@ const FEATURE_SETTINGS = {
label: "Double-check completion",
description: "Reject first completion attempt and require re-verification",
},
showFeatureTips: {
stateKey: "showFeatureTips",
default: true,
label: "Feature tips",
description: "Show tips during thinking phases",
},
} as const
type FeatureKey = keyof typeof FEATURE_SETTINGS
+74 -30
View File
@@ -38,6 +38,46 @@ import { SkillsPanelContent } from "./SkillsPanelContent"
// Helper to wait for async state updates
const delay = (ms = 60) => new Promise((resolve) => setTimeout(resolve, ms))
type WaitForConditionOptions = {
timeoutMs?: number
intervalMs?: number
errorMessage: string
}
const waitForCondition = async (
condition: () => boolean,
{ timeoutMs = 1000, intervalMs = 25, errorMessage }: WaitForConditionOptions,
) => {
const start = Date.now()
while (Date.now() - start < timeoutMs) {
if (condition()) {
return
}
await delay(intervalMs)
}
throw new Error(errorMessage)
}
const waitForFrameToInclude = async (lastFrame: () => string | undefined, text: string) =>
waitForCondition(() => (lastFrame() || "").includes(text), {
errorMessage: `Expected frame to include: ${text}`,
})
const waitForFrameToExclude = async (lastFrame: () => string | undefined, text: string) =>
waitForCondition(() => !(lastFrame() || "").includes(text), {
errorMessage: `Expected frame to exclude: ${text}`,
})
const waitForMockToBeCalled = async (mockFn: { mock: { calls: unknown[] } }) =>
waitForCondition(() => mockFn.mock.calls.length > 0, {
errorMessage: "Expected mock to be called",
})
const waitForSkillsPanelReady = async (lastFrame: () => string | undefined, expectedText: string) => {
await waitForFrameToExclude(lastFrame, "Loading skills...")
await waitForFrameToInclude(lastFrame, expectedText)
}
describe("SkillsPanelContent", () => {
const mockController = {} as any
const mockOnClose = vi.fn()
@@ -64,11 +104,11 @@ describe("SkillsPanelContent", () => {
localSkills: [],
})
const { stdin } = render(<SkillsPanelContent {...defaultProps} />)
await delay()
const { stdin, lastFrame } = render(<SkillsPanelContent {...defaultProps} />)
await waitForSkillsPanelReady(lastFrame, "No skills installed.")
stdin.write("\x1B") // Escape
await delay()
await waitForMockToBeCalled(mockOnClose)
expect(mockOnClose).toHaveBeenCalled()
})
@@ -79,11 +119,11 @@ describe("SkillsPanelContent", () => {
localSkills: [],
})
const { stdin } = render(<SkillsPanelContent {...defaultProps} />)
await delay()
const { stdin, lastFrame } = render(<SkillsPanelContent {...defaultProps} />)
await waitForSkillsPanelReady(lastFrame, "test-skill")
stdin.write("\r") // Enter
await delay()
await waitForMockToBeCalled(mockOnUseSkill)
expect(mockOnUseSkill).toHaveBeenCalledWith("/test/path/SKILL.md")
})
@@ -94,11 +134,11 @@ describe("SkillsPanelContent", () => {
localSkills: [],
})
const { stdin } = render(<SkillsPanelContent {...defaultProps} />)
await delay()
const { stdin, lastFrame } = render(<SkillsPanelContent {...defaultProps} />)
await waitForSkillsPanelReady(lastFrame, "test-skill")
stdin.write(" ") // Space
await delay()
await waitForMockToBeCalled(mockToggleSkill)
expect(mockToggleSkill).toHaveBeenCalledWith(
mockController,
@@ -116,15 +156,17 @@ describe("SkillsPanelContent", () => {
localSkills: [],
})
const { stdin } = render(<SkillsPanelContent {...defaultProps} />)
await delay()
const { stdin, lastFrame } = render(<SkillsPanelContent {...defaultProps} />)
await waitForSkillsPanelReady(lastFrame, "skill")
// Navigate down to marketplace (past the one skill)
stdin.write("\x1B[B") // Down arrow
await delay()
// Use vim-style navigation here because it's more deterministic in the
// full suite than raw arrow escape sequences on Windows.
stdin.write("j")
await waitForFrameToInclude(lastFrame, " Browse more skills at https://skills.sh/")
stdin.write("\r") // Enter
await delay()
await waitForMockToBeCalled(mockExec)
// Should have called exec with open command
expect(mockExec).toHaveBeenCalled()
@@ -141,16 +183,16 @@ describe("SkillsPanelContent", () => {
localSkills: [],
})
const { stdin } = render(<SkillsPanelContent {...defaultProps} />)
await delay()
const { stdin, lastFrame } = render(<SkillsPanelContent {...defaultProps} />)
await waitForSkillsPanelReady(lastFrame, "skill-1")
// Navigate down
stdin.write("\x1B[B") // Down arrow
await delay()
await waitForFrameToInclude(lastFrame, " ● skill-2")
// Press Enter - should use second skill
stdin.write("\r")
await delay()
await waitForMockToBeCalled(mockOnUseSkill)
expect(mockOnUseSkill).toHaveBeenCalledWith("/path2")
})
@@ -164,16 +206,16 @@ describe("SkillsPanelContent", () => {
localSkills: [],
})
const { stdin } = render(<SkillsPanelContent {...defaultProps} />)
await delay()
const { stdin, lastFrame } = render(<SkillsPanelContent {...defaultProps} />)
await waitForSkillsPanelReady(lastFrame, "skill-1")
// Navigate down with j
stdin.write("j")
await delay()
await waitForFrameToInclude(lastFrame, " ● skill-2")
// Press Enter - should use second skill
stdin.write("\r")
await delay()
await waitForMockToBeCalled(mockOnUseSkill)
expect(mockOnUseSkill).toHaveBeenCalledWith("/path2")
})
@@ -186,10 +228,11 @@ describe("SkillsPanelContent", () => {
mockToggleSkill.mockRejectedValueOnce(new Error("toggle failed"))
const { stdin, lastFrame } = render(<SkillsPanelContent {...defaultProps} />)
await delay()
await waitForSkillsPanelReady(lastFrame, "test-skill")
stdin.write(" ") // Space to toggle
await delay(100)
await waitForMockToBeCalled(mockToggleSkill)
await waitForFrameToInclude(lastFrame, "● test-skill")
// toggleSkill was called with enabled: false (toggled from true)
expect(mockToggleSkill).toHaveBeenCalledWith(mockController, expect.objectContaining({ enabled: false }))
@@ -204,15 +247,15 @@ describe("SkillsPanelContent", () => {
localSkills: [],
})
const { stdin } = render(<SkillsPanelContent {...defaultProps} />)
await delay()
const { stdin, lastFrame } = render(<SkillsPanelContent {...defaultProps} />)
await waitForSkillsPanelReady(lastFrame, "only-skill")
// Navigate up from first item (should wrap to last - marketplace)
stdin.write("\x1B[A") // Up arrow
await delay()
await waitForFrameToInclude(lastFrame, " Browse more skills at https://skills.sh/")
stdin.write("\r") // Enter
await delay()
await waitForMockToBeCalled(mockExec)
// Should have opened marketplace (wrapped to last item)
expect(mockExec).toHaveBeenCalled()
@@ -221,8 +264,9 @@ describe("SkillsPanelContent", () => {
describe("skill loading", () => {
it("should call refreshSkills on mount", async () => {
render(<SkillsPanelContent {...defaultProps} />)
await delay()
const { lastFrame } = render(<SkillsPanelContent {...defaultProps} />)
await waitForMockToBeCalled(mockRefreshSkills)
await waitForFrameToExclude(lastFrame, "Loading skills...")
expect(mockRefreshSkills).toHaveBeenCalled()
})
+37 -9
View File
@@ -6,13 +6,13 @@
import { exec } from "node:child_process"
import os from "node:os"
import { Box, Text, useInput } from "ink"
import React, { useCallback, useEffect, useMemo, useState } from "react"
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react"
import type { Controller } from "@/core/controller"
import { refreshSkills } from "@/core/controller/file/refreshSkills"
import { toggleSkill } from "@/core/controller/file/toggleSkill"
import { COLORS } from "../constants/colors"
import { useStdinContext } from "../context/StdinContext"
import { isMouseEscapeSequence } from "../utils/input"
import { isEnterKey, isMouseEscapeSequence } from "../utils/input"
import { Panel } from "./Panel"
const SKILLS_MARKETPLACE_URL = "https://skills.sh/"
@@ -38,6 +38,14 @@ export const SkillsPanelContent: React.FC<SkillsPanelContentProps> = ({ controll
const [localSkills, setLocalSkills] = useState<SkillInfo[]>([])
const [selectedIndex, setSelectedIndex] = useState(0)
const [isLoading, setIsLoading] = useState(true)
const inputStateRef = useRef({
isLoading: true,
selectedIndex: 0,
skillEntries: [] as Array<{ skill: SkillInfo; isGlobal: boolean }>,
})
const handleToggleRef = useRef<() => Promise<void>>(async () => {})
const handleUseRef = useRef<() => void>(() => {})
const openMarketplaceRef = useRef<() => void>(() => {})
// Load skills on mount
useEffect(() => {
@@ -58,8 +66,12 @@ export const SkillsPanelContent: React.FC<SkillsPanelContentProps> = ({ controll
// Build flat list of skills with source info (global first, then local, alphabetical within each)
const skillEntries = useMemo(() => {
const entries: { skill: SkillInfo; isGlobal: boolean }[] = []
globalSkills.forEach((skill) => entries.push({ skill, isGlobal: true }))
localSkills.forEach((skill) => entries.push({ skill, isGlobal: false }))
globalSkills.forEach((skill) => {
entries.push({ skill, isGlobal: true })
})
localSkills.forEach((skill) => {
entries.push({ skill, isGlobal: false })
})
return entries.sort((a, b) => {
if (a.isGlobal !== b.isGlobal) return a.isGlobal ? -1 : 1
return a.skill.name.localeCompare(b.skill.name)
@@ -117,6 +129,14 @@ export const SkillsPanelContent: React.FC<SkillsPanelContentProps> = ({ controll
}
})
}, [])
handleToggleRef.current = handleToggle
handleUseRef.current = handleUse
openMarketplaceRef.current = openMarketplace
inputStateRef.current = {
isLoading,
selectedIndex,
skillEntries,
}
// Total items = skills + 1 for marketplace link
const totalItems = skillEntries.length + 1
@@ -132,6 +152,14 @@ export const SkillsPanelContent: React.FC<SkillsPanelContentProps> = ({ controll
return
}
const { isLoading, selectedIndex, skillEntries } = inputStateRef.current
if (isLoading) {
return
}
const totalItems = skillEntries.length + 1
const isMarketplaceSelected = selectedIndex === skillEntries.length
// Navigation
if (key.upArrow || input === "k") {
setSelectedIndex((i) => (i > 0 ? i - 1 : totalItems - 1))
@@ -143,16 +171,16 @@ export const SkillsPanelContent: React.FC<SkillsPanelContentProps> = ({ controll
}
// Actions
if (key.return) {
if (isEnterKey(input, key)) {
if (isMarketplaceSelected) {
openMarketplace()
openMarketplaceRef.current()
} else {
handleUse()
handleUseRef.current()
}
return
}
if (input === " " && !isMarketplaceSelected) {
handleToggle()
void handleToggleRef.current()
return
}
},
@@ -248,7 +276,7 @@ const SkillRow: React.FC<{ skill: SkillInfo; isSelected: boolean }> = ({ skill,
{skill.description && (
<Box marginLeft={4}>
<Text color="gray">
{skill.description.length > 60 ? skill.description.slice(0, 57) + "..." : skill.description}
{skill.description.length > 60 ? `${skill.description.slice(0, 57)}...` : skill.description}
</Text>
</Box>
)}
+9 -3
View File
@@ -4,7 +4,9 @@
import { Box, Text, useInput } from "ink"
import React, { useEffect, useMemo, useState } from "react"
import { StateManager } from "@/core/storage/StateManager"
import { COLORS } from "../constants/colors"
import { FeatureTip } from "./FeatureTip"
interface ThinkingIndicatorProps {
mode?: "act" | "plan"
@@ -52,6 +54,7 @@ const ShimmerText: React.FC<{ text: string; color: string; shimmerPos: number }>
}
export const ThinkingIndicator: React.FC<ThinkingIndicatorProps> = ({ mode = "act", startTime, onCancel }) => {
const showFeatureTips = StateManager.get().getGlobalSettingsKey("showFeatureTips") ?? true
const message = mode === "plan" ? "Planning" : "Acting"
const color = mode === "plan" ? "yellow" : COLORS.primaryBlue
@@ -118,9 +121,12 @@ export const ThinkingIndicator: React.FC<ThinkingIndicatorProps> = ({ mode = "ac
}, [startTime, elapsedMs])
return (
<Box paddingLeft={1}>
<ShimmerText color={color} shimmerPos={shimmerPos} text={fullText} />
{elapsedStr && <Text color="gray"> ({elapsedStr} · esc to interrupt)</Text>}
<Box flexDirection="column">
<Box paddingLeft={1}>
<ShimmerText color={color} shimmerPos={shimmerPos} text={fullText} />
{elapsedStr && <Text color="gray"> ({elapsedStr} · esc to interrupt)</Text>}
</Box>
{showFeatureTips && <FeatureTip />}
</Box>
)
}
-333
View File
@@ -1,333 +0,0 @@
/**
* Welcome view component
* Shows an interactive prompt when user starts cline without a command
* Supports file mentions with @
*/
import { Box, Text, useInput } from "ink"
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react"
import { StateManager } from "@/core/storage/StateManager"
import type { ApiProvider } from "@/shared/api"
import { getProviderDefaultModelId, getProviderModelIdKey, Mode, SettingsKey } from "@/shared/storage"
import { useStdinContext } from "../context/StdinContext"
import {
checkAndWarnRipgrepMissing,
extractMentionQuery,
type FileSearchResult,
getRipgrepInstallInstructions,
insertMention,
searchWorkspaceFiles,
} from "../utils/file-search"
import { isMouseEscapeSequence } from "../utils/input"
import { parseImagesFromInput } from "../utils/parser"
import { AccountInfoView } from "./AccountInfoView"
import { FileMentionMenu } from "./FileMentionMenu"
interface WelcomeViewProps {
onSubmit: (prompt: string, imagePaths: string[]) => void
onExit?: () => void
controller?: any
}
// ASCII art Cline logo
const CLINE_LOGO = [
" ::::::: ",
" ::::::::: ",
" ::::::::::::::::: ",
" ::::::::::::::::::::::: ",
" ::::::::::::::::::::::::: ",
" ::::::::::::::::::::::::::: ",
" ::::::: ::::::: ::::::: ",
" ::::::: ::::: ::::::: ",
":::::::: ::::: ::::::::",
":::::::: ::::: ::::::::",
" ::::::: ::::: ::::::: ",
" ::::::: ::::::: ::::::: ",
" ::::::::::::::::::::::::::: ",
" ::::::::::::::::::::::::: ",
" ::::::::::::::::::::::: ",
" :::::::::::::::: ",
]
const SEARCH_DEBOUNCE_MS = 150
const RIPGREP_WARNING_DURATION_MS = 5000
const MAX_SEARCH_RESULTS = 15
export const WelcomeView: React.FC<WelcomeViewProps> = ({ onSubmit, onExit, controller }) => {
const { isRawModeSupported } = useStdinContext()
const [textInput, setTextInput] = useState("")
const [fileResults, setFileResults] = useState<FileSearchResult[]>([])
const [selectedIndex, setSelectedIndex] = useState(0)
const [isSearching, setIsSearching] = useState(false)
const [showRipgrepWarning, setShowRipgrepWarning] = useState(false)
const [escPressedOnce, setEscPressedOnce] = useState(false)
const [mode, setMode] = useState<Mode>(() => {
const stateManager = StateManager.get()
return stateManager.getGlobalSettingsKey("mode") || "act"
})
const provider = useMemo(() => {
const stateManager = StateManager.get()
const mode = stateManager.getGlobalSettingsKey("mode") as string
const providerKey = mode === "act" ? "actModeApiProvider" : "planModeApiProvider"
const currentProvider = stateManager.getGlobalSettingsKey(providerKey) as string
return currentProvider || "cline"
}, [controller])
// Get model ID based on current mode and provider
// Different providers use different state keys (e.g., cline uses actModeOpenRouterModelId)
const modelId = useMemo(() => {
const stateManager = StateManager.get()
const modelKey = getProviderModelIdKey(provider as ApiProvider, mode)
return (
(stateManager.getGlobalSettingsKey(modelKey as SettingsKey) as string) ||
getProviderDefaultModelId(provider as ApiProvider)
)
}, [mode, provider])
const toggleMode = useCallback(() => {
const newMode: Mode = mode === "act" ? "plan" : "act"
setMode(newMode)
const stateManager = StateManager.get()
stateManager.setGlobalState("mode", newMode)
}, [mode])
const refs = useRef({
searchTimeout: null as NodeJS.Timeout | null,
lastQuery: "",
hasCheckedRipgrep: false,
})
const { prompt, imagePaths } = parseImagesFromInput(textInput)
const mentionInfo = useMemo(() => extractMentionQuery(textInput), [textInput])
const workspacePath = useMemo(() => {
try {
const root = controller?.getWorkspaceManagerSync?.()?.getPrimaryRoot?.()
if (root?.path) {
return root.path
}
} catch {
// Fallback to cwd
}
return process.cwd()
}, [controller])
// Search for files when in mention mode
useEffect(() => {
const { current: r } = refs
if (!mentionInfo.inMentionMode) {
setFileResults([])
setSelectedIndex(0)
if (r.searchTimeout) {
clearTimeout(r.searchTimeout)
r.searchTimeout = null
}
return
}
// Check for ripgrep on first mention trigger
if (!r.hasCheckedRipgrep) {
r.hasCheckedRipgrep = true
if (checkAndWarnRipgrepMissing()) {
setShowRipgrepWarning(true)
setTimeout(() => setShowRipgrepWarning(false), RIPGREP_WARNING_DURATION_MS)
}
}
const { query } = mentionInfo
if (query === r.lastQuery) {
return
}
r.lastQuery = query
if (r.searchTimeout) {
clearTimeout(r.searchTimeout)
}
setIsSearching(true)
r.searchTimeout = setTimeout(async () => {
try {
const results = await searchWorkspaceFiles(query, workspacePath, MAX_SEARCH_RESULTS)
setFileResults(results)
setSelectedIndex(0)
} catch {
setFileResults([])
} finally {
setIsSearching(false)
}
}, SEARCH_DEBOUNCE_MS)
return () => {
if (r.searchTimeout) {
clearTimeout(r.searchTimeout)
}
}
}, [mentionInfo.inMentionMode, mentionInfo.query, workspacePath])
useInput(
(input, key) => {
// Filter out mouse escape sequences
if (isMouseEscapeSequence(input)) {
return
}
const inMenu = mentionInfo.inMentionMode && fileResults.length > 0
// Menu navigation
if (inMenu) {
if (key.upArrow) {
setSelectedIndex((i) => (i > 0 ? i - 1 : fileResults.length - 1))
return
}
if (key.downArrow) {
setSelectedIndex((i) => (i < fileResults.length - 1 ? i + 1 : 0))
return
}
if (key.tab || key.return) {
const file = fileResults[selectedIndex]
if (file) {
setTextInput(insertMention(textInput, mentionInfo.atIndex, file.path))
setFileResults([])
setSelectedIndex(0)
}
return
}
if (key.escape) {
setFileResults([])
setSelectedIndex(0)
return
}
}
// Normal input handling
if (key.tab && !mentionInfo.inMentionMode) {
toggleMode()
return
}
if (key.return && !mentionInfo.inMentionMode) {
if (prompt.trim() || imagePaths.length > 0) {
onSubmit(prompt.trim(), imagePaths)
}
return
}
if (key.escape && !mentionInfo.inMentionMode) {
if (escPressedOnce) {
onExit?.()
} else {
setEscPressedOnce(true)
}
return
}
if (key.backspace || key.delete) {
setTextInput((prev) => prev.slice(0, -1))
setEscPressedOnce(false)
return
}
if (input && !key.ctrl && !key.meta && !key.upArrow && !key.downArrow && !key.tab) {
setTextInput((prev) => prev + input)
setEscPressedOnce(false)
}
},
{ isActive: isRawModeSupported },
)
const borderColor = mode === "act" ? "blue" : "yellow"
return (
<Box flexDirection="column" width="100%">
{/* Account/Provider info at top */}
{controller && (
<Box marginBottom={1}>
<AccountInfoView controller={controller} />
</Box>
)}
{/* Cline logo - centered */}
<Box alignItems="center" flexDirection="column">
{CLINE_LOGO.map((line, idx) => (
// biome-ignore lint/suspicious/noArrayIndexKey: static array that never changes
<Text color="white" key={idx}>
{line}
</Text>
))}
</Box>
{/* Main prompt - centered, bold */}
<Box justifyContent="center" marginTop={1}>
<Text bold color="white">
What can I do for you?
</Text>
</Box>
{/* Ripgrep warning if needed */}
{showRipgrepWarning && (
<Box marginTop={1}>
<Text color="yellow"> ripgrep not found - file search will be slower. </Text>
<Text color="gray">Install: {getRipgrepInstallInstructions()}</Text>
</Box>
)}
{/* Input field with border */}
<Box
borderColor={borderColor}
borderStyle="round"
flexDirection="row"
marginTop={1}
paddingLeft={1}
paddingRight={1}
width="100%">
<Text>{textInput}</Text>
<Text inverse> </Text>
</Box>
{/* Model ID and Mode toggle row */}
<Box justifyContent="space-between" width="100%">
{/* Model ID on left */}
<Text color="gray">{modelId}</Text>
{/* Mode toggle on right */}
<Box gap={1}>
<Box>
<Text bold={mode === "plan"} color={mode === "plan" ? "yellow" : "gray"}>
{mode === "plan" ? "●" : "○"} Plan
</Text>
</Box>
<Box>
<Text bold={mode === "act"} color={mode === "act" ? "blue" : "gray"}>
{mode === "act" ? "●" : "○"} Act
</Text>
</Box>
<Text color="gray">(Tab)</Text>
</Box>
</Box>
{/* File mention menu - below input */}
{mentionInfo.inMentionMode && (
<FileMentionMenu
isLoading={isSearching}
query={mentionInfo.query}
results={fileResults}
selectedIndex={selectedIndex}
/>
)}
{/* Attached images */}
{imagePaths.length > 0 && (
<Text color="magenta">
📎 {imagePaths.length} image{imagePaths.length > 1 ? "s" : ""} attached
</Text>
)}
{/* Help text */}
<Box>
<Text color="gray">Enter to submit · @ to mention files · </Text>
<Text bold={escPressedOnce} color={escPressedOnce ? "white" : "gray"}>
{escPressedOnce ? "Press Esc again to exit" : "Esc to exit"}
</Text>
</Box>
</Box>
)
}
+3 -1
View File
@@ -79,7 +79,7 @@ export class CliDiffServiceClient implements DiffServiceClientInterface {
* CLI implementation of EnvService - handles environment operations
*/
export class CliEnvServiceClient implements EnvServiceClientInterface {
private clipboardContent: string = ""
private clipboardContent = ""
private getTelemetrySetting(): proto.host.Setting {
// Read from StateManager - defaults to ENABLED if not set or "unset"
@@ -102,6 +102,8 @@ export class CliEnvServiceClient implements EnvServiceClientInterface {
version: CLI_VERSION,
platform: "Cline CLI - Node.js",
clineType: ClineClient.Cli,
// remoteName is intentionally omitted — the CLI runs locally on the user's machine.
// If CLI-in-container scenarios arise, populate this field to enable remote cadence tuning.
})
}
+134 -31
View File
@@ -11,6 +11,22 @@ import { captureUnhandledException } from "."
describe("CLI Commands", () => {
let program: Command
function getCommand(name: string): Command {
const command = program.commands.find((candidate) => candidate.name() === name)
if (!command) {
throw new Error(`Missing command: ${name}`)
}
return command
}
function getSubcommand(commandName: string, subcommandName: string): Command {
const subcommand = getCommand(commandName).commands.find((candidate) => candidate.name() === subcommandName)
if (!subcommand) {
throw new Error(`Missing subcommand: ${commandName} ${subcommandName}`)
}
return subcommand
}
beforeEach(() => {
// Create a fresh program instance for each test
program = new Command()
@@ -67,6 +83,28 @@ describe("CLI Commands", () => {
.option("--config <path>", "Configuration directory")
.action(() => {})
const mcpCommand = program.command("mcp").description("Manage MCP servers")
mcpCommand
.command("add")
.description("Add an MCP server shortcut")
.argument("<name>", "MCP server name")
.argument("[targetOrCommand...]", "Command args for stdio, or URL for remote")
.option("--type <type>", "Transport type", "stdio")
.option("-c, --cwd <path>", "Working directory")
.option("--config <path>", "Configuration directory")
.action(() => {})
program
.command("kanban")
.description("Run kanban")
.action(() => {})
program
.command("update")
.description("Check for updates and install if available")
.option("-v, --verbose", "Show verbose output")
.action(() => {})
// Default command for interactive mode
program
.argument("[prompt]", "Task prompt")
@@ -81,6 +119,9 @@ describe("CLI Commands", () => {
.option("--auto-condense", "Enable AI-powered context compaction instead of mechanical truncation")
.option("--hooks-dir <path>", "Additional hooks directory")
.option("--auto-approve-all", "Enable auto-approve all")
.option("--update", "Check for updates and install if available")
.option("--kanban", "Run kanban")
.option("--tui", "Open the legacy terminal UI instead of the kanban experience")
.action(() => {})
})
@@ -97,119 +138,119 @@ describe("CLI Commands", () => {
})
it("should parse --act flag", () => {
const taskCmd = program.commands.find((c) => c.name() === "task")!
const taskCmd = getCommand("task")
const args = ["test prompt", "--act"]
taskCmd.parse(args, { from: "user" })
expect(taskCmd.opts().act).toBe(true)
})
it("should parse --plan flag", () => {
const taskCmd = program.commands.find((c) => c.name() === "task")!
const taskCmd = getCommand("task")
const args = ["test prompt", "--plan"]
taskCmd.parse(args, { from: "user" })
expect(taskCmd.opts().plan).toBe(true)
})
it("should parse --yolo flag", () => {
const taskCmd = program.commands.find((c) => c.name() === "task")!
const taskCmd = getCommand("task")
const args = ["test prompt", "--yolo"]
taskCmd.parse(args, { from: "user" })
expect(taskCmd.opts().yolo).toBe(true)
})
it("should parse --auto-approve-all flag", () => {
const taskCmd = program.commands.find((c) => c.name() === "task")!
const taskCmd = getCommand("task")
const args = ["test prompt", "--auto-approve-all"]
taskCmd.parse(args, { from: "user" })
expect(taskCmd.opts().autoApproveAll).toBe(true)
})
it("should parse --model option", () => {
const taskCmd = program.commands.find((c) => c.name() === "task")!
const taskCmd = getCommand("task")
const args = ["test prompt", "--model", "claude-sonnet-4-20250514"]
taskCmd.parse(args, { from: "user" })
expect(taskCmd.opts().model).toBe("claude-sonnet-4-20250514")
})
it("should parse --images option with multiple paths", () => {
const taskCmd = program.commands.find((c) => c.name() === "task")!
const taskCmd = getCommand("task")
const args = ["test prompt", "--images", "/path/to/img1.png", "/path/to/img2.jpg"]
taskCmd.parse(args, { from: "user" })
expect(taskCmd.opts().images).toEqual(["/path/to/img1.png", "/path/to/img2.jpg"])
})
it("should parse --verbose flag", () => {
const taskCmd = program.commands.find((c) => c.name() === "task")!
const taskCmd = getCommand("task")
const args = ["test prompt", "--verbose"]
taskCmd.parse(args, { from: "user" })
expect(taskCmd.opts().verbose).toBe(true)
})
it("should parse --cwd option", () => {
const taskCmd = program.commands.find((c) => c.name() === "task")!
const taskCmd = getCommand("task")
const args = ["test prompt", "--cwd", "/some/path"]
taskCmd.parse(args, { from: "user" })
expect(taskCmd.opts().cwd).toBe("/some/path")
})
it("should parse --config option", () => {
const taskCmd = program.commands.find((c) => c.name() === "task")!
const taskCmd = getCommand("task")
const args = ["test prompt", "--config", "/custom/config"]
taskCmd.parse(args, { from: "user" })
expect(taskCmd.opts().config).toBe("/custom/config")
})
it("should parse --thinking flag", () => {
const taskCmd = program.commands.find((c) => c.name() === "task")!
const taskCmd = getCommand("task")
const args = ["test prompt", "--thinking"]
taskCmd.parse(args, { from: "user" })
expect(taskCmd.opts().thinking).toBe(true)
})
it("should parse --thinking with token budget", () => {
const taskCmd = program.commands.find((c) => c.name() === "task")!
const taskCmd = getCommand("task")
const args = ["test prompt", "--thinking", "8000"]
taskCmd.parse(args, { from: "user" })
expect(taskCmd.opts().thinking).toBe("8000")
})
it("should parse --reasoning-effort option", () => {
const taskCmd = program.commands.find((c) => c.name() === "task")!
const taskCmd = getCommand("task")
const args = ["test prompt", "--reasoning-effort", "high"]
taskCmd.parse(args, { from: "user" })
expect(taskCmd.opts().reasoningEffort).toBe("high")
})
it("should parse --max-consecutive-mistakes option", () => {
const taskCmd = program.commands.find((c) => c.name() === "task")!
const taskCmd = getCommand("task")
const args = ["test prompt", "--max-consecutive-mistakes", "999"]
taskCmd.parse(args, { from: "user" })
expect(taskCmd.opts().maxConsecutiveMistakes).toBe("999")
})
it("should parse --hooks-dir option", () => {
const taskCmd = program.commands.find((c) => c.name() === "task")!
const taskCmd = getCommand("task")
const args = ["test prompt", "--hooks-dir", "/tmp/hooks"]
taskCmd.parse(args, { from: "user" })
expect(taskCmd.opts().hooksDir).toBe("/tmp/hooks")
})
it("should parse --double-check-completion flag", () => {
const taskCmd = program.commands.find((c) => c.name() === "task")!
const taskCmd = getCommand("task")
const args = ["test prompt", "--double-check-completion"]
taskCmd.parse(args, { from: "user" })
expect(taskCmd.opts().doubleCheckCompletion).toBe(true)
})
it("should parse --auto-condense flag", () => {
const taskCmd = program.commands.find((c) => c.name() === "task")!
const taskCmd = getCommand("task")
const args = ["test prompt", "--auto-condense"]
taskCmd.parse(args, { from: "user" })
expect(taskCmd.opts().autoCondense).toBe(true)
})
it("should parse short flags", () => {
const taskCmd = program.commands.find((c) => c.name() === "task")!
const taskCmd = getCommand("task")
const args = ["test prompt", "-a", "-v", "-m", "gpt-4"]
taskCmd.parse(args, { from: "user" })
expect(taskCmd.opts().act).toBe(true)
@@ -220,26 +261,26 @@ describe("CLI Commands", () => {
describe("history command", () => {
it("should have default limit of 10", () => {
const historyCmd = program.commands.find((c) => c.name() === "history")!
const historyCmd = getCommand("history")
historyCmd.parse([], { from: "user" })
expect(historyCmd.opts().limit).toBe("10")
})
it("should have default page of 1", () => {
const historyCmd = program.commands.find((c) => c.name() === "history")!
const historyCmd = getCommand("history")
historyCmd.parse([], { from: "user" })
expect(historyCmd.opts().page).toBe("1")
})
it("should parse --limit option", () => {
const historyCmd = program.commands.find((c) => c.name() === "history")!
const historyCmd = getCommand("history")
const args = ["--limit", "20"]
historyCmd.parse(args, { from: "user" })
expect(historyCmd.opts().limit).toBe("20")
})
it("should parse --page option", () => {
const historyCmd = program.commands.find((c) => c.name() === "history")!
const historyCmd = getCommand("history")
const args = ["--page", "3"]
historyCmd.parse(args, { from: "user" })
expect(historyCmd.opts().page).toBe("3")
@@ -252,7 +293,7 @@ describe("CLI Commands", () => {
})
it("should parse short flags", () => {
const historyCmd = program.commands.find((c) => c.name() === "history")!
const historyCmd = getCommand("history")
const args = ["-n", "5", "-p", "2"]
historyCmd.parse(args, { from: "user" })
expect(historyCmd.opts().limit).toBe("5")
@@ -267,13 +308,34 @@ describe("CLI Commands", () => {
})
it("should parse --config option", () => {
const configCmd = program.commands.find((c) => c.name() === "config")!
const configCmd = getCommand("config")
const args = ["--config", "/custom/path"]
configCmd.parse(args, { from: "user" })
expect(configCmd.opts().config).toBe("/custom/path")
})
})
describe("kanban command", () => {
it("should parse kanban command", () => {
const args = ["node", "cli", "kanban"]
program.parse(args)
})
})
describe("update command", () => {
it("should parse update command", () => {
const args = ["node", "cli", "update"]
program.parse(args)
})
it("should parse --verbose on update command", () => {
const updateCmd = getCommand("update")
const args = ["--verbose"]
updateCmd.parse(args, { from: "user" })
expect(updateCmd.opts().verbose).toBe(true)
})
})
describe("auth command", () => {
it("should parse auth command", () => {
const args = ["node", "cli", "auth"]
@@ -281,35 +343,35 @@ describe("CLI Commands", () => {
})
it("should parse --provider option", () => {
const authCmd = program.commands.find((c) => c.name() === "auth")!
const authCmd = getCommand("auth")
const args = ["--provider", "openai"]
authCmd.parse(args, { from: "user" })
expect(authCmd.opts().provider).toBe("openai")
})
it("should parse --apikey option", () => {
const authCmd = program.commands.find((c) => c.name() === "auth")!
const authCmd = getCommand("auth")
const args = ["--apikey", "sk-test-key"]
authCmd.parse(args, { from: "user" })
expect(authCmd.opts().apikey).toBe("sk-test-key")
})
it("should parse --modelid option", () => {
const authCmd = program.commands.find((c) => c.name() === "auth")!
const authCmd = getCommand("auth")
const args = ["--modelid", "gpt-4"]
authCmd.parse(args, { from: "user" })
expect(authCmd.opts().modelid).toBe("gpt-4")
})
it("should parse --baseurl option", () => {
const authCmd = program.commands.find((c) => c.name() === "auth")!
const authCmd = getCommand("auth")
const args = ["--baseurl", "https://api.example.com"]
authCmd.parse(args, { from: "user" })
expect(authCmd.opts().baseurl).toBe("https://api.example.com")
})
it("should parse short flags", () => {
const authCmd = program.commands.find((c) => c.name() === "auth")!
const authCmd = getCommand("auth")
const args = ["-p", "anthropic", "-k", "key123", "-m", "claude-sonnet-4-20250514"]
authCmd.parse(args, { from: "user" })
expect(authCmd.opts().provider).toBe("anthropic")
@@ -318,6 +380,30 @@ describe("CLI Commands", () => {
})
})
describe("mcp command", () => {
it("should parse mcp add stdio syntax", () => {
const args = ["node", "cli", "mcp", "add", "kanban", "--", "kanban", "mcp"]
program.parse(args)
})
it("should parse mcp add remote http syntax", () => {
const args = ["node", "cli", "mcp", "add", "linear", "https://mcp.linear.app/mcp", "--type", "http"]
program.parse(args)
})
it("should default mcp add type to stdio", () => {
const addCmd = getSubcommand("mcp", "add")
addCmd.parse(["kanban", "--", "kanban", "mcp"], { from: "user" })
expect(addCmd.opts().type).toBe("stdio")
})
it("should parse mcp add type option", () => {
const addCmd = getSubcommand("mcp", "add")
addCmd.parse(["linear", "https://mcp.linear.app/mcp", "--type", "http"], { from: "user" })
expect(addCmd.opts().type).toBe("http")
})
})
describe("default command (interactive mode)", () => {
it("should parse optional prompt argument", () => {
const args = ["node", "cli", "do something"]
@@ -368,6 +454,21 @@ describe("CLI Commands", () => {
program.parse(["node", "cli", "--auto-approve-all"])
expect(program.opts().autoApproveAll).toBe(true)
})
it("should parse --kanban flag", () => {
program.parse(["node", "cli", "--kanban"])
expect(program.opts().kanban).toBe(true)
})
it("should parse --update flag", () => {
program.parse(["node", "cli", "--update"])
expect(program.opts().update).toBe(true)
})
it("should parse --tui flag", () => {
program.parse(["node", "cli", "--tui"])
expect(program.opts().tui).toBe(true)
})
})
describe("command structure", () => {
@@ -377,11 +478,14 @@ describe("CLI Commands", () => {
expect(commandNames).toContain("history")
expect(commandNames).toContain("config")
expect(commandNames).toContain("auth")
expect(commandNames).toContain("mcp")
expect(commandNames).toContain("kanban")
expect(commandNames).toContain("update")
})
it("should have correct aliases", () => {
const taskCmd = program.commands.find((c) => c.name() === "task")!
const historyCmd = program.commands.find((c) => c.name() === "history")!
const taskCmd = getCommand("task")
const historyCmd = getCommand("history")
expect(taskCmd.aliases()).toContain("t")
expect(historyCmd.aliases()).toContain("h")
})
@@ -405,7 +509,6 @@ describe("getProviderModelIdKey", () => {
litellm: "LiteLlmModelId",
requesty: "RequestyModelId",
together: "TogetherModelId",
coreweave: "CoreweaveModelId",
fireworks: "FireworksModelId",
sapaicore: "SapAiCoreModelId",
groq: "GroqModelId",
+268 -9
View File
@@ -2,6 +2,7 @@
* Cline CLI - TypeScript implementation with React Ink
*/
import type { ChildProcess } from "node:child_process"
import { exit } from "node:process"
import type { ApiProvider } from "@shared/api"
import { Command } from "commander"
@@ -27,6 +28,7 @@ import { isOpenaiReasoningEffort, OPENAI_REASONING_EFFORT_OPTIONS, type OpenaiRe
import { version as CLI_VERSION } from "../package.json"
import { runAcpMode } from "./acp/index.js"
import { App } from "./components/App"
import { KanbanMigrationView } from "./components/KanbanMigrationView"
import { checkRawModeSupport } from "./context/StdinContext"
import { createCliHostBridgeProvider } from "./controllers"
import { CliCommentReviewController } from "./controllers/CliCommentReviewController"
@@ -34,6 +36,21 @@ import { CliWebviewProvider } from "./controllers/CliWebviewProvider"
import { isAuthConfigured } from "./utils/auth"
import { restoreConsole, suppressConsoleUnlessVerbose } from "./utils/console"
import { printInfo, printWarning } from "./utils/display"
import {
forwardSignalToKanbanProcess,
isKanbanCommandAvailable,
KANBAN_LAUNCH_COMMAND,
KANBAN_SHUTDOWN_TIMEOUT_MS,
type KanbanMigrationAction,
LEGACY_TUI_FLAG,
markKanbanMigrationAnnouncementShown,
resolveKanbanInstallCommand,
shouldLaunchKanbanByDefault,
shouldShowKanbanMigrationAnnouncementForCurrentUser,
spawnKanbanInstallProcess,
spawnKanbanProcess,
} from "./utils/kanban"
import { addMcpServerShortcut, type McpAddOptions } from "./utils/mcp"
import { selectOutputMode } from "./utils/mode-selection"
import { parseImagesFromInput, processImagePaths } from "./utils/parser"
import { CLINE_CLI_DIR, getCliBinaryPath } from "./utils/path"
@@ -56,6 +73,8 @@ suppressConsoleUnlessVerbose()
interface TaskOptions {
act?: boolean
plan?: boolean
kanban?: boolean
tui?: boolean
model?: string
verbose?: boolean
cwd?: string
@@ -248,6 +267,83 @@ function getPlainTextModeReason(options: TaskOptions): string {
return getModeSelection(options).reason
}
function runKanbanAlias(spawnOptions?: Parameters<typeof spawnKanbanProcess>[0]): void {
const launchKanban = () => {
const child = spawnKanbanProcess(spawnOptions)
activeKanbanProcess = child
child.on("error", (error) => {
clearActiveKanbanProcess()
const errorMessage = error instanceof Error ? ` ${error.message}` : ""
printWarning(`Failed to run '${KANBAN_LAUNCH_COMMAND}'.${errorMessage}`)
exit(1)
})
child.on("close", (code, signal) => {
clearActiveKanbanProcess()
exit(resolveProcessExitCode(code, signal))
})
}
if (isKanbanCommandAvailable()) {
launchKanban()
return
}
const installCommand = resolveKanbanInstallCommand()
if (!installCommand) {
printWarning(
`'${KANBAN_LAUNCH_COMMAND}' not found and no supported package manager was detected in PATH (npm, pnpm, bun). Install Kanban globally and try again.`,
)
exit(1)
}
const installProcess = spawnKanbanInstallProcess(installCommand)
installProcess.on("error", (error) => {
const errorMessage = error instanceof Error ? ` ${error.message}` : ""
printWarning(`Failed to run '${installCommand.displayCommand}'.${errorMessage}`)
exit(1)
})
installProcess.on("close", (code, signal) => {
const installExitCode = resolveProcessExitCode(code, signal)
if (installExitCode !== 0) {
printWarning(`Failed to install Kanban automatically. Please run '${installCommand.displayCommand}' manually.`)
exit(installExitCode)
}
launchKanban()
})
}
async function showKanbanMigrationView(): Promise<KanbanMigrationAction> {
let selectedAction: KanbanMigrationAction = "exit"
await runInkApp(
React.createElement(KanbanMigrationView, {
isRawModeSupported: checkRawModeSupport(),
onSelect: (action: KanbanMigrationAction) => {
selectedAction = action
},
}),
async () => {},
)
return selectedAction
}
async function addMcpServer(name: string, targetOrCommand: string[] = [], options: McpAddOptions): Promise<void> {
try {
const result = await addMcpServerShortcut(name, targetOrCommand, options)
const transportLabel = result.transportType === "streamableHttp" ? "http" : result.transportType
printInfo(`Added MCP server '${result.serverName}' (${transportLabel}) to ${result.settingsPath}`)
} catch (error) {
printWarning(error instanceof Error ? error.message : "Failed to add MCP server.")
exit(1)
}
}
/**
* Run a task in plain text mode (no Ink UI).
* Handles auth check, task execution, cleanup, and exit.
@@ -314,6 +410,62 @@ let activeContext: CliContext | null = null
let isShuttingDown = false
// Track if we're in plain text mode (no Ink UI) - set by runTask when piped stdin detected
let isPlainTextMode = false
let activeKanbanProcess: ChildProcess | null = null
let activeKanbanShutdownTimer: NodeJS.Timeout | null = null
function clearActiveKanbanProcess(): void {
activeKanbanProcess = null
if (activeKanbanShutdownTimer) {
clearTimeout(activeKanbanShutdownTimer)
activeKanbanShutdownTimer = null
}
}
function requestKanbanProcessShutdown(signal: NodeJS.Signals): void {
if (!activeKanbanProcess) {
return
}
forwardSignalToKanbanProcess({
child: activeKanbanProcess,
signal,
})
if (activeKanbanShutdownTimer) {
clearTimeout(activeKanbanShutdownTimer)
}
if (signal === "SIGKILL") {
activeKanbanShutdownTimer = null
return
}
activeKanbanShutdownTimer = setTimeout(() => {
if (!activeKanbanProcess) {
return
}
forwardSignalToKanbanProcess({
child: activeKanbanProcess,
signal: "SIGKILL",
})
}, KANBAN_SHUTDOWN_TIMEOUT_MS)
activeKanbanShutdownTimer.unref?.()
}
function resolveProcessExitCode(code: number | null, signal: NodeJS.Signals | null): number {
if (code !== null) {
return code
}
switch (signal) {
case "SIGINT":
return 130
case "SIGTERM":
return 143
default:
return 1
}
}
/**
* Wait for stdout to fully drain before exiting.
@@ -333,10 +485,19 @@ async function drainStdout(): Promise<void> {
export async function captureUnhandledException(reason: Error, context: string) {
try {
const errorService = ErrorService.get()
await errorService.captureException(reason, { context })
// dispose flushes any pending error captures to ensure they're sent before the process exits
return errorService.dispose()
// ErrorService may not be initialized yet (e.g., error occurred before initializeCli())
// so we guard with a try/get pattern rather than letting ErrorService.get() throw
let errorService: ErrorService | null = null
try {
errorService = ErrorService.get()
} catch {
// ErrorService not yet initialized; skip capture
}
if (errorService) {
await errorService.captureException(reason, { context })
// dispose flushes any pending error captures to ensure they're sent before the process exits
return errorService.dispose()
}
} catch {
// Ignore errors during shutdown to avoid an infinite loop
Logger.info("Error capturing unhandled exception. Proceeding with shutdown.")
@@ -360,6 +521,17 @@ function onUnhandledException(reason: unknown, context: string) {
function setupSignalHandlers() {
const shutdown = async (signal: string) => {
if (activeKanbanProcess) {
if (isShuttingDown) {
requestKanbanProcessShutdown("SIGKILL")
return
}
isShuttingDown = true
requestKanbanProcessShutdown(signal === "SIGTERM" ? "SIGTERM" : "SIGINT")
return
}
if (isShuttingDown) {
// Force exit on second signal
process.exit(1)
@@ -395,7 +567,11 @@ function setupSignalHandlers() {
} catch {
// StateManager may not be initialized yet
}
await ErrorService.get().dispose()
try {
await ErrorService.get().dispose()
} catch {
// ErrorService may not be initialized yet
}
await disposeTelemetryServices()
}
} catch {
@@ -553,6 +729,11 @@ async function runTask(prompt: string, options: TaskOptions & { images?: string[
// Task without prompt starts in interactive mode
telemetryService.captureHostEvent("task_command", prompt ? "task" : "interactive")
// Capture piped stdin telemetry now that HostProvider is initialized
if (options.stdinWasPiped) {
telemetryService.captureHostEvent("piped", "detached")
}
// Apply shared task options (mode, model, thinking, yolo)
applyTaskOptions(options)
await StateManager.get().flushPendingState()
@@ -825,6 +1006,18 @@ program
.option("--config <path>", "Path to Cline configuration directory")
.action(runAuth)
const mcpCommand = program.command("mcp").description("Manage MCP servers")
mcpCommand
.command("add")
.description("Add an MCP server shortcut to cline_mcp_settings.json")
.argument("<name>", "MCP server name")
.argument("[targetOrCommand...]", "For stdio: use -- <command> [args]. For http/sse: provide <url>.")
.option("--type <type>", "Transport type: stdio (default), http, or sse", "stdio")
.option("-c, --cwd <path>", "Working directory for config resolution")
.option("--config <path>", "Path to Cline configuration directory")
.action(addMcpServer)
program
.command("version")
.description("Show Cline CLI version number")
@@ -834,7 +1027,12 @@ program
.command("update")
.description("Check for updates and install if available")
.option("-v, --verbose", "Show verbose output")
.action(() => checkForUpdates(CLI_VERSION))
.action((options) => checkForUpdates(CLI_VERSION, { verbose: options.verbose, includeKanban: true }))
program
.command("kanban")
.description(`Run ${KANBAN_LAUNCH_COMMAND}`)
.action(() => runKanbanAlias())
// Dev command with subcommands
const devCommand = program.command("dev").description("Developer tools and utilities")
@@ -874,6 +1072,11 @@ async function resumeTask(taskId: string, options: TaskOptions & { initialPrompt
telemetryService.captureHostEvent("resume_task_command", options.initialPrompt ? "with_prompt" : "interactive")
// Capture piped stdin telemetry now that HostProvider is initialized
if (options.stdinWasPiped) {
telemetryService.captureHostEvent("piped", "detached")
}
// Apply shared task options (mode, model, thinking, yolo)
applyTaskOptions(options)
await StateManager.get().flushPendingState()
@@ -980,9 +1183,37 @@ program
.option("--auto-condense", "Enable AI-powered context compaction instead of mechanical truncation")
.option("--hooks-dir <path>", "Path to additional hooks directory for runtime hook injection")
.option("--acp", "Run in ACP (Agent Client Protocol) mode for editor integration")
.option("--update", "Check for updates and install if available")
.option("--kanban", `Run ${KANBAN_LAUNCH_COMMAND}`)
.option("--tui", "Open the legacy terminal UI instead of the kanban experience")
.option("-T, --taskId <id>", "Resume an existing task by ID")
.option("--continue", "Resume the most recent task from the current working directory")
.action(async (prompt, options) => {
if (options.kanban && options.tui) {
printWarning(`Use either --kanban or ${LEGACY_TUI_FLAG}, not both.`)
exit(1)
}
if (options.update) {
if (prompt || options.taskId || options.continue || options.kanban || options.tui || options.acp) {
printWarning("Use --update without a prompt or task flags.")
exit(1)
}
await checkForUpdates(CLI_VERSION, { verbose: options.verbose, includeKanban: true })
return
}
if (options.kanban) {
if (prompt) {
printWarning("Use --kanban without a prompt.")
exit(1)
}
runKanbanAlias({ cwd: options.cwd })
return
}
// Check for ACP mode first - this takes precedence over everything else
if (options.acp) {
await runAcpMode({
@@ -1003,6 +1234,34 @@ program
// stdinInput has content means stdin was piped with data
const stdinWasPiped = stdinInput !== null
if (
shouldLaunchKanbanByDefault({
prompt,
stdinWasPiped,
taskId: options.taskId,
continue: options.continue,
tui: options.tui,
})
) {
let migrationAction: "kanban" | "exit" = "kanban"
const ctx = await initializeCli({ ...options, enableAuth: true })
try {
if (await shouldShowKanbanMigrationAnnouncementForCurrentUser()) {
migrationAction = await showKanbanMigrationView()
await markKanbanMigrationAnnouncementShown()
}
} finally {
await disposeCliContext(ctx)
}
if (migrationAction === "exit") {
exit(0)
}
runKanbanAlias({ cwd: options.cwd })
return
}
if (options.taskId && options.continue) {
printWarning("Use either --taskId or --continue, not both.")
exit(1)
@@ -1042,8 +1301,6 @@ program
effectivePrompt = stdinInput
}
telemetryService.captureHostEvent("piped", "detached")
// Debug: show that we received piped input
if (options.verbose) {
process.stderr.write(`[debug] Received ${stdinInput.length} bytes from stdin\n`)
@@ -1070,4 +1327,6 @@ program
})
// Parse and run
program.parse()
if (process.env.VITEST !== "true") {
program.parse()
}
+10
View File
@@ -15,3 +15,13 @@ export function isMouseEscapeSequence(input: string): boolean {
// They contain [< followed by numbers, semicolons, and end with M or m
return input.includes("[<") && /\[<\d+;\d+;\d+[Mm]/.test(input)
}
/**
* Ink's key metadata can be inconsistent across platforms/test environments for Enter.
* In particular, some Windows CI/test runs surface Enter as raw "\r" input without
* setting key.return. Treat either representation as Enter so keyboard handlers remain
* stable in production and in tests across platforms.
*/
export function isEnterKey(input: string, key: { return?: boolean }): boolean {
return key.return === true || input === "\r" || input === "\n"
}
+265
View File
@@ -0,0 +1,265 @@
import { chmodSync, mkdtempSync, rmSync, writeFileSync } from "node:fs"
import { tmpdir } from "node:os"
import { join } from "node:path"
import { describe, expect, it, vi } from "vitest"
import {
buildKanbanInstallSpawnOptions,
buildKanbanSpawnOptions,
forwardSignalToKanbanProcess,
hasUsedLegacyCli,
isKanbanCommandAvailable,
resolveKanbanInstallCommand,
shouldDetachKanbanProcess,
shouldLaunchKanbanByDefault,
shouldShowKanbanMigrationAnnouncement,
} from "./kanban"
describe("shouldLaunchKanbanByDefault", () => {
it("launches kanban for a bare interactive run", () => {
expect(
shouldLaunchKanbanByDefault({
stdinWasPiped: false,
}),
).toBe(true)
})
it("does not launch kanban when a prompt is provided", () => {
expect(
shouldLaunchKanbanByDefault({
prompt: "fix the tests",
stdinWasPiped: false,
}),
).toBe(false)
})
it("does not launch kanban when stdin is piped", () => {
expect(
shouldLaunchKanbanByDefault({
stdinWasPiped: true,
}),
).toBe(false)
})
it("does not launch kanban when the legacy tui is requested", () => {
expect(
shouldLaunchKanbanByDefault({
stdinWasPiped: false,
tui: true,
}),
).toBe(false)
})
})
describe("hasUsedLegacyCli", () => {
it("treats task history as legacy usage", () => {
expect(
hasUsedLegacyCli({
taskHistoryCount: 1,
isNewUser: true,
welcomeViewCompleted: undefined,
hasConfiguredAuth: false,
}),
).toBe(true)
})
it("treats configured auth as legacy usage", () => {
expect(
hasUsedLegacyCli({
taskHistoryCount: 0,
isNewUser: true,
welcomeViewCompleted: undefined,
hasConfiguredAuth: true,
}),
).toBe(true)
})
it("skips the announcement for fresh installs", () => {
expect(
hasUsedLegacyCli({
taskHistoryCount: 0,
isNewUser: true,
welcomeViewCompleted: undefined,
hasConfiguredAuth: false,
}),
).toBe(false)
})
})
describe("kanban process launch", () => {
it("detaches the kanban process on unix-like platforms", () => {
expect(shouldDetachKanbanProcess("darwin")).toBe(true)
expect(shouldDetachKanbanProcess("linux")).toBe(true)
})
it("keeps the kanban process attached on windows", () => {
expect(shouldDetachKanbanProcess("win32")).toBe(false)
})
it("uses a detached process group by default on unix-like platforms", () => {
expect(buildKanbanSpawnOptions({}, "darwin")).toMatchObject({
stdio: "inherit",
detached: true,
})
})
it("does not detach the process on windows", () => {
expect(buildKanbanSpawnOptions({}, "win32")).toMatchObject({
stdio: "inherit",
detached: false,
})
})
it("enables shell mode on windows for command launches", () => {
expect(buildKanbanSpawnOptions({}, "win32")).toMatchObject({
shell: true,
})
})
it("does not set shell mode on unix-like platforms", () => {
expect(buildKanbanSpawnOptions({}, "darwin")).not.toHaveProperty("shell")
})
})
describe("kanban command availability", () => {
it("returns false when PATH is empty", () => {
expect(isKanbanCommandAvailable({ PATH: "" }, "darwin")).toBe(false)
})
it("detects the kanban command in PATH", () => {
const tempDirectory = mkdtempSync(join(tmpdir(), "kanban-cli-test-"))
const commandPath = join(tempDirectory, process.platform === "win32" ? "kanban.cmd" : "kanban")
writeFileSync(commandPath, process.platform === "win32" ? "@echo off\r\necho ok\r\n" : "#!/bin/sh\necho ok\n")
if (process.platform !== "win32") {
chmodSync(commandPath, 0o755)
}
try {
expect(isKanbanCommandAvailable({ PATH: tempDirectory }, process.platform)).toBe(true)
} finally {
rmSync(tempDirectory, { recursive: true, force: true })
}
})
})
describe("kanban install process launch", () => {
it("does not detach the install process on unix-like platforms", () => {
expect(buildKanbanInstallSpawnOptions({}, "darwin")).toMatchObject({
stdio: "inherit",
detached: false,
})
})
it("enables shell mode on windows for npm.cmd launches", () => {
expect(buildKanbanInstallSpawnOptions({}, "win32")).toMatchObject({
shell: true,
})
})
})
describe("kanban installer resolution", () => {
it("prefers npm when available", () => {
const tempDirectory = mkdtempSync(join(tmpdir(), "kanban-installer-test-"))
writeFileSync(join(tempDirectory, "npm"), "#!/bin/sh\necho npm\n")
writeFileSync(join(tempDirectory, "pnpm"), "#!/bin/sh\necho pnpm\n")
writeFileSync(join(tempDirectory, "bun"), "#!/bin/sh\necho bun\n")
chmodSync(join(tempDirectory, "npm"), 0o755)
chmodSync(join(tempDirectory, "pnpm"), 0o755)
chmodSync(join(tempDirectory, "bun"), 0o755)
try {
expect(resolveKanbanInstallCommand({ PATH: tempDirectory }, "darwin")?.packageManager).toBe("npm")
} finally {
rmSync(tempDirectory, { recursive: true, force: true })
}
})
it("falls back to pnpm when npm is unavailable", () => {
const tempDirectory = mkdtempSync(join(tmpdir(), "kanban-installer-test-"))
writeFileSync(join(tempDirectory, "pnpm"), "#!/bin/sh\necho pnpm\n")
chmodSync(join(tempDirectory, "pnpm"), 0o755)
try {
const installer = resolveKanbanInstallCommand({ PATH: tempDirectory }, "darwin")
expect(installer?.packageManager).toBe("pnpm")
expect(installer?.displayCommand).toBe("pnpm add -g kanban@latest")
} finally {
rmSync(tempDirectory, { recursive: true, force: true })
}
})
it("falls back to bun when npm and pnpm are unavailable", () => {
const tempDirectory = mkdtempSync(join(tmpdir(), "kanban-installer-test-"))
writeFileSync(join(tempDirectory, "bun"), "#!/bin/sh\necho bun\n")
chmodSync(join(tempDirectory, "bun"), 0o755)
try {
const installer = resolveKanbanInstallCommand({ PATH: tempDirectory }, "darwin")
expect(installer?.packageManager).toBe("bun")
expect(installer?.displayCommand).toBe("bun add -g kanban@latest")
} finally {
rmSync(tempDirectory, { recursive: true, force: true })
}
})
it("returns null when no supported package manager is available", () => {
expect(resolveKanbanInstallCommand({ PATH: "" }, "darwin")).toBeNull()
})
})
describe("forwardSignalToKanbanProcess", () => {
it("signals the detached kanban process group on unix-like platforms", () => {
const killProcess = vi.fn()
const child = {
pid: 4321,
kill: vi.fn(),
}
forwardSignalToKanbanProcess({
child,
signal: "SIGINT",
platform: "darwin",
killProcess,
})
expect(killProcess).toHaveBeenCalledWith(-4321, "SIGINT")
expect(child.kill).not.toHaveBeenCalled()
})
it("signals the child process directly on windows", () => {
const killProcess = vi.fn()
const child = {
pid: 4321,
kill: vi.fn(),
}
forwardSignalToKanbanProcess({
child,
signal: "SIGTERM",
platform: "win32",
killProcess,
})
expect(killProcess).not.toHaveBeenCalled()
expect(child.kill).toHaveBeenCalledWith("SIGTERM")
})
})
describe("shouldShowKanbanMigrationAnnouncement", () => {
it("shows the announcement once for legacy users", () => {
expect(
shouldShowKanbanMigrationAnnouncement({
announcementShown: false,
hasUsedLegacyCli: true,
}),
).toBe(true)
})
it("does not show the announcement twice", () => {
expect(
shouldShowKanbanMigrationAnnouncement({
announcementShown: true,
hasUsedLegacyCli: true,
}),
).toBe(false)
})
})
+246
View File
@@ -0,0 +1,246 @@
import { type ChildProcess, type SpawnOptions, spawn } from "node:child_process"
import { accessSync, constants as fsConstants } from "node:fs"
import { delimiter, extname, join } from "node:path"
import { StateManager } from "@/core/storage/StateManager"
import { checkAnyProviderConfigured } from "./auth"
export const KANBAN_LAUNCH_COMMAND = "kanban"
export const KANBAN_SHUTDOWN_TIMEOUT_MS = 10_000
export const LEGACY_TUI_FLAG = "--tui"
export type KanbanMigrationAction = "kanban" | "exit"
type KanbanInstaller = "npm" | "pnpm" | "bun"
interface KanbanInstallCommand {
packageManager: KanbanInstaller
command: string
args: readonly string[]
displayCommand: string
}
interface SignalableKanbanProcess {
pid?: number
kill: (signal?: NodeJS.Signals | number) => boolean
}
function getKanbanCommand(platform: NodeJS.Platform = process.platform): string {
return platform === "win32" ? "kanban.cmd" : "kanban"
}
function getPackageManagerCommand(packageManager: KanbanInstaller, platform: NodeJS.Platform = process.platform): string {
if (platform !== "win32") {
return packageManager
}
return packageManager === "bun" ? "bun" : `${packageManager}.cmd`
}
const KANBAN_INSTALL_COMMANDS: ReadonlyArray<Omit<KanbanInstallCommand, "displayCommand">> = [
{
packageManager: "npm",
command: "npm",
args: ["install", "-g", "kanban@latest"],
},
{
packageManager: "pnpm",
command: "pnpm",
args: ["add", "-g", "kanban@latest"],
},
{
packageManager: "bun",
command: "bun",
args: ["add", "-g", "kanban@latest"],
},
]
function toDisplayCommand(command: string, args: readonly string[]): string {
return `${command} ${args.join(" ")}`
}
export function shouldDetachKanbanProcess(platform: NodeJS.Platform = process.platform): boolean {
return platform !== "win32"
}
export function buildKanbanSpawnOptions(options: SpawnOptions = {}, platform: NodeJS.Platform = process.platform): SpawnOptions {
return {
stdio: "inherit",
detached: shouldDetachKanbanProcess(platform),
...(platform === "win32" ? { shell: true } : {}),
...options,
}
}
export function buildKanbanInstallSpawnOptions(
options: SpawnOptions = {},
platform: NodeJS.Platform = process.platform,
): SpawnOptions {
return {
stdio: "inherit",
detached: false,
...(platform === "win32" ? { shell: true } : {}),
...options,
}
}
export function spawnKanbanProcess(options: SpawnOptions = {}): ChildProcess {
return spawn(getKanbanCommand(), [], buildKanbanSpawnOptions(options))
}
export function spawnKanbanInstallProcess(installCommand: KanbanInstallCommand, options: SpawnOptions = {}): ChildProcess {
return spawn(
getPackageManagerCommand(installCommand.packageManager),
[...installCommand.args],
buildKanbanInstallSpawnOptions(options),
)
}
function getPathEntries(env: NodeJS.ProcessEnv): string[] {
const pathValue = env.PATH ?? env.Path ?? env.path
if (!pathValue) {
return []
}
return pathValue
.split(delimiter)
.map((entry) => entry.trim().replace(/^"(.*)"$/u, "$1"))
.filter((entry) => entry.length > 0)
}
function pathExists(candidatePath: string, platform: NodeJS.Platform): boolean {
try {
if (platform === "win32") {
accessSync(candidatePath, fsConstants.F_OK)
} else {
accessSync(candidatePath, fsConstants.X_OK)
}
return true
} catch {
return false
}
}
export function isCommandAvailable(
command: string,
env: NodeJS.ProcessEnv = process.env,
platform: NodeJS.Platform = process.platform,
): boolean {
const commandHasExtension = extname(command).length > 0
const pathExtensions =
platform === "win32" ? (env.PATHEXT ?? ".EXE;.CMD;.BAT;.COM").split(";").filter((ext) => ext.length > 0) : []
for (const pathEntry of getPathEntries(env)) {
const commandPath = join(pathEntry, command)
if (pathExists(commandPath, platform)) {
return true
}
if (!commandHasExtension && platform === "win32") {
for (const extension of pathExtensions) {
if (pathExists(`${commandPath}${extension}`, platform)) {
return true
}
}
}
}
return false
}
export function isKanbanCommandAvailable(
env: NodeJS.ProcessEnv = process.env,
platform: NodeJS.Platform = process.platform,
): boolean {
return isCommandAvailable(getKanbanCommand(platform), env, platform)
}
export function resolveKanbanInstallCommand(
env: NodeJS.ProcessEnv = process.env,
platform: NodeJS.Platform = process.platform,
): KanbanInstallCommand | null {
for (const installCommand of KANBAN_INSTALL_COMMANDS) {
if (isCommandAvailable(installCommand.command, env, platform)) {
return {
...installCommand,
displayCommand: toDisplayCommand(installCommand.command, installCommand.args),
}
}
}
return null
}
export function forwardSignalToKanbanProcess(options: {
child: SignalableKanbanProcess
signal: NodeJS.Signals
platform?: NodeJS.Platform
killProcess?: (pid: number, signal: NodeJS.Signals | number) => boolean
}): void {
if (options.child.pid == null) {
return
}
if (shouldDetachKanbanProcess(options.platform)) {
try {
;(options.killProcess ?? process.kill)(-options.child.pid, options.signal)
return
} catch (error) {
if (error && typeof error === "object" && "code" in error && error.code === "ESRCH") {
return
}
}
}
options.child.kill(options.signal)
}
export function shouldLaunchKanbanByDefault(options: {
prompt?: string
stdinWasPiped: boolean
taskId?: string
continue?: boolean
tui?: boolean
}): boolean {
return !options.prompt && !options.stdinWasPiped && !options.taskId && !options.continue && !options.tui
}
export function hasUsedLegacyCli(options: {
taskHistoryCount: number
isNewUser: boolean
welcomeViewCompleted: boolean | undefined
hasConfiguredAuth: boolean
}): boolean {
return (
options.taskHistoryCount > 0 ||
options.isNewUser === false ||
options.welcomeViewCompleted !== undefined ||
options.hasConfiguredAuth
)
}
export function shouldShowKanbanMigrationAnnouncement(options: {
announcementShown: boolean
hasUsedLegacyCli: boolean
}): boolean {
return !options.announcementShown && options.hasUsedLegacyCli
}
export async function shouldShowKanbanMigrationAnnouncementForCurrentUser(): Promise<boolean> {
const stateManager = StateManager.get()
const hasConfiguredAuth = await checkAnyProviderConfigured()
const hasUsedLegacy = hasUsedLegacyCli({
taskHistoryCount: stateManager.getGlobalStateKey("taskHistory")?.length ?? 0,
isNewUser: stateManager.getGlobalStateKey("isNewUser"),
welcomeViewCompleted: stateManager.getGlobalStateKey("welcomeViewCompleted"),
hasConfiguredAuth,
})
return shouldShowKanbanMigrationAnnouncement({
announcementShown: stateManager.getGlobalStateKey("cliKanbanMigrationAnnouncementShown"),
hasUsedLegacyCli: hasUsedLegacy,
})
}
export async function markKanbanMigrationAnnouncementShown(): Promise<void> {
const stateManager = StateManager.get()
stateManager.setGlobalState("cliKanbanMigrationAnnouncementShown", true)
await stateManager.flushPendingState()
}
+63
View File
@@ -0,0 +1,63 @@
import * as fs from "node:fs/promises"
import os from "node:os"
import path from "node:path"
import { afterEach, describe, expect, it } from "vitest"
import { addMcpServerShortcut } from "./mcp"
const tempDirs: string[] = []
async function createTempConfigDir(): Promise<string> {
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "cline-mcp-test-"))
tempDirs.push(dir)
return dir
}
type McpSettingsFile = {
mcpServers: Record<string, Record<string, unknown>>
}
async function readMcpSettings(configDir: string): Promise<McpSettingsFile> {
const settingsPath = path.join(configDir, "data", "settings", "cline_mcp_settings.json")
return JSON.parse(await fs.readFile(settingsPath, "utf-8")) as McpSettingsFile
}
afterEach(async () => {
for (const dir of tempDirs.splice(0, tempDirs.length)) {
await fs.rm(dir, { recursive: true, force: true })
}
})
describe("addMcpServerShortcut", () => {
it("writes stdio servers with type=stdio", async () => {
const configDir = await createTempConfigDir()
await addMcpServerShortcut("kanban", ["kanban", "mcp"], { config: configDir })
const settings = await readMcpSettings(configDir)
expect(settings.mcpServers.kanban).toEqual({
command: "kanban",
args: ["mcp"],
type: "stdio",
})
})
it("maps --type http to streamableHttp", async () => {
const configDir = await createTempConfigDir()
await addMcpServerShortcut("linear", ["https://mcp.linear.app/mcp"], { config: configDir, type: "http" })
const settings = await readMcpSettings(configDir)
expect(settings.mcpServers.linear).toEqual({
url: "https://mcp.linear.app/mcp",
type: "streamableHttp",
})
})
it("errors when URL is provided without --type http", async () => {
const configDir = await createTempConfigDir()
await expect(addMcpServerShortcut("linear", ["https://mcp.linear.app/mcp"], { config: configDir })).rejects.toThrow(
"Use --type http",
)
})
})
+159
View File
@@ -0,0 +1,159 @@
import * as fs from "node:fs/promises"
import path from "node:path"
import { getMcpSettingsFilePath } from "@/core/storage/disk"
import { ServerConfigSchema } from "@/services/mcp/schemas"
import { initializeCliContext } from "../vscode-context"
export interface McpAddOptions {
type?: string
config?: string
cwd?: string
}
export type McpAddTransportType = "stdio" | "streamableHttp" | "sse"
export interface AddMcpServerResult {
serverName: string
transportType: McpAddTransportType
settingsPath: string
}
function normalizeMcpTransportType(value?: string): McpAddTransportType {
const normalized = (value || "stdio").trim().toLowerCase()
switch (normalized) {
case "stdio":
return "stdio"
case "http":
case "streamable-http":
case "streamablehttp":
return "streamableHttp"
case "sse":
return "sse"
default:
throw new Error(`Invalid MCP transport type '${value}'. Valid values: stdio, http, sse.`)
}
}
function parseMcpSettings(content: string, settingsPath: string): Record<string, unknown> {
const trimmedContent = content.trim()
if (!trimmedContent) {
return { mcpServers: {} }
}
let parsed: unknown
try {
parsed = JSON.parse(content)
} catch {
throw new Error(`Invalid JSON in ${settingsPath}. Please fix the file and try again.`)
}
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
throw new Error(`Invalid MCP settings file at ${settingsPath}. Expected a JSON object.`)
}
const settings = parsed as Record<string, unknown>
if (settings.mcpServers === undefined) {
settings.mcpServers = {}
}
if (!settings.mcpServers || typeof settings.mcpServers !== "object" || Array.isArray(settings.mcpServers)) {
throw new Error(`Invalid MCP settings file at ${settingsPath}. Expected 'mcpServers' to be an object.`)
}
return settings
}
function createMcpServerConfig(targetOrCommand: string[], transportType: McpAddTransportType): Record<string, unknown> {
if (transportType === "stdio") {
if (targetOrCommand.length < 1) {
throw new Error("Missing stdio command. Example: cline mcp add kanban -- kanban mcp")
}
// Guard against common mistake:
// `cline mcp add <name> <url>` without `--type http`
if (targetOrCommand.length === 1) {
const [value] = targetOrCommand
try {
const parsedUrl = new URL(value)
if (parsedUrl.protocol === "http:" || parsedUrl.protocol === "https:") {
throw new Error(
`Looks like you provided a URL for '${value}'. Use --type http, for example: cline mcp add <name> ${value} --type http`,
)
}
} catch (error) {
if (error instanceof Error && error.message.startsWith("Looks like you provided a URL")) {
throw error
}
}
}
const [command, ...args] = targetOrCommand
const config: Record<string, unknown> = {
command,
type: "stdio",
}
if (args.length > 0) {
config.args = args
}
ServerConfigSchema.parse(config)
return config
}
if (targetOrCommand.length !== 1) {
throw new Error(
"HTTP/SSE MCP servers require exactly one URL. Example: cline mcp add linear https://mcp.linear.app/mcp --type http",
)
}
const config = {
url: targetOrCommand[0],
type: transportType,
}
ServerConfigSchema.parse(config)
return config
}
export async function addMcpServerShortcut(
name: string,
targetOrCommand: string[] = [],
options: McpAddOptions,
): Promise<AddMcpServerResult> {
const trimmedName = name.trim()
if (!trimmedName) {
throw new Error("Server name is required.")
}
const transportType = normalizeMcpTransportType(options.type)
const { DATA_DIR } = initializeCliContext({
clineDir: options.config,
workspaceDir: options.cwd || process.cwd(),
})
const settingsDirectoryPath = path.join(DATA_DIR, "settings")
await fs.mkdir(settingsDirectoryPath, { recursive: true })
const settingsPath = await getMcpSettingsFilePath(settingsDirectoryPath)
const content = await fs.readFile(settingsPath, "utf-8")
const settings = parseMcpSettings(content, settingsPath)
const mcpServers = settings.mcpServers as Record<string, unknown>
if (mcpServers[trimmedName]) {
throw new Error(`An MCP server named '${trimmedName}' already exists.`)
}
const serverConfig = createMcpServerConfig(targetOrCommand, transportType)
mcpServers[trimmedName] = serverConfig
await fs.writeFile(settingsPath, `${JSON.stringify(settings, null, 2)}\n`, "utf-8")
return {
serverName: trimmedName,
transportType,
settingsPath,
}
}
+54
View File
@@ -0,0 +1,54 @@
import type { SlashCommandInfo } from "@shared/proto/cline/slash"
import { describe, expect, it } from "vitest"
import { filterCommands, getStandaloneSlashCommandToExecute } from "./slash-commands"
const createCommand = (name: string): SlashCommandInfo => ({
name,
description: `${name} command`,
section: "default",
cliCompatible: true,
})
describe("filterCommands", () => {
it("prioritizes exact matches ahead of fuzzy matches", () => {
const commands = [createCommand("help"), createCommand("history"), createCommand("q")]
const result = filterCommands(commands, "q")
expect(result.map((command) => command.name)[0]).toBe("q")
})
it("prioritizes prefix matches ahead of fuzzy matches", () => {
const commands = [createCommand("history"), createCommand("help"), createCommand("exit")]
const result = filterCommands(commands, "hi")
expect(result.map((command) => command.name)[0]).toBe("history")
})
})
describe("getStandaloneSlashCommandToExecute", () => {
it("ignores standalone execution when slash menu is visible", () => {
expect(
getStandaloneSlashCommandToExecute({
prompt: "/q",
inSlashMode: true,
hasSlashMenu: true,
hasPendingAsk: false,
isSpinnerActive: false,
}),
).toBeNull()
})
it("returns standalone command when enter should execute it directly", () => {
expect(
getStandaloneSlashCommandToExecute({
prompt: "/exit",
inSlashMode: false,
hasSlashMenu: false,
hasPendingAsk: false,
isSpinnerActive: false,
}),
).toBe("exit")
})
})
+75 -2
View File
@@ -4,6 +4,7 @@
*/
import type { SlashCommandInfo } from "@shared/proto/cline/slash"
import { CLI_ONLY_COMMANDS } from "@shared/slashCommands"
import { fuzzyFilter } from "./fuzzy-search"
export interface SlashQueryInfo {
@@ -17,12 +18,29 @@ export interface VisibleWindow<T> {
startIndex: number
}
export interface StandaloneSlashCommandExecutionInput {
prompt: string
inSlashMode: boolean
hasSlashMenu: boolean
hasPendingAsk: boolean
isSpinnerActive: boolean
}
export function createCliOnlySlashCommands(): SlashCommandInfo[] {
return CLI_ONLY_COMMANDS.map((cmd) => ({
name: cmd.name,
description: cmd.description || "",
section: cmd.section || "default",
cliCompatible: true,
}))
}
/**
* Calculate visible window for a scrollable list menu.
* Centers the selected item in the visible window when possible.
* Returns the visible items and the start index for selection tracking.
*/
export function getVisibleWindow<T>(items: T[], selectedIndex: number, maxVisible: number = 5): VisibleWindow<T> {
export function getVisibleWindow<T>(items: T[], selectedIndex: number, maxVisible = 5): VisibleWindow<T> {
if (items.length <= maxVisible) {
return { items, startIndex: 0 }
}
@@ -91,6 +109,42 @@ export function extractSlashQuery(text: string, cursorPosition?: number): SlashQ
}
}
/**
* Detect a standalone slash command (for example "/q" or "/exit")
* that should be executed immediately when enter is pressed.
*/
export function getStandaloneSlashCommandName(text: string): string | null {
const match = text.trim().match(/^\/([a-zA-Z0-9_.-]+)$/)
return match?.[1] ?? null
}
/**
* Resolve whether pressing Enter should execute a standalone CLI slash command.
* This keeps ChatView's key handling deterministic and easy to test.
*/
export function getStandaloneSlashCommandToExecute({
prompt,
inSlashMode,
hasSlashMenu,
hasPendingAsk,
isSpinnerActive,
}: StandaloneSlashCommandExecutionInput): string | null {
const standaloneSlashCommand = getStandaloneSlashCommandName(prompt)
if (!standaloneSlashCommand) {
return null
}
if (hasPendingAsk || isSpinnerActive) {
return null
}
if (inSlashMode && hasSlashMenu) {
return null
}
return standaloneSlashCommand
}
/**
* Filter commands using fuzzy matching
*/
@@ -98,7 +152,26 @@ export function filterCommands(commands: SlashCommandInfo[], query: string): Sla
if (!query) {
return commands
}
return fuzzyFilter(commands, query, (cmd) => cmd.name)
const normalizedQuery = query.toLowerCase()
const exactMatches: SlashCommandInfo[] = []
const prefixMatches: SlashCommandInfo[] = []
const remaining: SlashCommandInfo[] = []
for (const command of commands) {
const normalizedName = command.name.toLowerCase()
if (normalizedName === normalizedQuery) {
exactMatches.push(command)
continue
}
if (normalizedName.startsWith(normalizedQuery)) {
prefixMatches.push(command)
continue
}
remaining.push(command)
}
return [...exactMatches, ...prefixMatches, ...fuzzyFilter(remaining, query, (cmd) => cmd.name)]
}
/**
+198 -70
View File
@@ -1,9 +1,10 @@
import { spawn } from "node:child_process"
import { type ChildProcess, spawn, spawnSync } from "node:child_process"
import { realpathSync } from "node:fs"
import { exit } from "node:process"
import { ClineEndpoint } from "@/config"
import { fetch } from "@/shared/net"
import { printInfo, printWarning } from "./display"
import { printInfo, printSuccess, printWarning } from "./display"
import { resolveKanbanInstallCommand, spawnKanbanInstallProcess } from "./kanban"
export enum PackageManager {
NPM = "npm",
@@ -19,6 +20,11 @@ interface InstallationInfo {
updateCommand?: string
}
interface CheckForUpdatesOptions {
verbose?: boolean
includeKanban?: boolean
}
/**
* Check if a version string is a nightly build.
*/
@@ -91,9 +97,12 @@ function getInstallationInfo(currentVersion: string): InstallationInfo {
* Uses the appropriate tag based on whether the current version is nightly.
*/
async function getLatestVersion(currentVersion: string): Promise<string | null> {
return getLatestPackageVersion("cline", getNpmTag(currentVersion))
}
async function getLatestPackageVersion(packageName: string, tag = "latest"): Promise<string | null> {
try {
const tag = getNpmTag(currentVersion)
const response = await fetch(`https://registry.npmjs.org/cline/${tag}`)
const response = await fetch(`https://registry.npmjs.org/${encodeURIComponent(packageName)}/${tag}`)
if (!response.ok) return null
const data = (await response.json()) as { version: string }
return data.version || null
@@ -102,6 +111,29 @@ async function getLatestVersion(currentVersion: string): Promise<string | null>
}
}
async function getLatestKanbanVersion(): Promise<string | null> {
return getLatestPackageVersion("kanban")
}
function getInstalledKanbanVersion(): string | null {
try {
const command = process.platform === "win32" ? "kanban.cmd" : "kanban"
const result = spawnSync(command, ["--version"], {
encoding: "utf8",
shell: process.platform === "win32",
})
if (result.status !== 0) {
return null
}
const output = `${result.stdout ?? ""}\n${result.stderr ?? ""}`.trim()
const versionMatch = output.match(/\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?/)
return versionMatch?.[0] ?? null
} catch {
return null
}
}
/**
* Auto-update check that runs on CLI startup.
* Checks for updates asynchronously (non-blocking), then spawns a detached
@@ -157,85 +189,181 @@ async function checkAndUpdate(currentVersion: string, updateCommand: string): Pr
}
}
async function waitForProcessExit(updateProcess: ChildProcess): Promise<number> {
return new Promise<number>((resolve, reject) => {
updateProcess.once("close", (code) => {
resolve(code ?? 1)
})
updateProcess.once("error", (error) => {
reject(error)
})
})
}
async function runClineUpdate(updateCommand: string): Promise<number> {
const updateProcess = spawn(updateCommand, {
stdio: "inherit",
shell: true,
env: process.env,
windowsHide: true,
})
return waitForProcessExit(updateProcess)
}
type KanbanInstallCommand = NonNullable<ReturnType<typeof resolveKanbanInstallCommand>>
async function runKanbanUpdate(installCommand: KanbanInstallCommand): Promise<number> {
const updateProcess = spawnKanbanInstallProcess(installCommand, {
env: process.env,
windowsHide: true,
})
return waitForProcessExit(updateProcess)
}
function formatUpdateSummaryTargets(targets: string[]): string {
if (targets.length === 0) {
return ""
}
if (targets.length === 1) {
return targets[0]
}
if (targets.length === 2) {
return `${targets[0]} and ${targets[1]}`
}
return `${targets.slice(0, -1).join(", ")}, and ${targets.at(-1)}`
}
/**
* Check for updates and install if available (manual command)
*/
export async function checkForUpdates(currentVersion: string, options?: { verbose?: boolean }) {
printInfo("Checking for updates...")
export async function checkForUpdates(currentVersion: string, options: CheckForUpdatesOptions = {}) {
const includeKanban = options.includeKanban ?? true
printInfo("Checking for updates to cline and kanban packages...")
const { updateCommand, packageManager } = getInstallationInfo(currentVersion)
try {
const latestVersion = await getLatestVersion(currentVersion)
if (!latestVersion) {
printWarning("Failed to check for updates: could not fetch latest version")
exit(1)
}
const latestClineVersion = await getLatestVersion(currentVersion)
const canCheckClineVersion = latestClineVersion !== null
if (options?.verbose) {
printInfo(`Current version: ${currentVersion}`)
printInfo(`Latest version: ${latestVersion}`)
printInfo(`Package manager: ${packageManager}`)
}
// Compare versions
if (latestVersion === currentVersion) {
printInfo(`You are already on the latest version (${currentVersion})`)
exit(0)
}
// Check if current is newer (dev version)
if (compareVersions(currentVersion, latestVersion) > 0) {
printInfo(`You are already on a newer version ${currentVersion} (latest: ${latestVersion})`)
exit(0)
}
printInfo(`New version available: ${latestVersion} (current: ${currentVersion})`)
if (!updateCommand) {
printInfo("Unable to determine update command for your installation.")
printInfo("Please update manually using your package manager.")
exit(0)
}
// Ask user to confirm update
const userConfirmed = new Promise<boolean>((resolve) => {
process.stdout.write("Do you want to update now? (y/N): ")
process.stdin.setEncoding("utf-8")
process.stdin.once("data", (dataBuff) => {
const input = dataBuff.toString().trim().toLowerCase()
resolve(input === "y" || input === "yes")
})
})
if (!(await userConfirmed)) {
exit(0)
}
printInfo(`Installing update via ${packageManager}...`)
const updateProcess = spawn(updateCommand, {
stdio: "inherit",
shell: true,
env: process.env,
windowsHide: true,
})
updateProcess.on("close", (code) => {
if (code === 0) {
printInfo(`Successfully updated to version ${latestVersion}`)
exit(0)
} else {
printWarning(`Update failed. Please try running: ${updateCommand}`)
exit(1)
if (canCheckClineVersion) {
printInfo(`Latest version: ${latestClineVersion}`)
}
})
}
updateProcess.on("error", (err) => {
printWarning(`Failed to run update: ${err.message}`)
printInfo(`Please try running manually: ${updateCommand}`)
if (!canCheckClineVersion) {
printWarning("Failed to check for Cline updates: could not fetch latest version")
}
const clineComparison = latestClineVersion ? compareVersions(currentVersion, latestClineVersion) : null
const clineUpdateAvailable = clineComparison !== null && clineComparison < 0
const clineIsUpToDate = clineComparison !== null && clineComparison === 0
const canUpdateCline = clineUpdateAvailable && Boolean(updateCommand)
if (clineUpdateAvailable && latestClineVersion) {
printInfo(`New version available: ${latestClineVersion} (current: ${currentVersion})`)
}
if (clineUpdateAvailable && !updateCommand) {
printInfo("Unable to determine Cline update command for your installation.")
printInfo("Please update Cline manually using your package manager.")
}
const kanbanInstallCommand = includeKanban ? resolveKanbanInstallCommand() : null
const kanbanInstallerAvailable = kanbanInstallCommand !== null
if (includeKanban && !kanbanInstallerAvailable && options.verbose) {
printWarning("Unable to determine Kanban update command (npm, pnpm, or bun not found in PATH).")
}
const latestKanbanVersion = kanbanInstallerAvailable ? await getLatestKanbanVersion() : null
const installedKanbanVersion = includeKanban ? getInstalledKanbanVersion() : null
const kanbanIsUpToDate =
latestKanbanVersion !== null &&
installedKanbanVersion !== null &&
compareVersions(installedKanbanVersion, latestKanbanVersion) >= 0
const shouldInstallKanban =
kanbanInstallerAvailable &&
latestKanbanVersion !== null &&
(installedKanbanVersion === null || compareVersions(installedKanbanVersion, latestKanbanVersion) < 0)
if (!canCheckClineVersion && !shouldInstallKanban) {
exit(1)
})
}
if (!canUpdateCline && !shouldInstallKanban) {
if (clineIsUpToDate && kanbanIsUpToDate && installedKanbanVersion) {
printInfo(`You are already on the latest version cline@${currentVersion} and kanban@${installedKanbanVersion}`)
} else if (clineIsUpToDate) {
printInfo(`You are already on the latest version cline@${currentVersion}`)
}
exit(0)
}
let hadFailure = false
const installedUpdates: string[] = []
if (canUpdateCline && updateCommand && latestClineVersion) {
printInfo(`Installing cline@${latestClineVersion}...`)
try {
const clineUpdateCode = await runClineUpdate(updateCommand)
if (clineUpdateCode === 0) {
installedUpdates.push(`cline@${latestClineVersion}`)
} else {
printWarning(`Cline update failed. Please try running: ${updateCommand}`)
hadFailure = true
}
} catch (error) {
const message = error instanceof Error ? error.message : String(error)
printWarning(`Failed to run Cline update: ${message}`)
printInfo(`Please try running manually: ${updateCommand}`)
hadFailure = true
}
}
if (shouldInstallKanban && kanbanInstallCommand && latestKanbanVersion) {
const kanbanTargetVersion = latestKanbanVersion ?? "latest"
printInfo(`Installing kanban@${kanbanTargetVersion}...`)
try {
const kanbanUpdateCode = await runKanbanUpdate(kanbanInstallCommand)
if (kanbanUpdateCode === 0) {
installedUpdates.push(`kanban@${kanbanTargetVersion}`)
} else {
printWarning(`Kanban update failed. Please try running: ${kanbanInstallCommand.displayCommand}`)
hadFailure = true
}
} catch (error) {
const message = error instanceof Error ? error.message : String(error)
printWarning(`Failed to run Kanban update: ${message}`)
if (kanbanInstallCommand) {
printInfo(`Please try running manually: ${kanbanInstallCommand.displayCommand}`)
}
hadFailure = true
}
}
if (!hadFailure) {
if (installedUpdates.length > 1) {
printSuccess(`Installed updates for ${formatUpdateSummaryTargets(installedUpdates)}`)
} else if (installedUpdates.length === 1) {
printSuccess(`Installed update for ${installedUpdates[0]}`)
} else {
printInfo("No updates were installed.")
}
}
if (hadFailure) {
exit(1)
}
if (canUpdateCline || shouldInstallKanban) {
exit(0)
}
exit(1)
} catch (error) {
const message = error instanceof Error ? error.message : String(error)
printWarning(`Error checking for updates: ${message}`)
@@ -259,7 +387,7 @@ function parseVersion(version: string): ParsedVersion {
return {
base: nightlyMatch[1].split(".").map(Number),
isNightly: true,
timestamp: parseInt(nightlyMatch[2], 10),
timestamp: Number.parseInt(nightlyMatch[2], 10),
}
}
return {
+1 -1
View File
@@ -69,7 +69,7 @@ cline auth
cline auth -p cline -k "YOUR_API_KEY" -m anthropic/claude-sonnet-4-6
```
See the [CLI Reference](/cline-cli/cli-reference#cline-auth) for all auth options.
See the [CLI Reference](/cli/cli-reference#cline-auth) for all auth options.
## Security Best Practices
+4 -29
View File
@@ -21,32 +21,7 @@ For example:
Pass this string as the `model` parameter in your [Chat Completions](/api/chat-completions) request.
## Popular Models
| Model ID | Provider | Context Window | Reasoning | Best For |
|----------|----------|---------------|-----------|----------|
| `anthropic/claude-sonnet-4-6` | Anthropic | 200K | Yes | General coding, analysis, complex tasks |
| `anthropic/claude-sonnet-4-5` | Anthropic | 200K | Yes | Balanced performance and cost |
| `openai/gpt-4o` | OpenAI | 128K | No | Multimodal tasks, fast responses |
| `google/gemini-2.5-pro` | Google | 1M | Yes | Very long context, document analysis |
| `deepseek/deepseek-chat` | DeepSeek | 64K | No | Cost-effective coding tasks |
| `x-ai/grok-3` | xAI | 128K | Yes | Reasoning-heavy tasks |
<Note>
Model availability and pricing change over time. Check [app.cline.bot](https://app.cline.bot) for the latest catalog.
</Note>
## Free Models
These models are available at no cost. They are a good starting point for experimentation and lightweight tasks:
| Model ID | Provider | Context Window |
|----------|----------|---------------|
| `minimax/minimax-m2.5` | MiniMax | 1M |
| `kwaipilot/kat-coder-pro` | Kwaipilot | 32K |
| `z-ai/glm-5` | Z-AI | 128K |
Free models have the same API interface as paid models. Just use their model ID:
Example:
```bash
curl -X POST https://api.cline.bot/api/v1/chat/completions \
@@ -79,7 +54,7 @@ Models with reasoning support include most Claude, Gemini 2.5, and Grok 3 models
| Multi-modal (text + images) | `openai/gpt-4o` or `anthropic/claude-sonnet-4-6` |
| Complex reasoning | Any model with reasoning support |
For a deeper comparison of model capabilities and pricing, see the [Model Selection Guide](/core-features/model-selection-guide).
For setup and account flow details, see the [Cline provider guide](/getting-started/cline-provider).
## Image Support
@@ -108,7 +83,7 @@ Not all models support images. Check the model's `supportsImages` capability bef
<Card title="Chat Completions" icon="message" href="/api/chat-completions">
Use these models in your API requests.
</Card>
<Card title="Model Selection Guide" icon="scale-balanced" href="/core-features/model-selection-guide">
In-depth comparison for choosing the right model.
<Card title="Cline provider" icon="scale-balanced" href="/getting-started/cline-provider">
Fastest setup path with built-in authentication and billing.
</Card>
</CardGroup>
+1 -1
View File
@@ -24,7 +24,7 @@ Your App → Cline API (api.cline.bot) → Anthropic / OpenAI / Google / etc
<Card title="Chat Completions" icon="message" href="/api/chat-completions">
Full endpoint reference with request schemas, streaming, and tool calling.
</Card>
<Card title="SDK Examples" icon="code" href="/api/sdk-examples">
<Card title="Code Examples" icon="code" href="/api/sdk-examples">
Ready-to-copy examples for Python, Node.js, curl, and the Cline CLI.
</Card>
</CardGroup>
-257
View File
@@ -1,257 +0,0 @@
---
title: "Cline API Reference"
sidebarTitle: "API Reference"
description: "Reference for the Cline Chat Completions API, an OpenAI-compatible endpoint for programmatic access."
---
The Cline API provides an OpenAI-compatible Chat Completions endpoint. You can use it from the Cline extension, the CLI, or any HTTP client that speaks the OpenAI format.
## Base URL
```
https://api.cline.bot/api/v1
```
## Authentication
All requests require a Bearer token in the `Authorization` header. You can use either:
- **API key** created at [app.cline.bot](https://app.cline.bot) (Settings > API Keys)
- **Account auth token** (used automatically by the Cline extension and CLI when you sign in)
```bash
Authorization: Bearer YOUR_API_KEY
```
### Getting an API Key
<Steps>
<Step title="Go to app.cline.bot">
Open [app.cline.bot](https://app.cline.bot) and sign in.
</Step>
<Step title="Open Settings > API Keys">
Navigate to **Settings**, then **API Keys**.
</Step>
<Step title="Create and copy your key">
Create a new key and copy it. Store it securely. You will not be able to see it again.
</Step>
</Steps>
## Chat Completions
Create a chat completion with streaming support. This endpoint follows the [OpenAI Chat Completions](https://platform.openai.com/docs/api-reference/chat/create) format.
### Request
```
POST /chat/completions
```
**Headers:**
| Header | Required | Description |
|--------|----------|-------------|
| `Authorization` | Yes | `Bearer YOUR_API_KEY` |
| `Content-Type` | Yes | `application/json` |
| `HTTP-Referer` | No | Your application URL |
| `X-Title` | No | Your application name |
**Body parameters:**
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `model` | string | Yes | Model ID in `provider/model` format (e.g., `anthropic/claude-sonnet-4-6`) |
| `messages` | array | Yes | Array of message objects with `role` and `content` |
| `stream` | boolean | No | Enable SSE streaming (default: `true`) |
| `tools` | array | No | Tool definitions in OpenAI function calling format |
| `temperature` | number | No | Sampling temperature |
### Example Request
```bash
curl -X POST https://api.cline.bot/api/v1/chat/completions \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "anthropic/claude-sonnet-4-6",
"messages": [
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "Explain what a context window is in 2 sentences."}
],
"stream": true
}'
```
### Response (Streaming)
When `stream: true`, the response is a series of [Server-Sent Events](https://developer.mozilla.org/en-US/docs/Web/API/Server-Sent_Events). Each event contains a JSON chunk:
```json
data: {"id":"gen-abc123","choices":[{"delta":{"content":"A context"},"index":0}],"model":"anthropic/claude-sonnet-4-6"}
data: {"id":"gen-abc123","choices":[{"delta":{"content":" window is"},"index":0}],"model":"anthropic/claude-sonnet-4-6"}
data: [DONE]
```
The final chunk includes a `usage` object with token counts and cost:
```json
{
"usage": {
"prompt_tokens": 25,
"completion_tokens": 42,
"prompt_tokens_details": {
"cached_tokens": 0
},
"cost": 0.000315
}
}
```
### Response (Non-Streaming)
When `stream: false`, the response is a single JSON object:
```json
{
"id": "gen-abc123",
"model": "anthropic/claude-sonnet-4-6",
"choices": [
{
"message": {
"role": "assistant",
"content": "A context window is the maximum amount of text..."
},
"finish_reason": "stop",
"index": 0
}
],
"usage": {
"prompt_tokens": 25,
"completion_tokens": 42
}
}
```
## Models
Model IDs use the `provider/model-name` format, the same format used by [OpenRouter](https://openrouter.ai). Some examples:
| Model ID | Description |
|----------|-------------|
| `anthropic/claude-sonnet-4-6` | Claude Sonnet 4.6 |
| `anthropic/claude-sonnet-4-5` | Claude Sonnet 4.5 |
| `google/gemini-2.5-pro` | Gemini 2.5 Pro |
| `openai/gpt-4o` | GPT-4o |
### Free Models
The following models are available at no cost:
| Model ID | Provider |
|----------|----------|
| `minimax/minimax-m2.5` | MiniMax |
| `kwaipilot/kat-coder-pro` | Kwaipilot |
| `z-ai/glm-5` | Z-AI |
<Note>
Model availability and pricing may change. Check [app.cline.bot](https://app.cline.bot) for the latest list.
</Note>
## Error Handling
Errors follow the OpenAI error format:
```json
{
"error": {
"code": 401,
"message": "Invalid API key",
"metadata": {}
}
}
```
Common error codes:
| Code | Meaning |
|------|---------|
| `401` | Invalid or missing API key |
| `402` | Insufficient credits |
| `429` | Rate limit exceeded |
| `500` | Server error |
| `error` (finish_reason) | Mid-stream error from the upstream model provider |
## Using with Cline
The easiest way to use the Cline API is through the Cline extension or CLI, which handle authentication and streaming for you.
### VS Code / JetBrains
Select **Cline** as your provider in the model picker dropdown. Sign in with your Cline account and your API key is managed automatically.
### Cline CLI
Configure the CLI with your API key in one command:
```bash
cline auth -p cline -k "YOUR_API_KEY" -m anthropic/claude-sonnet-4-6
```
Then run tasks normally:
```bash
cline "Write a one-line hello world in Python."
```
See the [CLI Reference](/cline-cli/cli-reference) for all available commands and options.
## Using with Other Tools
Because the Cline API is OpenAI-compatible, you can use it with any library or tool that supports custom OpenAI endpoints.
### Python (OpenAI SDK)
```python
from openai import OpenAI
client = OpenAI(
base_url="https://api.cline.bot/api/v1",
api_key="YOUR_API_KEY",
)
response = client.chat.completions.create(
model="anthropic/claude-sonnet-4-6",
messages=[{"role": "user", "content": "Hello!"}],
)
print(response.choices[0].message.content)
```
### Node.js (OpenAI SDK)
```typescript
import OpenAI from "openai"
const client = new OpenAI({
baseURL: "https://api.cline.bot/api/v1",
apiKey: "YOUR_API_KEY",
})
const response = await client.chat.completions.create({
model: "anthropic/claude-sonnet-4-6",
messages: [{ role: "user", content: "Hello!" }],
})
console.log(response.choices[0].message.content)
```
## Related
<CardGroup cols={2}>
<Card title="CLI Reference" icon="terminal" href="/cline-cli/cli-reference">
Full command reference for the Cline CLI, including auth setup.
</Card>
<Card title="Enterprise API" icon="building" href="/enterprise-solutions/api-reference">
Admin endpoints for user management, organizations, billing, and API keys.
</Card>
</CardGroup>
+5 -5
View File
@@ -1,6 +1,6 @@
---
title: "SDK Examples"
sidebarTitle: "SDK Examples"
title: "Code Examples"
sidebarTitle: "Code Examples"
description: "Use the Cline API from Python, Node.js, curl, the Cline CLI, and the VS Code extension."
---
@@ -214,7 +214,7 @@ console.log(data.choices[0].message.content)
## Cline CLI
The [Cline CLI](/cline-cli/cli-reference) is the fastest way to use the Cline API from your terminal. It handles authentication, streaming, and tool execution for you.
The [Cline CLI](/cli/cli-reference) is the fastest way to use the Cline API from your terminal. It handles authentication, streaming, and tool execution for you.
### Setup
@@ -242,7 +242,7 @@ cline -m google/gemini-2.5-pro "Analyze this codebase."
cline -y "Run tests and fix failures."
```
See the [CLI Reference](/cline-cli/cli-reference) for all commands and options.
See the [CLI Reference](/cli/cli-reference) for all commands and options.
## VS Code / JetBrains
@@ -269,7 +269,7 @@ For setup instructions, see [Installing Cline](/getting-started/installing-cline
<Card title="Models" icon="brain" href="/api/models">
Browse available models.
</Card>
<Card title="CLI Reference" icon="terminal" href="/cline-cli/cli-reference">
<Card title="CLI Reference" icon="terminal" href="/cli/cli-reference">
Complete Cline CLI command reference.
</Card>
</CardGroup>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.9 MiB

After

Width:  |  Height:  |  Size: 8.9 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 MiB

After

Width:  |  Height:  |  Size: 6.1 MiB

@@ -198,11 +198,11 @@ If Cline can't access files or run commands:
## Learn More
<Columns cols={2}>
<Card title="CLI Overview" icon="terminal" href="/cline-cli/overview">
<Card title="CLI Overview" icon="terminal" href="/usage/cli-overview">
Learn about Cline CLI's core capabilities and use cases.
</Card>
<Card title="Headless Mode" icon="robot" href="/cline-cli/three-core-flows">
<Card title="Headless Mode" icon="robot" href="/usage/cli-overview#headless-mode">
Run Cline autonomously in scripts, CI/CD pipelines, and automated workflows.
</Card>
+57
View File
@@ -0,0 +1,57 @@
---
title: "Agent Teams"
sidebarTitle: "Agent Teams"
description: "Coordinate multiple agents working together on complex tasks from the CLI."
---
<Warning>
This feature currently only applies to Cline SDK, CLI, and Kanban. This feature is not applicable on VSCode and JetBrains Extension for now.
</Warning>
Agent teams let you break complex work across multiple agents that coordinate through a shared task board. One agent acts as the coordinator, delegating subtasks to specialist agents.
## Starting a Team
```bash
cline --team-name auth-sprint "Plan and implement user authentication with tests"
```
The `--team-name` flag enables team mode. The coordinator agent gets additional tools for spawning teammates and delegating tasks.
## Resuming Team Work
Team state persists across sessions. Resume where you left off:
```bash
cline --team-name auth-sprint "Continue with incomplete tasks"
```
## Interactive Mode
In interactive mode, use the `/team` slash command:
```
/team Plan and implement a REST API with tests
```
## Team State
Team state is stored at `~/.cline/data/teams/[team-name]/` and includes:
- Task board with current tasks and status
- Inter-agent mailbox
- Mission log with activity history
## Disabling Teams
Teams are enabled by default. Disable them with:
```bash
cline --no-teams "your prompt"
```
## Sub-Agents
For simpler delegation within a single session (no persistent state), use [sub-agents](/features/subagents). Sub-agents run in parallel for read-only research and return focused reports to the main agent.
See the [SDK Multi-Agent Teams guide](/sdk/guides/multi-agent-teams) for the programmatic API.
+305
View File
@@ -0,0 +1,305 @@
---
title: "CLI Reference"
description: "Complete command reference for Cline CLI including all commands, flags, and configuration options."
---
```bash
cline --help # Show all commands
cline <command> --help # Show help for a specific command
```
## Synopsis
```bash
cline [options] [command] [prompt]
```
## Help Menu (Source of Truth)
```text
Usage: cline [options] [command] [prompt]
Cline CLI - AI coding assistant in your terminal
Arguments:
prompt Your prompt. Default to start in act mode with auto-approve enabled.
Options:
-V, --version Output the version number
-p, --plan Run in plan mode
--json Output messages as JSON instead of styled text
--auto-approve <boolean> Set tool auto-approval for all tools (default: true)
-t, --timeout <seconds> Optional timeout in seconds (default: 0 for no timeout)
-m, --model <model-id> Model to use for the session with the selected provider
-v, --verbose Show verbose output
-c, --cwd <path> Working directory
--config <path> Configuration directory (default: ~/.cline/data/settings)
--data-dir <path> Use isolated local state at this directory path (default: ~/.cline)
--thinking <level> Set reasoning effort level between none|low|medium|high|xhigh (default: medium)
--retries <count> Maximum consecutive mistakes (retries) before halting
--hooks-dir <path> Directory path to additional hooks for runtime hook injection (default: ~/.cline/hooks)
--acp Run in Agent Client Protocol (ACP) mode for editor integration
-i, --tui Open the terminal user interface (TUI) for interactive sessions
--id <session-id> Resume an existing session by ID
-k, --key <api-key> API key override for this run
-P, --provider <id> Provider id (default: cline)
-s, --system <system-prompt> Override the default system prompt
-z, --zen Start a session that runs in the background hub
-h, --help display help for command
Commands:
auth [options] [provider] Authenticate a provider and configure what model is used
config [options] Show current configuration
connect [options] [adapter] Connect to an editor or IDE adapter
mcp Manage MCP servers
dev Developer tools and utilities
doctor Diagnose and fix configuration issues
history|h [options] List session history or manage saved sessions
hook Handle a hook payload from stdin
plugin Manage Cline Plugins
schedule Manage scheduled tasks
hub Manage the local hub daemon
update [options] Check for updates and install if available
version Show Cline CLI version number
kanban Launch the kanban app and exit
```
## Global Options
| Option | Description |
|--------|-------------|
| `-V, --version` | Output the version number |
| `-p, --plan` | Run in plan mode |
| `--json` | Output messages as JSON instead of styled text |
| `--auto-approve <boolean>` | Set tool auto-approval for all tools (default: `true`) |
| `-t, --timeout <seconds>` | Optional timeout in seconds (default: `0` for no timeout) |
| `-m, --model <model-id>` | Model to use for the session with the selected provider |
| `-v, --verbose` | Show verbose output |
| `-c, --cwd <path>` | Working directory |
| `--config <path>` | Configuration directory (default: `~/.cline/data/settings`) |
| `--data-dir <path>` | Use isolated local state at this directory path (default: `~/.cline`) |
| `--thinking <level>` | Set reasoning effort: `none\|low\|medium\|high\|xhigh` (default `medium`) |
| `--retries <count>` | Maximum consecutive mistakes (retries) before halting |
| `--hooks-dir <path>` | Directory path to additional hooks for runtime hook injection (default: `~/.cline/hooks`) |
| `--acp` | Run in Agent Client Protocol (ACP) mode for editor integration |
| `-i, --tui` | Open the terminal user interface (TUI) for interactive sessions |
| `--id <session-id>` | Resume an existing session by ID |
| `-k, --key <api-key>` | API key override for this run |
| `-P, --provider <id>` | Provider id (default: `cline`) |
| `-s, --system <system-prompt>` | Override the default system prompt |
| `-z, --zen` | Start a session that runs in the background hub |
| `-h, --help` | Display help for command |
## Commands
### `cline` (default)
Start a task or enter interactive mode.
```bash
cline
cline "your prompt here"
cline "Run tests and fix failures"
echo "prompt" | cline
```
### `auth [options] [provider]`
Configure authentication with an AI provider.
```bash
cline auth
```
### `config [options]`
Show current configuration.
```bash
cline config
```
### `connect [options] [adapter]`
Connect to messaging platforms. See [Connectors](/cli/connectors).
```bash
cline connect
cline connect [adapter]
```
### `mcp`
Manage MCP servers. See [MCP](/mcp/mcp-overview).
```bash
cline mcp
```
### `dev`
Developer tools and utilities.
```bash
cline dev
```
### `doctor`
Diagnose and fix configuration issues.
```bash
cline doctor
```
### `history|h [options]`
List session history or manage saved sessions.
```bash
cline history
cline h
```
### `hook`
Handle a hook payload from stdin.
```bash
cat payload.json | cline hook
```
### `plugin`
Manage Cline plugins. Install plugins from npm, git repositories, or local paths. See [Plugins](/customization/plugins) for full details and the plugin manifest format.
```bash
cline plugin install <source> # Install a plugin
cline plugin i <source> # Shorthand alias
```
| Option | Description |
|--------|-------------|
| `--npm` | Treat source as an npm package |
| `--git` | Treat source as a git repository |
| `--force` | Replace an existing install for the same source |
| `--json` | Output result as JSON |
| `--cwd <path>` | Install to `<path>/.cline/plugins` instead of the global directory |
Try it with the [TypeScript Navigation Plugin](https://github.com/cline/typescript-lsp-plugin):
```bash
cline plugin install https://github.com/cline/typescript-lsp-plugin.git
```
### `schedule`
Manage scheduled agents. See [Scheduling](/cli/scheduling).
```bash
cline schedule
```
### `hub`
Manage the local hub daemon.
```bash
cline hub
```
### `update [options]`
Check for updates and install if available.
```bash
cline update
```
### `version`
Show Cline CLI version number.
```bash
cline version
cline -V
```
### `kanban`
Launch the kanban app and exit.
```bash
cline kanban
```
## Environment Variables
| Variable | Description |
|----------|-------------|
| `CLINE_DATA_DIR` | Custom configuration directory (replaces `~/.cline/data/`) |
| `CLINE_HUB_ADDRESS` | Override hub address (default: `127.0.0.1:25463`) |
| `CLINE_SESSION_BACKEND_MODE` | Force backend mode (`local`, `hub`, `remote`, `auto`) |
| `CLINE_SANDBOX_DATA_DIR` | Sandbox session storage directory |
| `CLINE_SANDBOX` | Enable sandbox mode |
| `CLINE_HOOKS_DIR` | Additional hooks directory |
| `CLINE_BUILD_ENV` | Set to `development` for debug features |
| `CLINE_DEBUG_PORT_BASE` | Base port for Node.js inspector |
| `CLINE_COMMAND_PERMISSIONS` | JSON policy restricting shell commands (see below) |
### CLINE_COMMAND_PERMISSIONS
Restrict which shell commands the agent can execute:
```bash
export CLINE_COMMAND_PERMISSIONS='{"allow": ["npm *", "git *"], "deny": ["rm -rf *", "sudo *"]}'
```
| Field | Type | Description |
|-------|------|-------------|
| `allow` | `string[]` | Glob patterns for allowed commands. If set, only matching commands are permitted. |
| `deny` | `string[]` | Glob patterns for denied commands. Deny rules always take precedence. |
| `allowRedirects` | `boolean` | Whether to allow shell redirects (`>`, `>>`, `<`). Default: `false`. |
## JSON Output Format
When using `--json`, each message is a JSON object on its own line:
```json
{"type": "say", "text": "I'll create the file now.", "ts": 1760501486669, "say": "text"}
```
| Field | Type | Description |
|-------|------|-------------|
| `type` | `"ask"` or `"say"` | Message category |
| `text` | `string` | Message content |
| `ts` | `number` | Unix timestamp in milliseconds |
| `say` | `string` | Subtype when `type` is `"say"` |
| `ask` | `string` | Subtype when `type` is `"ask"` |
| `reasoning` | `string` | Model reasoning (if available) |
| `partial` | `boolean` | `true` while streaming |
## Configuration Files
```
~/.cline/
data/
settings/
providers.json # API keys and provider config
rules/ # Global rules
skills/ # Global skills
teams/ # Team state
sessions/ # Session database (SQLite)
logs/
hub-daemon.log # Hub logs
plugins/ # Global plugins
_installed/ # Managed by `cline plugin install`
.cline/ # Project root
rules/ # Project rules
skills/ # Project skills
hooks/ # Lifecycle hooks
plugins/ # Project plugins
mcp.json # MCP server config
agents.yaml # Agent definitions
```
+156
View File
@@ -0,0 +1,156 @@
---
title: "Connectors"
sidebarTitle: "Connectors"
description: "Connect the CLI to Telegram, Slack, Discord, Google Chat, WhatsApp, etc."
---
<Warning>
This feature currently only applies to Cline CLI.
</Warning>
Connectors let you chat with your agent from messaging platforms. Each incoming message creates or continues an agent session, and the agent's response is sent back to the conversation.
## Setup Wizard
Run `cline connect` to open an interactive wizard that guides you through platform selection, credential entry, security configuration, and advanced options (provider, model, system prompt, agent mode).
```bash
cline connect
```
## Supported Platforms
| Platform | Direct Command | Required Credentials |
|----------|---------------|---------------------|
| Telegram | `cline connect telegram` | Bot username, bot token |
| Slack | `cline connect slack` | Bot token, signing secret, base URL |
| Discord | `cline connect discord` | Application ID, bot token, public key, base URL |
| Google Chat | `cline connect gchat` | Service account credentials JSON, base URL |
| WhatsApp | `cline connect whatsapp` | Phone number ID, access token, app secret, verify token, base URL |
| Linear | `cline connect linear` | API key, webhook signing secret, base URL |
## Telegram
<Steps>
<Step title="Create a Telegram bot">
Open Telegram and start a chat with [@BotFather](https://t.me/BotFather). Send `/newbot` and follow the prompts:
1. Enter a display name (e.g., "Cline")
2. Enter a username ending in `bot` (e.g., `cline_myname_bot`). Must be unique across Telegram.
3. BotFather responds with your bot token (looks like `7123456789:AAH...`)
</Step>
<Step title="Start the connector">
```bash
cline connect telegram -m <BOT-USERNAME> -k <BOT-TOKEN>
```
</Step>
<Step title="Chat with your bot">
Open Telegram, search for your bot's username, and send a message. The agent processes it and replies in the chat.
</Step>
</Steps>
### Security
By default, anyone who finds your bot can message it and it will execute tasks on your machine. Lock it down with the `--hook-command` flag.
<Steps>
<Step title="Get your Telegram user ID">
Message [@userinfobot](https://t.me/userinfobot) on Telegram. It replies with your user ID immediately.
</Step>
<Step title="Start with access control">
Replace `12345` with your actual Telegram user ID:
```bash
cline connect telegram -m <BOT-USERNAME> -k <BOT-TOKEN> \
--hook-command 'jq -r ".payload.actor.participantKey" | grep -q "telegram:id:12345" && echo "{\"action\":\"allow\"}" || echo "{\"action\":\"deny\",\"message\":\"unauthorized\"}"'
```
</Step>
</Steps>
The `--hook-command` receives each incoming message with sender info via stdin. Your script returns `{"action": "allow"}` or `{"action": "deny", "message": "reason"}`. Without `--hook-command`, everything is auto-approved.
## Slack
Requires a bot token, signing secret, and public base URL.
```bash
cline connect slack --token <BOT-TOKEN> --signing-secret <SECRET> --base-url <URL>
```
Each Slack thread maps to an agent session, so the agent maintains conversation context within a thread.
## Discord
Requires an application ID, bot token, public key, and public base URL.
```bash
cline connect discord --app-id <ID> --token <TOKEN> --public-key <KEY> --base-url <URL>
```
## Google Chat
Requires a service account credentials JSON file and public base URL.
```bash
cline connect gchat --credentials <JSON> --base-url <URL>
```
## WhatsApp
Requires a phone number ID, access token, app secret, webhook verify token, and public base URL.
```bash
cline connect whatsapp --phone-id <ID> --token <TOKEN> --app-secret <SECRET> --base-url <URL>
```
## Linear
Requires an API key, webhook signing secret, and public base URL.
```bash
cline connect linear --api-key <KEY> --signing-secret <SECRET> --base-url <URL>
```
## Managing Connectors
```bash
# Stop all connectors
cline connect --stop
# Stop a specific connector
cline connect telegram --stop
```
## Hook Command Protocol
The `--hook-command` pattern works across all connectors. The script receives a JSON payload via stdin:
```json
{
"payload": {
"actor": {
"participantKey": "telegram:id:12345",
"displayName": "User Name"
},
"message": "The incoming message text"
}
}
```
Return `{"action": "allow"}` or `{"action": "deny", "message": "reason"}`.
## Running Multiple Connectors
Multiple connectors can run simultaneously. They all share the same hub:
```bash
# Terminal 1
cline connect telegram -m my_bot -k $TELEGRAM_TOKEN
# Terminal 2
cline connect slack --token $SLACK_TOKEN --signing-secret $SECRET --base-url $URL
```
Connectors require the hub. Start it with `cline hub start` if it doesn't auto-start.
@@ -7,7 +7,7 @@ Automate GitHub issue analysis with AI. Mention `@cline` in any issue comment to
<Note>
**New to Cline CLI?** This sample assumes you understand Cline CLI basics and have completed the [Installation Guide](https://docs.cline.bot/cline-cli/installation). If you're new to Cline CLI, we recommend starting with the [GitHub RCA sample](./github-issue-rca) first, as it's simpler and will help you understand the fundamentals before setting up GitHub Actions.
**New to Cline CLI?** This sample assumes you understand Cline CLI basics and have completed the [Installation Guide](https://docs.cline.bot/getting-started/installing-cline). If you're new to Cline CLI, we recommend starting with the [GitHub RCA sample](./github-issue-rca) first, as it's simpler and will help you understand the fundamentals before setting up GitHub Actions.
</Note>
## The Workflow
@@ -32,7 +32,7 @@ Let's configure your repository.
Before you begin, you'll need:
- **Cline CLI knowledge** - Completed the [Installation Guide](https://docs.cline.bot/cline-cli/installation) and understand basic usage
- **Cline CLI knowledge** - Completed the [Installation Guide](https://docs.cline.bot/getting-started/installing-cline) and understand basic usage
- **GitHub repository** - With admin access to configure Actions and secrets
- **GitHub Actions familiarity** - Basic understanding of workflows and CI/CD
- **API provider account** - OpenRouter, Anthropic, or similar with API key
@@ -95,7 +95,7 @@ jobs:
- name: Install Cline CLI
if: steps.detect.outputs.hit == 'true'
run: npm install -g cline
run: npm install -g @cline/cli
- name: Configure Cline Authentication
if: steps.detect.outputs.hit == 'true'
@@ -120,7 +120,6 @@ jobs:
env:
ISSUE_URL: ${{ steps.detect.outputs.issue_url }}
COMMENT: ${{ steps.detect.outputs.comment_body }}
CLINE_ADDRESS: ${{ env.CLINE_ADDRESS }}
run: |
set -euo pipefail
@@ -231,10 +230,9 @@ nano git-scripts/analyze-issue.sh # or use vim, code, etc.
# Analyze a GitHub issue using Cline CLI
if [ -z "$1" ]; then
echo "Usage: $0 <github-issue-url> [prompt] [address]"
echo "Usage: $0 <github-issue-url> [prompt]"
echo "Example: $0 https://github.com/owner/repo/issues/123"
echo "Example: $0 https://github.com/owner/repo/issues/123 'What is the root cause of this issue?'"
echo "Example: $0 https://github.com/owner/repo/issues/123 'What is the root cause of this issue?'"
exit 1
fi
@@ -243,9 +241,8 @@ ISSUE_URL="$1"
PROMPT="${2:-What is the root cause of this issue?}"
# Ask Cline for its analysis, showing only the summary
cline -y "$PROMPT: $ISSUE_URL" --mode act -F json | \
sed -n '/^{/,$p' | \
jq -r 'select(.say == "completion_result") | .text' | \
cline --auto-approve true --json "$PROMPT: $ISSUE_URL" | \
jq -r 'select(.type == "agent_event" and .event.type == "done") | .event.text' | \
sed 's/\\n/\n/g'
```
@@ -283,7 +280,7 @@ GitHub Actions will:
1. Detect the `@cline` mention
2. Start a Cline CLI instance
3. Download the analysis script
4. Analyze the issue using act mode with yolo (fully autonomous)
4. Analyze the issue using Act mode with auto-approval enabled
5. Post Cline's analysis as a new comment
**Note**: The workflow only triggers on issue comments, not pull request
@@ -296,7 +293,7 @@ The workflow (`cline-responder.yml`):
1. **Triggers** on issue comments (created or edited)
2. **Detects** `@cline` mentions (case-insensitive)
3. **Installs** Cline CLI globally using npm
4. **Configures** authentication using `cline config set open-router-api-key=...`
4. **Configures** authentication using `cline auth --provider openrouter --apikey ...`
6. **Downloads** the reusable `analyze-issue.sh` script from the
`github-issue-rca` sample
7. **Runs** analysis in Cline CLI
@@ -6,7 +6,7 @@ description: "Automated GitHub issue analysis using Cline CLI to identify root c
Automated GitHub issue analysis using Cline CLI. This script uses Cline's autonomous AI capabilities to fetch, analyze, and identify root causes of GitHub issues, outputting clean, parseable results that can be easily integrated into your development workflows.
<Note>
**New to Cline CLI?** This sample assumes you have already completed the [Installation Guide](https://docs.cline.bot/cline-cli/installation) and authenticated with `cline auth`. If you haven't set up Cline CLI yet, please start there first.
**New to Cline CLI?** This sample assumes you have already completed the [Installation Guide](https://docs.cline.bot/getting-started/installing-cline) and authenticated with `cline auth`. If you haven't set up Cline CLI yet, please start there first.
</Note>
<Frame>
@@ -17,7 +17,7 @@ Automated GitHub issue analysis using Cline CLI. This script uses Cline's autono
This sample assumes you have already:
- **Cline CLI** installed and authenticated ([Installation Guide](https://docs.cline.bot/cline-cli/installation))
- **Cline CLI** installed and authenticated ([Installation Guide](https://docs.cline.bot/getting-started/installing-cline))
- **At least one AI model provider** configured (e.g., OpenRouter, Anthropic, OpenAI)
- **Basic familiarity** with Cline CLI commands
@@ -80,24 +80,18 @@ curl -O https://raw.githubusercontent.com/cline/cline/main/src/samples/cli/githu
# Analyze a GitHub issue using Cline CLI
if [ -z "$1" ]; then
echo "Usage: $0 <github-issue-url> [prompt] [address]"
echo "Usage: $0 <github-issue-url> [prompt]"
echo "Example: $0 https://github.com/owner/repo/issues/123"
echo "Example: $0 https://github.com/owner/repo/issues/123 'What is the root cause of this issue?'"
echo "Example: $0 https://github.com/owner/repo/issues/123 'What is the root cause of this issue?' 127.0.0.1:46529"
exit 1
fi
# Gather the args
ISSUE_URL="$1"
PROMPT="${2:-What is the root cause of this issue?}"
if [ -n "$3" ]; then
ADDRESS="--address $3"
fi
# Ask Cline for its analysis, showing only the summary
cline -y "$PROMPT: $ISSUE_URL" --mode act $ADDRESS -F json | \
sed -n '/^{/,$p' | \
jq -r 'select(.say == "completion_result") | .text' | \
cline --auto-approve true --json "$PROMPT: $ISSUE_URL" | \
jq -r 'select(.type == "agent_event" and .event.type == "done") | .event.text' | \
sed 's/\\n/\n/g'
```
@@ -133,23 +127,6 @@ Ask specific questions about the issue:
./analyze-issue.sh https://github.com/owner/repo/issues/456 "What is the security impact?"
```
### Using Specific Cline Instance
Target a particular Cline instance by address:
```bash
./analyze-issue.sh https://github.com/owner/repo/issues/123 \
"What is the root cause of this issue?" \
127.0.0.1:46529
```
<Warning>
This is useful when:
- Running multiple Cline instances
- Using a remote Cline server
- Testing with specific configurations
</Warning>
<Note>
The script will automatically handle everything: fetching the issue, analyzing it with Cline, and displaying the results. The analysis typically takes 30-60 seconds depending on the issue complexity.
</Note>
@@ -164,10 +141,9 @@ The script validates input and provides usage instructions:
```bash
if [ -z "$1" ]; then
echo "Usage: $0 <github-issue-url> [prompt] [address]"
echo "Usage: $0 <github-issue-url> [prompt]"
echo "Example: $0 https://github.com/owner/repo/issues/123"
echo "Example: $0 https://github.com/owner/repo/issues/123 'What is the root cause?'"
echo "Example: $0 https://github.com/owner/repo/issues/123 'Analyze security impact' 127.0.0.1:46529"
exit 1
fi
```
@@ -176,7 +152,6 @@ fi
- Validates required GitHub issue URL
- Shows clear usage examples
- Supports optional custom prompt
- Supports optional Cline instance address
### Argument Parsing
@@ -186,15 +161,12 @@ The script extracts and sets up the arguments:
# Gather the args
ISSUE_URL="$1"
PROMPT="${2:-What is the root cause of this issue?}"
if [ -n "$3" ]; then
ADDRESS="--address $3"
fi
```
**Explanation:**
- `ISSUE_URL="$1"` - First argument is always the issue URL
- `PROMPT="${2:-...}"` - Second argument is optional, defaults to root cause analysis
- `ADDRESS` - Third argument is optional, only set if provided
- The SDK CLI runs the task directly, so no address flag is required.
### The Core Analysis Pipeline
@@ -202,39 +174,26 @@ This is where the magic happens:
```bash
# Ask Cline for its analysis, showing only the summary
cline -y "$PROMPT: $ISSUE_URL" --mode act $ADDRESS -F json | \
sed -n '/^{/,$p' | \
jq -r 'select(.say == "completion_result") | .text' | \
cline --auto-approve true --json "$PROMPT: $ISSUE_URL" | \
jq -r 'select(.type == "agent_event" and .event.type == "done") | .event.text' | \
sed 's/\\n/\n/g'
```
<Accordion title="Pipeline Breakdown: Understanding Each Component">
**1. `cline -y "$PROMPT: $ISSUE_URL"`**
- `-y` enables yolo mode (no user interaction)
**1. `cline --auto-approve true --json "$PROMPT: $ISSUE_URL"`**
- `cline` is the Cline CLI binary
- Act mode is the default for prompt runs
- `--auto-approve true` allows tool use without interactive prompts
- `--json` emits newline-delimited JSON for parsing
- Constructs prompt with issue URL
**2. `--mode act`**
- Enables act mode for active investigation
- Allows Cline to use tools (read files, run commands, etc.)
**3. `$ADDRESS`**
- Optional address flag for specific instance
- Expands to `--address <ip:port>` if set
**4. `-F json`**
- Outputs in JSON format for parsing
**5. `sed -n '/^{/,$p'`**
- Extracts JSON from output
- Skips any non-JSON prefix lines
**6. `jq -r 'select(.say == "completion_result") | .text'`**
- Filters for completion result messages
- Extracts the text field
**2. `jq -r 'select(.type == "agent_event" and .event.type == "done") | .event.text'`**
- Filters for the final agent `done` event
- Extracts the final text field
- `-r` outputs raw strings (no JSON quotes)
**7. `sed 's/\\n/\n/g'`**
**3. `sed 's/\\n/\n/g'`**
- Converts escaped newlines to actual newlines
- Makes output readable
@@ -376,6 +335,6 @@ This pattern can be adapted for many other automation scenarios, from pull reque
## Related Resources
- [CLI Installation Guide](https://docs.cline.bot/cline-cli/installation)
- [CLI Reference Documentation](https://docs.cline.bot/cline-cli/cli-reference)
- [Headless Mode](https://docs.cline.bot/cline-cli/three-core-flows)
- [CLI Installation Guide](https://docs.cline.bot/getting-started/installing-cline)
- [CLI Reference Documentation](https://docs.cline.bot/cli/cli-reference)
- [Headless Mode](https://docs.cline.bot/usage/cli-overview#headless-mode)
@@ -69,7 +69,7 @@ jobs:
cache: "npm"
- name: Install Cline CLI
run: npm install -g cline
run: npm install -g @cline/cli
- name: Configure Cline Authentication
# Replace 'anthropic' with your provider of choice (openai, openrouter, etc.)
@@ -110,7 +110,7 @@ jobs:
]
}
run: |
cline --yolo 'You are a GitHub PR reviewer for this repository. Your goal is to give the PR author helpful feedback and give maintainers the context they need to review efficiently.
cline --auto-approve true 'You are a GitHub PR reviewer for this repository. Your goal is to give the PR author helpful feedback and give maintainers the context they need to review efficiently.
PR: #'"${PR_NUMBER}"'
@@ -175,11 +175,11 @@ cline auth --provider anthropic --apikey "..."
```
The `auth` command configures Cline in the CI environment without interactive prompts. You can switch providers (e.g., `openai`, `openrouter`) by changing the flags.
### Autonomous Mode (`--yolo`)
### Autonomous Mode (`--auto-approve true`)
```bash
cline --yolo '...'
cline --auto-approve true '...'
```
The `--yolo` flag tells Cline to run autonomously, executing commands without waiting for user approval. This is essential for CI/CD workflows.
The `--auto-approve true` flag tells Cline to run autonomously, executing approved tools without waiting for interactive confirmation. Prompt runs start in Act mode by default, so CI/CD workflows can perform the requested work immediately.
### Command Permissions
We explicitly restrict what commands Cline can run using `CLINE_COMMAND_PERMISSIONS`. This ensures Cline can only use `gh` and `git` commands relevant to reviewing, preventing any accidental or malicious system modifications.
@@ -43,20 +43,20 @@ Use different models for different phases of work. Route simple tasks to cheap m
ISSUE_CONTENT=$(gh issue view $(gh issue list -L 1 | awk '{print $1}'))
# Phase 1: Quick summary with cheap model
SUMMARY=$(echo "$ISSUE_CONTENT" | cline -y --config ~/.cline-haiku \
SUMMARY=$(echo "$ISSUE_CONTENT" | cline --auto-approve true --config ~/.cline-haiku \
"summarize this issue in 2-3 sentences")
# Phase 2: Detailed plan with expensive model + thinking
PLAN=$(echo "$SUMMARY" | cline -y --thinking --config ~/.cline-opus \
PLAN=$(echo "$SUMMARY" | cline --auto-approve true --thinking high --config ~/.cline-opus \
"create detailed implementation plan with edge cases")
# Phase 3: Execute with mid-tier model
echo "$PLAN" | cline -y --config ~/.cline-sonnet \
echo "$PLAN" | cline --auto-approve true --config ~/.cline-sonnet \
"implement the plan from above"
```
<Note>
Each cline invocation needs to complete before passing output to the next phase. Use shell variables to store intermediate results rather than piping cline commands directly.
Each `cline` invocation needs to complete before passing output to the next phase. Use shell variables to store intermediate results rather than piping `cline` commands directly.
</Note>
**Cost impact:**
@@ -102,19 +102,19 @@ Get multiple AI perspectives on the same change, then synthesize their feedback.
DIFF=$(git show)
# Review 1: Gemini's perspective
echo "$DIFF" | cline -y --config ~/.cline-gemini \
echo "$DIFF" | cline --auto-approve true --config ~/.cline-gemini \
"review this diff and write your analysis to gemini-review.md"
# Review 2: Codex's perspective
echo "$DIFF" | cline -y --config ~/.cline-codex \
echo "$DIFF" | cline --auto-approve true --config ~/.cline-codex \
"review this diff and write your analysis to codex-review.md"
# Review 3: Opus's perspective
echo "$DIFF" | cline -y --config ~/.cline-opus \
echo "$DIFF" | cline --auto-approve true --config ~/.cline-opus \
"review this diff and write your analysis to opus-review.md"
# Synthesize all reviews into a consensus
cat gemini-review.md codex-review.md opus-review.md | cline -y \
cat gemini-review.md codex-review.md opus-review.md | cline --auto-approve true \
"summarize these 3 reviews and identify: 1) issues all models agree on, 2) issues only one model caught, 3) your final recommendation"
```
@@ -130,19 +130,19 @@ Run reviews in parallel for faster feedback:
```bash
# Run all reviews simultaneously
git show | cline -y --config ~/.cline-gemini "review and save to gemini-review.md" &
git show | cline -y --config ~/.cline-codex "review and save to codex-review.md" &
git show | cline -y --config ~/.cline-opus "review and save to opus-review.md" &
git show | cline --auto-approve true --config ~/.cline-gemini "review and save to gemini-review.md" &
git show | cline --auto-approve true --config ~/.cline-codex "review and save to codex-review.md" &
git show | cline --auto-approve true --config ~/.cline-opus "review and save to opus-review.md" &
# Wait for all to complete
wait
# Synthesize
cat *-review.md | cline -y "create consensus review"
cat *-review.md | cline --auto-approve true "create consensus review"
```
<Note>
Parallel execution requires managing multiple Cline instances. See [Multi-instance workflows](/cline-cli/three-core-flows#3-multi-instance-run-parallel-agents) for details.
Parallel execution requires managing multiple Cline instances. See [Multi-instance workflows](/usage/cli-overview#automation-patterns) for details.
</Note>
## Extended Thinking for Complex Tasks
@@ -151,14 +151,14 @@ Use the `--thinking` flag when Cline needs to analyze multiple approaches:
```bash
# Without thinking: Fast but may miss nuances
cline -y "refactor this codebase"
cline --auto-approve true "refactor this codebase"
# With thinking: Slower but more thorough
cline -y --thinking \
cline --auto-approve true --thinking high \
"refactor this codebase - consider: performance, maintainability, backward compatibility"
```
The `--thinking` flag allocates 1024 tokens for internal reasoning before Cline responds. Best for:
The `--thinking <level>` flag sets reasoning effort. Use `--thinking high` or `--thinking xhigh` when you want the model to spend more effort on complex tradeoffs. Best for:
- Architectural decisions
- Security analysis
- Complex refactoring
@@ -178,12 +178,12 @@ The `--thinking` flag allocates 1024 tokens for internal reasoning before Cline
```bash
# Haiku: Quick summary and issue identification
gh pr view $PR | cline -y --config ~/.cline-haiku \
gh pr view $PR | cline --auto-approve true --config ~/.cline-haiku \
"list all issues to fix, output as JSON"
# Opus with thinking: Deep analysis only if issues found
if [ -s issues.json ]; then
cline -y --thinking --config ~/.cline-opus \
cline --auto-approve true --thinking high --config ~/.cline-opus \
"analyze these issues and recommend fixes"
fi
```
@@ -192,31 +192,31 @@ fi
```bash
# Different models have different security perspectives
git diff main | cline -y --config ~/.cline-gemini "security review" > gemini-sec.md &
git diff main | cline -y --config ~/.cline-opus "security review" > opus-sec.md &
git diff main | cline -y --config ~/.cline-codex "security review" > codex-sec.md &
git diff main | cline --auto-approve true --config ~/.cline-gemini "security review" > gemini-sec.md &
git diff main | cline --auto-approve true --config ~/.cline-opus "security review" > opus-sec.md &
git diff main | cline --auto-approve true --config ~/.cline-codex "security review" > codex-sec.md &
wait
# High-priority: Issues all 3 models found
cat *-sec.md | cline -y "find security issues all 3 reviews mentioned"
cat *-sec.md | cline --auto-approve true "find security issues all 3 reviews mentioned"
```
## Related Documentation
<Columns cols={2}>
<Card title="CLI Reference" icon="terminal" href="/cline-cli/cli-reference">
<Card title="CLI Reference" icon="terminal" href="/cli/cli-reference">
Complete documentation for --config and --thinking flags
</Card>
<Card title="Headless Mode" icon="robot" href="/cline-cli/three-core-flows">
<Card title="Headless Mode" icon="robot" href="/usage/cli-overview#headless-mode">
Run Cline autonomously in scripts, CI/CD pipelines, and automated workflows.
</Card>
<Card title="Model Selection Guide" icon="brain" href="/core-features/model-selection-guide">
Compare models and choose the right one for your needs
<Card title="Cline provider" icon="brain" href="/getting-started/cline-provider">
Fastest built-in model access setup and account workflow
</Card>
<Card title="CI/CD Integration" icon="github" href="/cline-cli/samples/github-integration">
<Card title="CI/CD Integration" icon="github" href="/cli/samples/github-integration">
Automate GitHub workflows with Cline CLI
</Card>
</Columns>
+110
View File
@@ -0,0 +1,110 @@
---
title: "Scheduling"
sidebarTitle: "Scheduling"
description: "Run agents on cron schedules for recurring automations like daily summaries and code reviews."
---
<Warning>
This feature currently only applies to Cline SDK, CLI, and Kanban. This feature is not applicable on VSCode and JetBrains Extension for now.
</Warning>
The CLI supports running agents on cron schedules through the hub. Scheduled agents persist across process restarts and run independently of any terminal session.
## Schedule Wizard
Run `cline schedule` to open an interactive menu for creating and managing schedules, browsing execution history, and viewing performance statistics.
```bash
cline schedule
```
The wizard provides:
| Action | Description |
|--------|-------------|
| Create new schedule | Set up a recurring task with cron timing and prompt |
| List schedules | View all schedules with status and next run time |
| Upcoming runs | Preview the next 10 scheduled executions |
| Active executions | Show currently running tasks |
| Trigger now | Immediately run a selected schedule |
| Pause / Resume | Suspend or restart a schedule |
| Execution history | View past runs with status, duration, tokens, and cost |
| Statistics | Success rate, average duration, last failure |
| Delete | Remove a schedule |
## Creating Schedules with Flags
```bash
cline schedule create "PR summary" \
--cron "0 9 * * MON-FRI" \
--prompt "List all open PRs and their review status" \
--workspace /path/to/repo \
--model anthropic/claude-sonnet-4-6
```
## Managing Schedules
```bash
cline schedule list
cline schedule trigger <schedule-id>
cline schedule pause <schedule-id>
cline schedule resume <schedule-id>
cline schedule delete <schedule-id>
cline schedule executions <schedule-id>
```
## Cron Expression Reference
| Expression | Schedule |
|-----------|----------|
| `*/5 * * * *` | Every 5 minutes |
| `*/15 * * * *` | Every 15 minutes |
| `0 * * * *` | Every hour |
| `0 */6 * * *` | Every 6 hours |
| `0 0 * * *` | Daily at midnight |
| `0 9 * * *` | Daily at 9am |
| `0 9 * * 1-5` | Every weekday at 9am |
| `0 9 * * 1` | Every Monday at 9am |
| `0 0 1 * *` | First of every month |
## Examples
### Daily Standup Summary
```bash
cline schedule create "Standup prep" \
--cron "0 8 * * MON-FRI" \
--prompt "Summarize: (1) PRs merged yesterday, (2) PRs currently in review, (3) open issues assigned to team members." \
--workspace /path/to/repo
```
### Weekly Dependency Check
```bash
cline schedule create "Dependency check" \
--cron "0 10 * * MON" \
--prompt "Check for outdated npm dependencies. For any with security vulnerabilities, create a branch with the update and open a PR." \
--workspace /path/to/project
```
### Codebase Health Report
```bash
cline schedule create "Code health" \
--cron "0 6 * * MON" \
--prompt "Analyze the codebase for: (1) files with no test coverage, (2) TODO/FIXME comments older than 30 days, (3) functions longer than 100 lines." \
--workspace /path/to/project
```
## Routing Results
Combine schedules with [connectors](/cli/connectors) to send results to messaging platforms:
```bash
cline connect telegram -m my_bot -k $BOT_TOKEN
cline schedule create "Morning briefing" \
--cron "0 8 * * *" \
--prompt "Summarize overnight activity in the repo"
```
Scheduling requires the hub. It starts automatically when you create a schedule.
-428
View File
@@ -1,428 +0,0 @@
---
title: "CLI Reference"
description: "Complete command reference for Cline CLI including all commands, flags, and configuration options"
---
This page documents all available commands, flags, and configuration options for Cline CLI. For quick help in your terminal, use:
```bash
cline --help # Show all commands
cline task --help # Show task command options
cline auth --help # Show auth command options
man cline # View the full manual page (if installed)
```
## Synopsis
```bash
cline [prompt] [options]
cline <command> [options] [arguments]
```
## Global Options
These options work with any command:
| Option | Description |
|--------|-------------|
| `--config <path>` | Use a custom configuration directory instead of `~/.cline/data/` |
| `-c, --cwd <path>` | Set the working directory for the task |
| `-v, --verbose` | Show detailed output including model reasoning |
| `--help` | Show help for the command |
## Modes of Operation
Cline CLI automatically detects the best output mode based on how you invoke it:
| Mode | When Activated | Description |
|------|----------------|-------------|
| **Interactive** | `cline` with no args, TTY connected | Rich terminal UI with real-time streaming, keyboard shortcuts, and visual feedback. |
| **Task** | `cline "prompt"` with TTY connected | Interactive UI starts immediately with your task. |
| **Plain Text** | stdin piped, stdout redirected, or `--yolo`/`--json` flags | Clean text output without UI, suitable for scripting and CI/CD. |
## Agent Behavior
Cline operates in two primary modes that control how it approaches tasks:
| Mode | Description |
|------|-------------|
| **Act Mode** (default) | Cline actively uses tools to accomplish tasks. It can read files, write code, execute commands, use a headless browser, and more. |
| **Plan Mode** | Cline gathers information and creates a detailed plan before implementation. It explores the codebase, asks clarifying questions, and presents a strategy for your approval before switching to Act Mode. |
Use `-a, --act` or `-p, --plan` flags to explicitly set the mode.
## Commands
### cline (default)
Run Cline without a subcommand to start a task or enter interactive mode.
```bash
# Interactive mode (no arguments)
cline
# Start a task directly
cline "your prompt here"
# Resume the latest task for the current directory
cline --continue
```
**Options:**
| Option | Description |
|--------|-------------|
| `-a, --act` | Start in Act mode (default). Cline executes actions directly. |
| `-p, --plan` | Start in Plan mode. Cline analyzes and creates a strategy before acting. |
| `-y, --yolo` | YOLO mode: auto-approve all actions, use plain text output, exit when complete. Ideal for CI/CD. |
| `-m, --model <id>` | Use a specific model (e.g., `claude-sonnet-4-5-20250929`, `gpt-4o`). |
| `-i, --images <paths...>` | Include image files with the prompt. |
| `--thinking` | Enable extended thinking with a 1024 token budget. |
| `--json` | Output messages as JSON (one object per line). Forces plain text mode. |
| `--timeout <seconds>` | Maximum execution time before the task is stopped. |
| `--continue` | Resume the most recent task from the current working directory. |
**Mode Behavior:**
| Invocation | Output Mode | Why |
|------------|-------------|-----|
| `cline` | Interactive UI | No arguments, TTY connected |
| `cline "prompt"` | Interactive UI | TTY connected |
| `cline -y "prompt"` | Plain text | YOLO flag forces plain text |
| `cline --json "prompt"` | JSON | JSON flag forces plain text |
| `cat file \| cline "prompt"` | Plain text | stdin is piped |
| `cline "prompt" > out.txt` | Plain text | stdout is redirected |
---
### cline task (alias: t)
Run a task with a prompt. This is equivalent to `cline "prompt"`.
```bash
cline task "Create a REST API endpoint"
cline t "Fix the bug in utils.js"
```
**Options:** Same as the default command above.
---
### cline auth
Configure authentication with an AI provider.
```bash
# Interactive wizard
cline auth
# Quick setup with flags
cline auth -p anthropic -k sk-ant-api-xxxxx -m claude-sonnet-4-5-20250929
```
**Options:**
| Option | Description |
|--------|-------------|
| `-p, --provider <id>` | Provider ID. See [Supported Providers](#supported-providers) below. |
| `-k, --apikey <key>` | API key for the provider. |
| `-m, --modelid <id>` | Model ID to use (e.g., `claude-sonnet-4-5-20250929`, `gpt-4o`). |
| `-b, --baseurl <url>` | Base URL for OpenAI-compatible providers. |
**Supported Providers:**
| Provider ID | Description |
|-------------|-------------|
| `anthropic` | Anthropic Claude (direct API) |
| `openai-native` | OpenAI GPT models |
| `openai-codex` | ChatGPT subscription via OAuth |
| `openrouter` | OpenRouter (access multiple providers) |
| `bedrock` | AWS Bedrock |
| `gemini` | Google Gemini |
| `xai` | X AI (Grok) |
| `cerebras` | Cerebras (fast inference) |
| `deepseek` | DeepSeek |
| `ollama` | Ollama (local models) |
| `lmstudio` | LM Studio (local models) |
| `openai` | OpenAI-compatible API (custom base URL) |
---
### cline history (alias: h)
Browse task history with pagination.
```bash
# Show recent tasks (default: 10)
cline history
# Show more tasks
cline history -n 20
# Paginate through history
cline history -n 10 -p 2
```
**Options:**
| Option | Description |
|--------|-------------|
| `-n, --limit <number>` | Number of tasks to show (default: 10) |
| `-p, --page <number>` | Page number, 1-based (default: 1) |
---
### cline config
View and manage configuration settings.
```bash
cline config
```
Opens an interactive configuration view with tabs for:
- **Settings** - Global and workspace-specific settings
- **Rules** - `.clinerules` files and imported rules
- **Workflows** - Available workflows (appear as slash commands)
- **Hooks** - Configured hook scripts
- **Skills** - Enabled skills
---
### cline update
Check for updates and install the latest version.
```bash
cline update
```
---
### cline version
Show the installed CLI version.
```bash
cline version
```
---
### cline dev
Developer tools for debugging.
```bash
# Open the log file
cline dev log
```
## Environment Variables
### CLINE_DIR
Override the default configuration directory:
```bash
export CLINE_DIR=/path/to/custom/config
cline "your task"
```
When set, all Cline data (settings, secrets, task history) is stored in this directory instead of `~/.cline/data/`.
**Use cases:**
- Running isolated Cline instances with different settings
- CI/CD environments with custom state directories
- Testing configuration changes without affecting your main setup
### CLINE_COMMAND_PERMISSIONS
Restrict which shell commands Cline can execute:
```bash
export CLINE_COMMAND_PERMISSIONS='{"allow": ["npm *", "git *"], "deny": ["rm -rf *"]}'
```
**Format:**
```json
{
"allow": ["pattern1", "pattern2"],
"deny": ["pattern3"],
"allowRedirects": true
}
```
| Field | Type | Description |
|-------|------|-------------|
| `allow` | `string[]` | Glob patterns for allowed commands. If set, **only** matching commands are permitted. |
| `deny` | `string[]` | Glob patterns for denied commands. Deny rules **always take precedence** over allow rules. |
| `allowRedirects` | `boolean` | Whether to allow shell redirects (`>`, `>>`, `<`). Default: `false`. |
**Examples:**
```bash
# Allow only npm and git commands (deny everything else)
export CLINE_COMMAND_PERMISSIONS='{"allow": ["npm *", "git *"]}'
# Allow dev commands but explicitly deny dangerous ones
export CLINE_COMMAND_PERMISSIONS='{"allow": ["npm *", "git *", "node *"], "deny": ["rm -rf *", "sudo *"]}'
# Allow file reading with redirects
export CLINE_COMMAND_PERMISSIONS='{"allow": ["cat *", "echo *"], "allowRedirects": true}'
```
**How commands are evaluated:**
1. Check for dangerous characters (backticks outside single quotes, unquoted newlines)
2. Parse command into segments split by operators (`&&`, `||`, `|`, `;`)
3. If redirects are detected and `allowRedirects` is not true, command is denied
4. Each segment is validated against deny rules first, then allow rules
5. Subshell contents (`$(...)` and `(...)`) are recursively validated
6. All segments must pass for the command to be allowed
## JSON Output Format
When using `--json`, each message is output as a JSON object (one per line):
```json
{
"type": "say",
"text": "I'll create the file now.",
"ts": 1760501486669,
"say": "text"
}
```
**Required fields:**
| Field | Type | Description |
|-------|------|-------------|
| `type` | `"ask"` \| `"say"` | Message category |
| `text` | `string` | Human-readable message content |
| `ts` | `number` | Unix timestamp in milliseconds |
**Optional fields:**
| Field | Type | Description |
|-------|------|-------------|
| `say` | `string` | Subtype when `type` is `"say"` (e.g., `"text"`, `"tool"`) |
| `ask` | `string` | Subtype when `type` is `"ask"` (e.g., `"tool"`, `"followup"`) |
| `reasoning` | `string` | Model reasoning (omitted when empty) |
| `partial` | `boolean` | `true` while streaming (omitted when complete) |
| `images` | `string[]` | Image URIs (omitted when empty) |
| `files` | `string[]` | File paths (omitted when empty) |
## Configuration Files
Cline stores all data in `~/.cline/` by default:
```text
~/.cline/
├── data/ # Configuration directory
│ ├── globalState.json # Global settings
│ ├── secrets.json # API keys (stored securely)
│ ├── workspace/ # Workspace-specific state
│ └── tasks/ # Task history and conversations
└── log/ # Debug logs (view with cline dev log)
```
## Examples
### Interactive Development
```bash
# Start interactive mode
cline
# Start with a task and use interactive UI
cline "Help me refactor this codebase"
```
### Direct Task Execution
```bash
# Run a task directly
cline "Add error handling to utils.js"
# Start in Plan mode to review strategy first
cline -p "Design a caching layer for the API"
# Use a specific model
cline -m gpt-4o "Explain this code"
```
### Piped Input
```bash
# Pipe file contents
cat README.md | cline "Summarize this document"
# Review git changes
git diff | cline "Review these changes"
# Analyze test output
npm test 2>&1 | cline "Fix any failing tests"
```
### Automation and CI/CD
```bash
# YOLO mode for automated workflows
cline -y "Run tests and fix failures"
# JSON output for scripting
cline --json "List all TODO comments" | jq '.text'
# With timeout
cline -y --timeout 600 "Run the full test suite"
# Chain commands
git diff | cline -y "explain" | cline -y "write a commit message"
```
### Authentication
```bash
# Interactive wizard
cline auth
# Quick setup: Anthropic
cline auth -p anthropic -k sk-ant-api-xxxxx -m claude-sonnet-4-5-20250929
# Quick setup: OpenAI
cline auth -p openai-native -k sk-xxxxx -m gpt-4o
# Quick setup: OpenRouter
cline auth -p openrouter -k sk-or-xxxxx
# OpenAI-compatible with custom URL
cline auth -p openai -k your-key -b https://api.example.com/v1
```
## Support
- **Report bugs:** https://github.com/cline/cline/issues
- **Discord community:** https://discord.gg/cline
- **Documentation:** https://docs.cline.bot
## See Also
<Columns cols={2}>
<Card title="Installation & Setup" icon="download" href="/cline-cli/installation">
Install Cline CLI and configure authentication.
</Card>
<Card title="Interactive Mode" icon="terminal" href="/cline-cli/interactive-mode">
Keyboard shortcuts, slash commands, and file mentions.
</Card>
<Card title="Headless Mode" icon="robot" href="/cline-cli/three-core-flows">
Run Cline autonomously in scripts, CI/CD pipelines, and automated workflows.
</Card>
<Card title="Configuration" icon="gear" href="/cline-cli/configuration">
Environment variables and advanced settings.
</Card>
</Columns>
-320
View File
@@ -1,320 +0,0 @@
---
title: "Configuration"
description: "Manage Cline CLI settings with cline config, environment variables, and configuration files"
---
Cline CLI provides multiple ways to configure settings, from the interactive `cline config` command to environment variables for automation.
## The Config Command
Launch the configuration interface:
```bash
cline config
```
This opens an interactive view with tabs for different configuration categories.
## Configuration Tabs
Navigate between tabs using arrow keys.
### Settings Tab
View and edit global and workspace-specific settings:
- **Global State**: Settings that apply across all workspaces
- **Workspace State**: Settings specific to the current directory
### Rules Tab
Manage Cline rules that guide AI behavior:
- **`.clinerules` files**: Project-specific rules in your workspace
- **Cursor rules**: Import rules from Cursor editor format
- **Windsurf rules**: Import rules from Windsurf editor format
Rules help Cline understand your project's conventions, coding standards, and preferences.
### Workflows Tab
View and manage [workflows](/customization/workflows):
- List available workflows
- View workflow definitions
- Workflows appear as slash commands in interactive mode
### Hooks Tab
Configure [hooks](/customization/hooks) for custom logic integration:
- Enable/disable hooks globally
- View configured hook scripts
- Hooks run at key points in Cline's workflow
<Note>
Hooks must be enabled via settings. Use `cline config` to toggle `hooks-enabled`.
</Note>
### Skills Tab
Manage [skills](/customization/skills) that extend Cline's capabilities:
- View available skills
- Enable/disable specific skills
- Skills provide specialized instructions for specific tasks
## Configuration Directory
Cline stores configuration in `~/.cline/data/`:
```text
~/.cline/
├── data/ # Configuration directory
│ ├── globalState.json # Global settings
│ ├── secrets.json # API keys (encrypted)
│ ├── settings/ # Settings files
│ │ └── cline_mcp_settings.json # MCP server configuration
│ ├── workspace/ # Workspace-specific state
│ └── tasks/ # Task history and data
└── log/ # Log files
```
### Viewing Logs
For debugging, view the log file:
```bash
cline dev log
```
This opens the log file in your default editor.
## Environment Variables
### CLINE_DIR
Override the default configuration directory:
```bash
export CLINE_DIR=/custom/path/to/cline
cline "your task"
```
When set, all Cline data is stored in this directory instead of `~/.cline/data/`.
**Use cases:**
- Running multiple isolated Cline configurations
- Team-shared configurations
- CI/CD with custom state directories
### CLINE_COMMAND_PERMISSIONS
Restrict which shell commands Cline can execute:
```bash
export CLINE_COMMAND_PERMISSIONS='{"allow": ["npm *", "git *"], "deny": ["rm -rf *"]}'
```
**Format:**
```json
{
"allow": ["pattern1", "pattern2"],
"deny": ["pattern3"],
"allowRedirects": true
}
```
**Fields:**
| Field | Type | Description |
|-------|------|-------------|
| `allow` | `string[]` | Glob patterns for allowed commands. If set, only matching commands are permitted. |
| `deny` | `string[]` | Glob patterns for denied commands. Deny rules take precedence over allow. |
| `allowRedirects` | `boolean` | Whether to allow shell redirects (`>`, `>>`, `<`). Default: `false` |
**Examples:**
```bash
# Allow only npm and git commands
export CLINE_COMMAND_PERMISSIONS='{"allow": ["npm *", "git *"]}'
# Allow dev commands but deny dangerous ones
export CLINE_COMMAND_PERMISSIONS='{"allow": ["npm *", "git *", "node *"], "deny": ["rm -rf *", "sudo *"]}'
# Allow file operations with redirects
export CLINE_COMMAND_PERMISSIONS='{"allow": ["cat *", "echo *"], "allowRedirects": true}'
```
<Warning>
When `allow` is set, all commands not matching the allow patterns are denied. Use this for security-sensitive environments.
</Warning>
## Using --config Flag
Run Cline with a custom configuration directory:
```bash
cline --config /path/to/custom/config "your task"
```
This is useful for:
- Running isolated Cline instances
- Testing different configurations
- Separating work and personal setups
**Example: Multiple configurations**
```bash
# Work configuration
cline --config ~/.cline-work "review this PR"
# Personal projects
cline --config ~/.cline-personal "help me with this side project"
```
## MCP Server Configuration
Cline CLI supports [MCP (Model Context Protocol)](/mcp/mcp-overview) servers, giving you access to external tools and data sources directly from the terminal. The CLI uses the same MCP configuration format as the VS Code extension.
### Setting Up MCP Servers
To configure MCP servers for the CLI, create or edit the settings file at:
```
~/.cline/data/settings/cline_mcp_settings.json
```
The file uses the same JSON format as the VS Code extension:
```json
{
"mcpServers": {
"my-server": {
"command": "node",
"args": ["/path/to/server.js"],
"env": {
"API_KEY": "your_api_key"
},
"alwaysAllow": ["tool1", "tool2"],
"disabled": false
}
}
}
```
For the full configuration reference including STDIO and SSE transport types, see [Adding and Configuring MCP Servers](/mcp/adding-and-configuring-servers).
<Note>
The CLI does not yet have a `/mcp` slash command for managing MCP servers interactively. For now, you'll need to edit the `cline_mcp_settings.json` file directly.
</Note>
### Custom Config Directory
If you use the `CLINE_DIR` environment variable or `--config` flag, the MCP settings file will be located at `<your-config-dir>/data/settings/cline_mcp_settings.json` instead.
## Configuration for Local Providers
### Ollama
Configure context window size for Ollama:
```bash
# In settings or via config
cline config
# Navigate to Settings tab, find ollama-api-options-ctx-num
```
Or set via environment:
```bash
# Set context window to 32K tokens
cline -m ollama/llama3 "your task"
```
### LM Studio
Configure max tokens for LM Studio:
```bash
cline config
# Navigate to Settings tab, find lm-studio-max-tokens
```
## Importing Configuration
### From VS Code Extension
If you use the Cline VS Code extension, the CLI automatically detects and can share some settings. However, the CLI maintains its own configuration for terminal-specific features.
### From Other CLI Tools
See [Installation & Setup](/cline-cli/installation#option-3-import-from-existing-tools) for importing configurations from:
- Codex CLI
- OpenCode
## Configuration Best Practices
### For Development
Use the default configuration with workspace-specific rules:
```bash
# Add project-specific rules
echo "Use TypeScript strict mode" > .clinerules/typescript.md
```
### For CI/CD
Use environment variables and `--yolo` mode:
```bash
export CLINE_COMMAND_PERMISSIONS='{"allow": ["npm test", "npm run build"]}'
cline -y "run tests and fix any failures"
```
### For Teams
Share configuration via version control:
```bash
# Commit .clinerules/ to your repo
git add .clinerules/
git commit -m "Add Cline rules for team"
```
## Troubleshooting
### Configuration Not Persisting
1. Check write permissions on `~/.cline/data/`
2. Ensure `CLINE_DIR` isn't set to a read-only location
3. Verify the config directory exists
### Environment Variables Not Working
1. Ensure variables are exported: `export CLINE_DIR=/path`
2. Check for typos in variable names
3. Verify JSON syntax for `CLINE_COMMAND_PERMISSIONS`
### Reset Configuration
To start fresh, remove the configuration directory:
```bash
rm -rf ~/.cline/data/
cline auth # Re-authenticate
```
## Next Steps
<Columns cols={2}>
<Card title="Headless Mode" icon="robot" href="/cline-cli/three-core-flows">
Run Cline autonomously in scripts, CI/CD pipelines, and automated workflows.
</Card>
<Card title="CLI Reference" icon="terminal" href="/cline-cli/cli-reference">
Complete command documentation with all flags and options.
</Card>
</Columns>
-457
View File
@@ -1,457 +0,0 @@
---
title: "Getting Started"
description: "Run Cline AI coding agents directly in your terminal with an interactive CLI or automated workflows"
---
## What is Cline CLI?
Cline CLI brings the full power of Cline to your terminal. Whether you prefer an interactive experience or automated workflows for CI/CD pipelines, the CLI adapts to your needs.
The CLI supports macOS, Linux, and Windows, and works with all the same AI providers as the VS Code extension.
## Two Ways to Use Cline CLI
The CLI operates in two distinct modes, automatically selecting the appropriate one based on how you invoke it:
### Interactive Mode
Interactive mode is designed for **hands-on development sessions** where you want to collaborate with Cline in real-time. It provides a rich terminal interface that feels like chatting with an AI assistant.
**When it activates:** Running `cline` without arguments, or when stdin is a TTY (terminal).
```bash
cline
```
Key features:
- **Real-time conversation** - Type messages, see Cline's responses, and iterate on tasks
- **Visual feedback** - Animated welcome screen, syntax-highlighted code, and progress indicators
- **File mentions** with `@` - Reference workspace files with fuzzy search autocomplete
- **Slash commands** with `/` - Quick access to `/settings`, `/history`, `/models`, and workflows
- **Keyboard shortcuts** - `Tab` to toggle Plan/Act, `Shift+Tab` for auto-approve all
- **Session summaries** - See tasks completed, files modified, and token usage on exit
- **Settings panel** - Configure providers, models, and features without leaving the CLI
Interactive mode keeps you in control. You review Cline's plan, approve or modify actions, and guide the conversation.
[Learn more about interactive mode →](/cline-cli/interactive-mode)
### Headless Mode (Non-Interactive)
Headless mode is designed for **automation, scripting, and CI/CD pipelines** where human interaction isn't possible or desired.
**When it activates:** Using the `-y`/`--yolo` flag, `--json` flag, piping input/output, or when stdin is not a TTY.
```bash
# Headless with auto-approval (YOLO mode)
cline -y "Run tests and fix any failures"
# Headless with JSON output for parsing
cline --json "List all TODO comments" | jq '.text'
# Headless via piped input
cat README.md | cline "Summarize this document"
# Chain multiple headless commands
git diff | cline -y "explain these changes" | cline -y "write a commit message"
```
Key features:
- **No visual interface** - Clean text or JSON output suitable for scripting
- **Automatic execution** - With `-y`, Cline approves all actions and runs autonomously
- **Process control** - Exits automatically when the task completes
- **Piped workflows** - Read from stdin, write to stdout, chain with other commands
- **Machine-readable output** - Use `--json` to get structured output for parsing
<Warning>
Headless mode with `-y` gives Cline full autonomy. Run on a clean git branch so you can easily revert changes if needed.
</Warning>
### Mode Detection Summary
Cline automatically detects which mode to use based on your invocation. This table shows how different command patterns trigger each mode, helping you predict behavior in scripts and interactive sessions.
| Invocation | Mode | Reason |
|------------|------|--------|
| `cline` | Interactive | No arguments, TTY connected |
| `cline "task"` | Interactive | TTY connected |
| `cline -y "task"` | Headless | YOLO flag forces headless |
| `cline --json "task"` | Headless | JSON flag forces headless |
| `cat file \| cline "task"` | Headless | stdin is piped |
| `cline "task" > output.txt` | Headless | stdout is redirected |
[Learn more about headless mode →](/cline-cli/three-core-flows)
## Supported Model Providers
Cline CLI supports all providers available in the VS Code extension:
- **Anthropic** (Claude)
- **OpenAI** (GPT-4o, GPT-4)
- **OpenAI Codex** (ChatGPT subscription)
- **OpenRouter**
- **AWS Bedrock**
- **Google Gemini**
- **X AI (Grok)**
- **Cerebras**
- **DeepSeek**
- **Ollama** (local models)
- **LM Studio** (local models)
- **OpenAI Compatible** (any compatible API)
During setup, authenticate with `cline auth` to configure your preferred provider. [See authentication →](#authenticate)
## What You Can Build
### Automated Code Maintenance
Keep your codebase healthy with automated fixes. Cline scans for issues and applies corrections across multiple files.
```bash
cline -y "Fix all ESLint errors in src/"
```
Finds and fixes linting violations throughout your source directory.
```bash
cline -y "Update all deprecated React lifecycle methods"
```
Migrates legacy code patterns to modern equivalents (e.g., `componentWillMount` → `useEffect`).
```bash
cline -y "Update dependencies with known vulnerabilities"
```
Identifies outdated packages with security issues and updates them to safe versions.
### CI/CD Integration
Integrate Cline into your continuous integration pipelines for automated code review and documentation.
```bash
git diff origin/main | cline -y "Review these changes for issues"
```
Pipes your PR diff to Cline for automated code review, catching bugs and style issues before merge.
```bash
git log --oneline v1.0..v1.1 | cline -y "Write release notes"
```
Generates human-readable release notes from your commit history between two tags.
```bash
cline -y "Run tests and fix failures" --timeout 600
```
Executes your test suite, analyzes failures, and attempts fixes with a 10-minute timeout.
### Development Workflows
From quick edits to complex refactors, Cline adapts to your workflow.
```bash
cline
```
Launches interactive mode for exploratory development and back-and-forth collaboration.
```bash
cline "Refactor this function to use async/await"
```
Executes a focused task directly from the command line with approval prompts at key steps.
```bash
cline "Based on @src/api.ts, add error handling to all endpoints"
```
Uses file mentions (`@`) to give Cline context about specific files in your workspace.
### Custom Shell Pipelines
Chain Cline with other CLI tools to build powerful automation workflows.
```bash
gh pr diff 123 | cline -y "Review this PR"
```
Fetches a GitHub PR diff and pipes it directly to Cline for review.
```bash
cline --json "List all TODO comments" | jq '.text'
```
Outputs structured JSON that you can process with tools like `jq` for scripting.
```bash
git diff | cline -y "explain" | cline -y "write a haiku about these changes"
```
Chains multiple Cline invocations together for creative multi-step workflows.
## Features at a Glance
| Feature | Interactive Mode | Non-Interactive Mode |
|---------|------------------|----------------------|
| Interactive chat | ✓ | - |
| File mentions (@) | ✓ | ✓ (inline) |
| Slash commands (/) | ✓ | - |
| Settings panel | ✓ | `cline config` |
| Plan/Act toggle | ✓ (Tab) | `-p` / `-a` flags |
| Auto-approve | ✓ (Shift+Tab) | `-y` flag |
| Session summary | ✓ | - |
| JSON output | - | `--json` |
| Piped input | - | ✓ |
---
## Installation & Setup
In just a few minutes, you can install the CLI, authenticate with your preferred AI provider, and start running tasks from any directory on your machine.
### Prerequisites
Cline CLI requires **Node.js version 20 or higher**. We recommend Node.js 22 for the best experience.
Check your Node.js version:
```bash
node --version
```
If you need to install or update Node.js, visit [nodejs.org](https://nodejs.org) or use a version manager like [nvm](https://github.com/nvm-sh/nvm).
### Install Cline CLI
Install globally via npm:
```bash
npm install -g cline
```
Verify the installation:
```bash
cline version
```
<Tip>
To install a specific version, use `npm install -g cline@2.0.0`. Check [npm](https://www.npmjs.com/package/cline) for available versions.
</Tip>
### Authenticate
After installation, run the authentication wizard:
```bash
cline auth
```
This launches an interactive wizard with multiple options. Choose the method that works best for your workflow.
#### Option 1: Sign in with Cline (Recommended)
Select **"Sign in with Cline"** to authenticate with your Cline account via OAuth. Your browser opens automatically to complete sign-in.
#### Option 2: Sign in with ChatGPT Subscription
If you have a ChatGPT Plus or Pro subscription, select **"Sign in with ChatGPT Subscription"**. This uses OpenAI's Codex OAuth to authenticate with your existing subscription.
#### Option 3: Import from Existing Tools
Already using another AI coding CLI? Cline can import your existing configuration:
- **Import from Codex CLI** - Imports credentials from `~/.codex/auth.json`
- **Import from OpenCode** - Imports configuration from `~/.local/share/opencode/auth.json`
#### Option 4: Bring Your Own API Key
Select **"Bring your own API key"** to manually configure any supported provider. Or skip the wizard entirely with flags:
```bash
# Anthropic (Claude)
cline auth -p anthropic -k sk-ant-api-xxxxx -m claude-sonnet-4-5-20250929
# OpenAI
cline auth -p openai-native -k sk-xxxxx -m gpt-4o
# OpenRouter
cline auth -p openrouter -k sk-or-xxxxx -m anthropic/claude-sonnet-4-5-20250929
# OpenAI-compatible provider with custom base URL
cline auth -p openai -k your-api-key -b https://api.example.com/v1
```
**Quick Setup Flags:**
| Flag | Description |
|------|-------------|
| `-p, --provider <id>` | Provider ID (e.g., `anthropic`, `openai-native`, `openrouter`) |
| `-k, --apikey <key>` | Your API key |
| `-m, --modelid <id>` | Model ID (e.g., `claude-sonnet-4-5-20250929`, `gpt-4o`) |
| `-b, --baseurl <url>` | Base URL for OpenAI-compatible providers |
<Tip>
Flags are especially useful for scripting, CI/CD environments, or setting up multiple machines.
</Tip>
#### Supported Providers
| Provider | Provider ID | Notes |
|----------|-------------|-------|
| Anthropic | `anthropic` | Direct Claude API access |
| OpenAI | `openai-native` | GPT-4o, GPT-4, etc. |
| OpenAI Codex | `openai-codex` | ChatGPT subscription OAuth |
| OpenRouter | `openrouter` | Access multiple providers |
| AWS Bedrock | `bedrock` | Claude via AWS |
| Google Gemini | `gemini` | Gemini Pro, etc. |
| X AI (Grok) | `xai` | Grok models |
| Cerebras | `cerebras` | Fast inference |
| DeepSeek | `deepseek` | DeepSeek models |
| Ollama | `ollama` | Local models |
| LM Studio | `lmstudio` | Local models |
| OpenAI Compatible | `openai` | Any OpenAI-compatible API |
### Verify Your Setup
Confirm everything is working with a simple test:
```bash
cline "What is 2 + 2?"
```
If Cline responds with an answer, your installation and authentication are complete.
Check your current configuration:
```bash
cline config
```
### Quick Start
Now you're ready to use Cline. Choose how you want to work:
#### Interactive Mode
Launch the interactive CLI for development:
```bash
cline
```
You'll see the Cline welcome screen. Type your task and press Enter. Use:
- `Tab` to toggle between Plan and Act modes
- `Shift+Tab` to enable auto-approve
- `/help` for available commands
[Learn more about interactive mode →](/cline-cli/interactive-mode)
#### Direct Task Execution
Run a task directly from your shell:
```bash
cline "Add error handling to utils.js"
```
For non-interactive execution (perfect for scripts and CI/CD):
```bash
cline -y "Run tests and fix any failures"
```
[Learn more about headless mode →](/cline-cli/three-core-flows)
### Switching Providers
To change your configured provider at any time:
```bash
cline auth
```
You can also use the settings panel in interactive mode:
```bash
cline
# Then type: /settings
# Navigate to the API tab
```
### Updating
Check for updates and install the latest version:
```bash
cline update
```
Or update manually via npm:
```bash
npm update -g cline
```
### Troubleshooting
#### Command Not Found
If `cline` is not found after installation:
1. Ensure npm global bin is in your PATH:
```bash
npm bin -g
```
2. Add the path to your shell configuration (`.bashrc`, `.zshrc`, etc.):
```bash
export PATH="$PATH:$(npm bin -g)"
```
3. Restart your terminal or source your shell config.
#### Permission Errors
If you get permission errors during installation:
```bash
# Option 1: Use a Node version manager (recommended)
# nvm, fnm, or volta handle permissions automatically
# Option 2: Fix npm permissions
# See: https://docs.npmjs.com/resolving-eacces-permissions-errors-when-installing-packages-globally
```
#### OAuth Flow Issues
If the browser doesn't open automatically during OAuth:
1. Copy the URL from the terminal
2. Paste it in your browser manually
3. Complete the sign-in flow
4. Return to the terminal
#### API Key Validation
If your API key is rejected:
1. Verify the key is correct and hasn't expired
2. Check that you've selected the correct provider
3. Ensure your API account has the necessary permissions
**Provider-specific tips:**
- **Anthropic**: Keys start with `sk-ant-`
- **OpenAI**: Keys start with `sk-`
- **AWS Bedrock**: Requires AWS credentials configured separately. See [AWS Bedrock documentation](/provider-config/aws-bedrock/api-key).
### Uninstallation
To remove Cline CLI:
```bash
npm uninstall -g cline
```
To also remove configuration data:
```bash
rm -rf ~/.cline
```
## Next Steps
- **[Interactive Mode](/cline-cli/interactive-mode)** - Master the interactive CLI with shortcuts and slash commands
- **[Headless Mode](/cline-cli/three-core-flows)** - Run Cline autonomously in scripts, CI/CD pipelines, and automated workflows
- **[Configuration](/cline-cli/configuration)** - Configure settings, rules, workflows, and environment variables
- **[CLI Reference](/cline-cli/cli-reference)** - Complete command documentation with all flags and options
-278
View File
@@ -1,278 +0,0 @@
---
title: "Installation & Setup"
description: "Install Cline CLI on macOS, Linux, or Windows and configure your AI provider"
---
Cline CLI brings the full power of Cline to your terminal. In just a few minutes, you can install the CLI, authenticate with your preferred AI provider, and start running tasks from any directory on your machine.
## Prerequisites
Cline CLI requires **Node.js version 20 or higher**. We recommend Node.js 22 for the best experience.
Check your Node.js version:
```bash
node --version
```
If you need to install or update Node.js, visit [nodejs.org](https://nodejs.org) or use a version manager like [nvm](https://github.com/nvm-sh/nvm).
## Install Cline CLI
Install globally via npm:
```bash
npm install -g cline
```
Verify the installation:
```bash
cline version
```
<Tip>
To install a specific version, use `npm install -g cline@2.0.0`. Check [npm](https://www.npmjs.com/package/cline) for available versions.
</Tip>
## Authenticate
After installation, run the authentication wizard:
```bash
cline auth
```
This launches an interactive wizard with multiple options. Choose the method that works best for your workflow.
### Option 1: Sign in with Cline (Recommended)
Select **"Sign in with Cline"** to authenticate with your Cline account via OAuth. Your browser opens automatically to complete sign-in.
### Option 2: Sign in with ChatGPT Subscription
If you have a ChatGPT Plus or Pro subscription, select **"Sign in with ChatGPT Subscription"**. This uses OpenAI's Codex OAuth to authenticate with your existing subscription.
### Option 3: Import from Existing Tools
Already using another AI coding CLI? Cline can import your existing configuration:
- **Import from Codex CLI** - Imports credentials from `~/.codex/auth.json`
- **Import from OpenCode** - Imports configuration from `~/.local/share/opencode/auth.json`
### Option 4: Bring Your Own API Key
Select **"Bring your own API key"** to manually configure any supported provider. Or skip the wizard entirely with flags:
```bash
# Anthropic (Claude)
cline auth -p anthropic -k sk-ant-api-xxxxx -m claude-sonnet-4-5-20250929
# OpenAI
cline auth -p openai-native -k sk-xxxxx -m gpt-4o
# OpenRouter
cline auth -p openrouter -k sk-or-xxxxx -m anthropic/claude-sonnet-4-5-20250929
# Moonshot
cline auth -p moonshot -k sk-xxxxx -m kimi-k2.5
# OpenAI-compatible provider with custom base URL
cline auth -p openai -k your-api-key -b https://api.example.com/v1
```
**Quick Setup Flags:**
| Flag | Description |
|------|-------------|
| `-p, --provider <id>` | Provider ID (e.g., `anthropic`, `openai-native`, `openrouter`, `moonshot`) |
| `-k, --apikey <key>` | Your API key |
| `-m, --modelid <id>` | Model ID (e.g., `claude-sonnet-4-5-20250929`, `gpt-4o`) |
| `-b, --baseurl <url>` | Base URL for OpenAI-compatible providers |
<Tip>
Flags are especially useful for scripting, CI/CD environments, or setting up multiple machines.
</Tip>
### Supported Providers
| Provider | Provider ID | Notes |
|----------|-------------|-------|
| Anthropic | `anthropic` | Direct Claude API access |
| OpenAI | `openai-native` | GPT-4o, GPT-4, etc. |
| OpenAI Codex | `openai-codex` | ChatGPT subscription OAuth |
| OpenRouter | `openrouter` | Access multiple providers |
| AWS Bedrock | `bedrock` | Claude via AWS |
| Google Gemini | `gemini` | Gemini Pro, etc. |
| X AI (Grok) | `xai` | Grok models |
| Cerebras | `cerebras` | Fast inference |
| DeepSeek | `deepseek` | DeepSeek models |
| Moonshot | `moonshot` | Kimi models via Moonshot AI |
| Ollama | `ollama` | Local models |
| LM Studio | `lmstudio` | Local models |
| OpenAI Compatible | `openai` | Any OpenAI-compatible API |
## Verify Your Setup
Confirm everything is working with a simple test:
```bash
cline "What is 2 + 2?"
```
If Cline responds with an answer, your installation and authentication are complete.
Check your current configuration:
```bash
cline config
```
## Quick Start
Now you're ready to use Cline. Choose how you want to work:
### Interactive Mode
Launch the interactive CLI for development:
```bash
cline
```
You'll see the Cline welcome screen. Type your task and press Enter. Use:
- `Tab` to toggle between Plan and Act modes
- `Shift+Tab` to enable auto-approve
- `/help` for available commands
[Learn more about interactive mode →](/cline-cli/interactive-mode)
### Direct Task Execution
Run a task directly from your shell:
```bash
cline "Add error handling to utils.js"
```
For non-interactive execution (perfect for scripts and CI/CD):
```bash
cline -y "Run tests and fix any failures"
```
[Learn more about headless mode →](/cline-cli/three-core-flows)
## Switching Providers
To change your configured provider at any time:
```bash
cline auth
```
You can also use the settings panel in interactive mode:
```bash
cline
# Then type: /settings
# Navigate to the API tab
```
## Updating
Check for updates and install the latest version:
```bash
cline update
```
Or update manually via npm:
```bash
npm update -g cline
```
## Troubleshooting
### Command Not Found
If `cline` is not found after installation:
1. Ensure npm global bin is in your PATH:
```bash
npm bin -g
```
2. Add the path to your shell configuration (`.bashrc`, `.zshrc`, etc.):
```bash
export PATH="$PATH:$(npm bin -g)"
```
3. Restart your terminal or source your shell config.
### Permission Errors
If you get permission errors during installation:
```bash
# Option 1: Use a Node version manager (recommended)
# nvm, fnm, or volta handle permissions automatically
# Option 2: Fix npm permissions
# See: https://docs.npmjs.com/resolving-eacces-permissions-errors-when-installing-packages-globally
```
### OAuth Flow Issues
If the browser doesn't open automatically during OAuth:
1. Copy the URL from the terminal
2. Paste it in your browser manually
3. Complete the sign-in flow
4. Return to the terminal
### API Key Validation
If your API key is rejected:
1. Verify the key is correct and hasn't expired
2. Check that you've selected the correct provider
3. Ensure your API account has the necessary permissions
**Provider-specific tips:**
- **Anthropic**: Keys start with `sk-ant-`
- **OpenAI**: Keys start with `sk-`
- **AWS Bedrock**: Requires AWS credentials configured separately. See [AWS Bedrock documentation](/provider-config/aws-bedrock/api-key).
## Uninstallation
To remove Cline CLI:
```bash
npm uninstall -g cline
```
To also remove configuration data:
```bash
rm -rf ~/.cline
```
## Next Steps
<Columns cols={2}>
<Card title="Interactive Mode" icon="terminal" href="/cline-cli/interactive-mode">
Master the interactive CLI with shortcuts and slash commands.
</Card>
<Card title="Headless Mode" icon="robot" href="/cline-cli/three-core-flows">
Run Cline autonomously in scripts, CI/CD pipelines, and automated workflows.
</Card>
<Card title="Configuration" icon="gear" href="/cline-cli/configuration">
Configure settings, rules, workflows, and environment variables.
</Card>
<Card title="CLI Reference" icon="book" href="/cline-cli/cli-reference">
Complete command documentation with all flags and options.
</Card>
</Columns>
-252
View File
@@ -1,252 +0,0 @@
---
title: "Interactive Mode"
description: "Master the interactive CLI with keyboard shortcuts, slash commands, and file mentions"
---
Interactive mode is the primary way to work with Cline CLI when you want a collaborative, conversational experience. Unlike headless mode (which runs a single task and exits), interactive mode keeps a session open where you can have back-and-forth conversations with Cline, refine your requests, and guide the AI as it works.
## Why Use Interactive Mode?
Interactive mode is ideal when you:
- **Don't know exactly what you need yet** - Explore a codebase, ask questions, and let Cline help you understand the architecture before making changes
- **Want to review before acting** - Toggle Plan mode to see Cline's strategy, then switch to Act mode when you're ready
- **Need iterative refinement** - Build on previous responses, ask follow-up questions, and guide Cline to the right solution
- **Prefer human oversight** - Review each action, approve file changes, and maintain control over what Cline does
- **Working on complex tasks** - Multi-step refactoring, debugging sessions, or feature development that requires judgment calls
For automated workflows, scripts, or CI/CD pipelines, see [headless mode](/cline-cli/overview#headless-mode-non-interactive) instead.
## Prerequisites
Before using interactive mode, you need to have Cline CLI installed and authenticated. If you haven't done this yet, follow the [Installation & Setup guide](/cline-cli/installation) first.
## Launching Interactive Mode
Start interactive mode by running `cline` without any arguments:
```bash
cline
```
You'll see an animated welcome screen with the Cline robot. Start typing your task in the input field at the bottom of the screen.
## Keyboard Shortcuts
Keyboard shortcuts are the primary way to navigate and control the interactive CLI. Since there's no mouse interaction in the terminal, learning these shortcuts will help you work efficiently and switch between modes, manage input, and control your session without breaking your flow.
### Mode Controls
| Shortcut | Action |
|----------|--------|
| `Tab` | Toggle between Plan and Act mode |
| `Shift+Tab` | Toggle auto-approve all actions |
| `Esc` | Exit or cancel current operation |
### Input Controls
| Shortcut | Action |
|----------|--------|
| `Enter` | Submit your message |
| `↑` / `↓` | Navigate message history |
| `Home` / `End` | Move cursor to start/end of line |
| `Ctrl+A` | Move cursor to beginning |
| `Ctrl+E` | Move cursor to end |
| `Ctrl+W` | Delete word before cursor |
| `Ctrl+U` | Delete entire line |
### Session Controls
| Shortcut | Action |
|----------|--------|
| `Ctrl+C` | Exit with session summary |
## File Mentions with @
Reference files from your workspace by typing `@` followed by the filename:
```text
@src/utils.ts can you add error handling to this file?
```
As you type after `@`, Cline shows a fuzzy search dropdown of matching files. Use arrow keys to navigate and `Enter` to select.
<Tip>
File search uses ripgrep for fast, fuzzy matching. You can type partial paths like `@utils` to find `src/utils/helpers.ts`.
</Tip>
### Multiple File Mentions
Include multiple files in a single message:
```text
Compare @src/old-api.ts with @src/new-api.ts and list the breaking changes
```
## Slash Commands
Type `/` to see available commands. Slash commands provide quick access to settings, history, and workflows.
### Built-in Commands
| Command | Description |
|---------|-------------|
| `/settings` | Open the settings panel |
| `/models` | Quick model switching |
| `/history` | Browse and resume previous tasks |
| `/clear` | Start a fresh task (clears current conversation) |
| `/help` | Show help and available commands |
| `/exit` | Exit the CLI |
### Workflow Commands
If you have [workflows](/customization/workflows) configured, they appear as additional slash commands. For example, if you have a workflow named `code-review`, you can invoke it with:
```text
/code-review
```
## Settings Panel
Access the settings panel with `/settings`. Navigate between tabs using arrow keys.
| Tab | Description | Settings |
|-----|-------------|----------|
| **API** | Configure your AI provider and model | Provider selection, model choice, extended thinking toggle, thinking budget |
| **Auto-approve** | Control which actions Cline can perform without prompting | Read files, write files, execute commands, browser actions, MCP tools |
| **Features** | Toggle Cline capabilities | Hooks, skills, auto-compact, sound notifications |
| **Account** | Manage your Cline account | View account status, sign in/out, manage subscription |
| **Other** | Additional preferences | Theme preferences, debug options |
## Plan and Act Modes
Cline operates in two modes, toggled with `Tab`. These modes work the same way in the CLI as they do in the VS Code extension. For a deeper explanation of how Plan and Act modes work, see the [Plan and Act documentation](/core-workflows/plan-and-act).
### Plan Mode
In Plan mode, Cline analyzes your request and creates a strategy before making changes. Use this when:
- Exploring a new codebase
- Working on complex refactoring
- You want to review the approach first
### Act Mode
In Act mode, Cline executes tasks directly. Use this when:
- You're confident in the task
- Making straightforward changes
- Running quick operations
<Tip>
Press `Tab` anytime to switch modes. Starting in Plan mode and switching to Act after reviewing is a common workflow.
</Tip>
## Auto-approve Toggle
Press `Shift+Tab` to toggle auto-approve for all actions. This removes the approval prompts that appear before each action, letting Cline work continuously without interruption.
### When to Enable Auto-approve
Auto-approve is useful when:
- **You trust the task** - Well-defined tasks where you're confident in the outcome
- **Speed matters** - Long-running tasks where constant approvals slow you down
- **You're watching anyway** - You can see Cline's work in real-time and can interrupt if needed
- **Iterating quickly** - Rapid prototyping where you want to see results fast
### What Gets Auto-approved
When enabled, these actions happen without prompting:
- File reads
- File writes
- Command execution
- Browser actions
- MCP tool calls
You can also configure granular auto-approve settings (e.g., auto-approve reads but not writes) via `/settings` → Auto-approve tab, or see the [Auto-approve documentation](/features/auto-approve) for more details.
<Warning>
Auto-approve gives Cline full autonomy. Use on a clean git branch so you can easily revert changes if needed. You can always press `Ctrl+C` to stop Cline immediately.
</Warning>
## Session Summary
When you exit with `Ctrl+C`, Cline displays a session summary showing:
- Tasks completed
- Files modified
- Commands executed
- Token usage
This helps you track what was accomplished during your session.
## Running Multiple Instances
By default, all CLI instances share the same settings and state. However, you may want to run isolated instances with separate configurations for scenarios like:
- **Different models for different tasks** - Use a fast, cheap model for quick questions in one terminal and a more capable model for complex refactoring in another
- **Separate work and personal projects** - Keep API keys, rules, and task history isolated between contexts
- **Testing configuration changes** - Experiment with new settings without affecting your main setup
- **Team vs. individual settings** - Use shared team configuration for work projects and personal preferences for side projects
To run isolated instances, use the `--config` flag with different directories:
```bash
# Work instance with team configuration
cline --config ~/.cline-work
# Personal instance with different model/provider
cline --config ~/.cline-personal
# Experimental instance for testing new settings
cline --config ~/.cline-test
```
Each config directory maintains its own provider settings, API keys, task history, and preferences.
<Tip>
Use terminal multiplexers like tmux or split terminals to run multiple Cline instances in parallel, each working on different parts of your project with different models or settings.
</Tip>
## Tips for Effective Usage
### Start with Context
Give Cline context about what you're working on:
```text
I'm building a REST API with Express. The routes are in @src/routes/ and models in @src/models/. Help me add user authentication.
```
### Use Plan Mode for Exploration
When you're unsure about the best approach:
```text
[Tab to Plan mode]
How should I structure the database schema for a multi-tenant SaaS app?
```
### Iterate with Follow-ups
The interactive CLI maintains conversation context. Build on previous messages:
```text
> Add a login endpoint
[Cline creates the endpoint]
> Now add rate limiting to it
[Cline modifies the same endpoint]
> Add tests for both features
[Cline creates test files]
```
## Next Steps
<Columns cols={2}>
<Card title="Headless Mode" icon="robot" href="/cline-cli/three-core-flows">
Run Cline autonomously in scripts, CI/CD pipelines, and automated workflows.
</Card>
<Card title="Configuration" icon="gear" href="/cline-cli/configuration">
Explore `cline config` and advanced configuration options.
</Card>
</Columns>
-246
View File
@@ -1,246 +0,0 @@
---
title: "Overview"
description: "Run Cline AI coding agents directly in your terminal with an interactive CLI or automated workflows"
---
## What is Cline CLI?
Cline CLI brings the full power of Cline to your terminal. Whether you prefer an interactive experience or automated workflows for CI/CD pipelines, the CLI adapts to your needs.
The CLI supports macOS, Linux, and Windows, and works with all the same AI providers as the VS Code extension.
<Tip>
Ready to get started? Check out the [installation guide](/cline-cli/installation) to install Cline CLI and run your first task.
</Tip>
## Two Ways to Use Cline CLI
<Columns cols={2}>
<Card title="Interactive Mode" icon="terminal" href="/cline-cli/interactive-mode">
**For hands-on development.** Launch `cline` in your terminal and collaborate with Cline in real-time — chat, review plans, approve actions, and iterate on tasks with a rich visual interface.
</Card>
<Card title="Headless Mode" icon="robot" href="/cline-cli/three-core-flows">
**For automation & CI/CD.** Run `cline -y "task"` to let Cline work autonomously — no interaction needed. Pipe input/output, get JSON results, and chain commands in scripts and pipelines.
</Card>
</Columns>
The CLI operates in two distinct modes, automatically selecting the appropriate one based on how you invoke it:
### Interactive Mode
Interactive mode is designed for **hands-on development sessions** where you want to collaborate with Cline in real-time. It provides a rich terminal interface that feels like chatting with an AI assistant.
**When it activates:** Running `cline` without arguments, or when stdin is a TTY (terminal).
```bash
cline
```
Key features:
- **Real-time conversation** - Type messages, see Cline's responses, and iterate on tasks
- **Visual feedback** - Animated welcome screen, syntax-highlighted code, and progress indicators
- **File mentions** with `@` - Reference workspace files with fuzzy search autocomplete
- **Slash commands** with `/` - Quick access to `/settings`, `/history`, `/models`, and workflows
- **Keyboard shortcuts** - `Tab` to toggle Plan/Act, `Shift+Tab` for auto-approve all
- **Session summaries** - See tasks completed, files modified, and token usage on exit
- **Settings panel** - Configure providers, models, and features without leaving the CLI
Interactive mode keeps you in control. You review Cline's plan, approve or modify actions, and guide the conversation.
[Learn more about interactive mode →](/cline-cli/interactive-mode)
### Headless Mode (Non-Interactive)
Headless mode is designed for **automation, scripting, and CI/CD pipelines** where human interaction isn't possible or desired.
**When it activates:** Using the `-y`/`--yolo` flag, `--json` flag, piping input/output, or when stdin is not a TTY.
```bash
# Headless with auto-approval (YOLO mode)
cline -y "Run tests and fix any failures"
# Headless with JSON output for parsing
cline --json "List all TODO comments" | jq '.text'
# Headless via piped input
cat README.md | cline "Summarize this document"
# Chain multiple headless commands
git diff | cline -y "explain these changes" | cline -y "write a commit message"
```
Key features:
- **No visual interface** - Clean text or JSON output suitable for scripting
- **Automatic execution** - With `-y`, Cline approves all actions and runs autonomously
- **Process control** - Exits automatically when the task completes
- **Piped workflows** - Read from stdin, write to stdout, chain with other commands
- **Machine-readable output** - Use `--json` to get structured output for parsing
<Warning>
Headless mode with `-y` gives Cline full autonomy. Run on a clean git branch so you can easily revert changes if needed.
</Warning>
### Mode Detection Summary
Cline automatically detects which mode to use based on your invocation. This table shows how different command patterns trigger each mode, helping you predict behavior in scripts and interactive sessions.
| Invocation | Mode | Reason |
|------------|------|--------|
| `cline` | Interactive | No arguments, TTY connected |
| `cline "task"` | Interactive | TTY connected |
| `cline -y "task"` | Headless | YOLO flag forces headless |
| `cline --json "task"` | Headless | JSON flag forces headless |
| `cat file \| cline "task"` | Headless | stdin is piped |
| `cline "task" > output.txt` | Headless | stdout is redirected |
[Learn more about headless mode →](/cline-cli/three-core-flows)
## Supported Model Providers
Cline CLI supports all providers available in the VS Code extension:
- **Anthropic** (Claude)
- **OpenAI** (GPT-4o, GPT-4)
- **OpenAI Codex** (ChatGPT subscription)
- **OpenRouter**
- **AWS Bedrock**
- **Google Gemini**
- **X AI (Grok)**
- **Cerebras**
- **DeepSeek**
- **Ollama** (local models)
- **LM Studio** (local models)
- **OpenAI Compatible** (any compatible API)
During setup, authenticate with `cline auth` to configure your preferred provider. [See setup guide →](/cline-cli/installation#authenticate)
## What You Can Build
### Automated Code Maintenance
Keep your codebase healthy with automated fixes. Cline scans for issues and applies corrections across multiple files.
```bash
cline -y "Fix all ESLint errors in src/"
```
Finds and fixes linting violations throughout your source directory.
```bash
cline -y "Update all deprecated React lifecycle methods"
```
Migrates legacy code patterns to modern equivalents (e.g., `componentWillMount` → `useEffect`).
```bash
cline -y "Update dependencies with known vulnerabilities"
```
Identifies outdated packages with security issues and updates them to safe versions.
### CI/CD Integration
Integrate Cline into your continuous integration pipelines for automated code review and documentation.
```bash
git diff origin/main | cline -y "Review these changes for issues"
```
Pipes your PR diff to Cline for automated code review, catching bugs and style issues before merge.
```bash
git log --oneline v1.0..v1.1 | cline -y "Write release notes"
```
Generates human-readable release notes from your commit history between two tags.
```bash
cline -y "Run tests and fix failures" --timeout 600
```
Executes your test suite, analyzes failures, and attempts fixes with a 10-minute timeout.
### Development Workflows
From quick edits to complex refactors, Cline adapts to your workflow.
```bash
cline
```
Launches interactive mode for exploratory development and back-and-forth collaboration.
```bash
cline "Refactor this function to use async/await"
```
Executes a focused task directly from the command line with approval prompts at key steps.
```bash
cline "Based on @src/api.ts, add error handling to all endpoints"
```
Uses file mentions (`@`) to give Cline context about specific files in your workspace.
### Custom Shell Pipelines
Chain Cline with other CLI tools to build powerful automation workflows.
```bash
gh pr diff 123 | cline -y "Review this PR"
```
Fetches a GitHub PR diff and pipes it directly to Cline for review.
```bash
cline --json "List all TODO comments" | jq '.text'
```
Outputs structured JSON that you can process with tools like `jq` for scripting.
```bash
git diff | cline -y "explain" | cline -y "write a haiku about these changes"
```
Chains multiple Cline invocations together for creative multi-step workflows.
## Features at a Glance
| Feature | Interactive Mode | Non-Interactive Mode |
|---------|------------------|----------------------|
| Interactive chat | ✓ | - |
| File mentions (@) | ✓ | ✓ (inline) |
| Slash commands (/) | ✓ | - |
| Settings panel | ✓ | `cline config` |
| Plan/Act toggle | ✓ (Tab) | `-p` / `-a` flags |
| Auto-approve | ✓ (Shift+Tab) | `-y` flag |
| Session summary | ✓ | - |
| JSON output | - | `--json` |
| Piped input | - | ✓ |
| [MCP servers](/cline-cli/configuration#mcp-server-configuration) | ✓ | ✓ |
## MCP Server Support
Cline CLI supports [MCP (Model Context Protocol)](/mcp/mcp-overview) servers, the same extensibility system available in the VS Code extension. MCP servers give Cline access to external tools and data sources, from databases and APIs to browser automation and project management.
To use MCP servers with the CLI, add your server configuration to `~/.cline/data/settings/cline_mcp_settings.json`. The format is identical to the VS Code extension.
[Configure MCP servers for the CLI →](/cline-cli/configuration#mcp-server-configuration)
## Learn More
<Columns cols={2}>
<Card title="Installation & Setup" icon="download" href="/cline-cli/installation">
Install Cline CLI and authenticate with your preferred provider.
</Card>
<Card title="Interactive Mode" icon="terminal" href="/cline-cli/interactive-mode">
Master the interactive CLI with keyboard shortcuts and slash commands.
</Card>
<Card title="Headless Mode" icon="robot" href="/cline-cli/three-core-flows">
Run Cline autonomously in scripts, CI/CD pipelines, and automated workflows.
</Card>
<Card title="Configuration" icon="gear" href="/cline-cli/configuration">
Configure settings, rules, workflows, and environment variables.
</Card>
<Card title="Use in Other Editors" icon="code" href="/cline-cli/acp-editor-integrations">
Run Cline as an ACP agent in JetBrains, Neovim, Zed, and more.
</Card>
<Card title="CLI Samples" icon="flask" href="/cline-cli/samples/overview">
Real-world examples of headless workflows and automation patterns.
</Card>
</Columns>
-56
View File
@@ -1,56 +0,0 @@
---
title: "Samples Overview"
description: Example implementations demonstrating Cline CLI capabilities
---
This section provides sample implementations that demonstrate various Cline CLI features and capabilities. Each sample includes complete code, detailed explanations, and real-world usage examples.
## Available Samples
<CardGroup cols={1}>
<Card
title="Model Orchestration"
icon="layer-group"
href="/cline-cli/samples/model-orchestration"
>
Use multiple AI models strategically with --config and --thinking flags. Optimize costs by routing simple tasks to cheap models and complex reasoning to premium models. Includes patterns for CI/CD code review, task phase optimization, and multi-model consensus.
</Card>
<Card
title="Worktree Workflows"
icon="code-branch"
href="/cline-cli/samples/worktree-workflows"
>
Use Git worktrees with the --cwd flag to run parallel tasks, test different approaches, and pipe context between isolated environments. Includes patterns for parallel execution, cross-worktree piping, and combining with model orchestration.
</Card>
<Card
title="GitHub Root Cause Analysis"
icon="magnifying-glass-chart"
href="/cline-cli/samples/github-issue-rca"
>
A command-line script that uses Cline's autonomous AI capabilities to fetch, analyze, and identify root causes of GitHub issues. Features JSON output parsing and non-interactive execution.
</Card>
<Card
title="GitHub Integration (Actions)"
icon="github"
href="/cline-cli/samples/github-integration"
>
Automatically respond to GitHub issues by mentioning @cline in comments. Uses Cline CLI in GitHub Actions to create an AI-powered issue assistant that analyzes and responds autonomously.
</Card>
<Card
title="GitHub PR Review (Actions)"
icon="code-pull-request"
href="/cline-cli/samples/github-pr-review"
>
Automatically review Pull Requests with AI. Configures Cline in GitHub Actions to analyze diffs, check for security issues, and post detailed reviews with inline code suggestions.
</Card>
</CardGroup>
## Additional Resources
- [CLI Installation Guide](/cline-cli/installation)
- [CLI Reference Documentation](/cline-cli/cli-reference)
- [Headless Mode](/cline-cli/three-core-flows)
@@ -1,273 +0,0 @@
---
title: "Worktree Workflows"
description: "Use Git worktrees with Cline CLI to run parallel tasks, test different approaches, and pipe context between isolated environments"
---
Git worktrees let you have multiple branches checked out simultaneously in different folders. Combined with Cline CLI's `--cwd` flag, this enables powerful parallel development workflows and isolated experimentation.
<Tip>
New to Git worktrees? See our comprehensive [Worktrees guide](/features/worktrees) for the full concept explanation, VS Code integration, and best practices.
</Tip>
## Quick Worktree Setup
If you haven't used Git worktrees before, here's the essentials:
```bash
# Create a new worktree in ~/worktree-a on branch feature-a
git worktree add ~/worktree-a -b feature-a
# Create another worktree for a different feature
git worktree add ~/worktree-b -b feature-b
# List all worktrees
git worktree list
# Remove a worktree when done
git worktree remove ~/worktree-a
```
Each worktree is a separate folder with its own branch checked out. They all share the same Git history and `.git` directory, but have independent working directories.
## The `--cwd` Flag
The `-c, --cwd <path>` flag tells Cline to run in a specific directory without changing your current location:
```bash
# Run Cline in a different directory
cline --cwd ~/worktree-a -y "refactor the authentication code"
# Short form
cline -c ~/worktree-b -y "add unit tests"
```
This is the key to worktree workflows—you can run multiple Cline instances in different worktrees simultaneously from a single terminal.
## Pattern 1: Parallel Task Execution
Run different tasks in parallel across multiple worktrees. Each task works on a separate branch in complete isolation.
### Example: Parallel Feature Development
```bash
# Terminal 1: Update docs in worktree-a
cline --cwd ~/worktree-a -y "read the last 10 changes using git show and update our README with them" &
# Terminal 2: TypeScript migration in worktree-b
cline --cwd ~/worktree-b -y "update the index.js to use typescript" &
# Terminal 3: Refactoring in worktree-c
cline --cwd ~/worktree-c -y "refactor the cli/ folder to be more modular" &
# Wait for all to complete
wait
```
The `&` runs each command in the background, allowing all three to execute simultaneously.
### When to Use Parallel Execution
**Perfect for:**
- Multiple independent features
- Bulk refactoring across different modules
- Running tests in one worktree while developing in another
- Trying multiple approaches to the same problem
**Not ideal for:**
- Tasks that modify the same files (merge conflicts likely)
- Tasks that depend on each other's results
- When you need to monitor progress closely
## Pattern 2: Cross-Worktree Context Piping
Pipe output from one worktree as input to another. Use when a task in one worktree needs context from attempts in another worktree.
### Example: Learning from Failures
```bash
# Try approach A in worktree-a, capture only the failure summary
cline --cwd ~/worktree-a -y \
"edit the index.ts to be better and then npm run. if it fails, output ONLY the failure summary. nothing else but the failure summary" \
| cline --cwd ~/worktree-b -y \
"i've tried to edit the index.ts in a different worktree but it failed. use a different approach for this work tree"
```
**How it works:**
1. First Cline instance runs in `worktree-a`, attempts a change, tests it
2. If it fails, outputs just the failure summary
3. That summary is piped to a second Cline instance in `worktree-b`
4. Second instance sees the failure and tries a different approach
### When to Use Context Piping
**Perfect for:**
- A/B testing different solutions
- Learning from failed attempts
- Iterative refinement (try → analyze → try differently)
- Comparing outputs across approaches
**Not ideal for:**
- Simple tasks that don't need cross-context
- When both worktrees would succeed independently
- Real-time collaboration (use parallel execution instead)
## Combining with Other CLI Features
### Different Models Per Worktree
Use `--config` to run different models in different worktrees:
```bash
# Cheap model for simple docs update
cline --cwd ~/worktree-docs --config ~/.cline-haiku -y \
"update README with latest changes"
# Expensive model for complex refactoring
cline --cwd ~/worktree-refactor --config ~/.cline-opus --thinking -y \
"refactor authentication system for better security"
```
This optimizes costs while maintaining quality where it matters.
### Task Isolation
Keep long-running worktree sessions isolated by running each task against a different worktree path:
```bash
# Run tasks in dedicated worktrees
cline --cwd ~/worktree-a -y "long-running task"
cline --cwd ~/worktree-b -y "another task"
```
Each worktree has its own Git branch and working directory, so task history and changes stay separated without needing instance management.
### With YOLO Mode
The `-y` (YOLO) flag is essential for worktree workflows:
```bash
# Without -y: Opens interactive chat (blocks other tasks)
cline --cwd ~/worktree-a "refactor code"
# With -y: Runs autonomously (doesn't block)
cline --cwd ~/worktree-a -y "refactor code" &
```
For parallel execution, always use `-y` to avoid blocking on user approval.
## Real-World Workflow Example
Here's a complete workflow showing how these patterns work together:
```bash
# Setup: Create three worktrees
git worktree add ~/cline-worktrees/feature-auth -b feature/authentication
git worktree add ~/cline-worktrees/feature-api -b feature/api-endpoints
git worktree add ~/cline-worktrees/fix-tests -b fix/failing-tests
# Pattern 1: Run parallel independent tasks
cline -c ~/cline-worktrees/feature-auth -y --config ~/.cline-sonnet \
"implement JWT authentication" &
cline -c ~/cline-worktrees/feature-api -y --config ~/.cline-sonnet \
"create REST API endpoints for user management" &
cline -c ~/cline-worktrees/fix-tests -y --config ~/.cline-haiku \
"fix all failing unit tests" &
wait
echo "All parallel tasks complete!"
# Pattern 2: Use piping for iterative refinement
cline -c ~/cline-worktrees/feature-auth -y \
"test the authentication with curl. output only errors if any" \
| cline -c ~/cline-worktrees/feature-auth -y \
"fix the authentication issues described in the input"
# Merge successful changes back
cd ~/cline-worktrees/feature-auth
git checkout main
git merge feature/authentication
# Cleanup
git worktree remove ~/cline-worktrees/feature-auth
```
## Best Practices
<AccordionGroup>
<Accordion title="Worktree Organization">
- **Use a dedicated folder**: Create `~/cline-worktrees/` for all worktrees
- **Meaningful branch names**: Use `feature/`, `fix/`, `refactor/` prefixes
- **Clean up regularly**: Remove worktrees after merging branches
</Accordion>
<Accordion title="Task Isolation">
- **Independent features only**: Don't parallelize tasks that touch the same files
- **Test in isolation**: Each worktree should have its own test run
- **Separate configs**: Use `.worktreeinclude` to copy `node_modules` and build artifacts
</Accordion>
<Accordion title="Resource Management">
- **Monitor disk space**: Each worktree is a full checkout
- **Limit parallel tasks**: Running too many simultaneously can slow your system
- **Use background jobs wisely**: Track with `jobs` command, kill with `kill %1`, etc.
</Accordion>
<Accordion title="Error Handling">
- **Check exit codes**: Use `|| echo "Task failed"` to catch errors
- **Log outputs**: Redirect to files for debugging: `> worktree-a.log 2>&1`
- **Graceful cleanup**: Always remove worktrees after tasks complete
</Accordion>
</AccordionGroup>
## Troubleshooting
<AccordionGroup>
<Accordion title="&quot;Branch already checked out&quot; error">
Git doesn't allow the same branch in multiple worktrees. Solutions:
- Use different branch names for each worktree
- Remove the existing worktree first: `git worktree remove <path>`
</Accordion>
<Accordion title="Tasks not running in parallel">
Make sure you're using:
- `&` at the end of each command to background it
- `-y` flag so Cline doesn't wait for approval
- Different worktrees (not the same path)
</Accordion>
<Accordion title="Pipe not working as expected">
Verify:
- First command outputs to stdout (not stderr)
- Second command reads from stdin (use `--` separator if needed)
- Both commands use correct `--cwd` paths
</Accordion>
<Accordion title="Changes not appearing in worktree">
Check:
- You're in the right worktree: `git worktree list`
- Files aren't gitignored
- You committed/staged changes if needed
</Accordion>
</AccordionGroup>
## Related Documentation
<Columns cols={2}>
<Card title="Worktrees Overview" icon="code-branch" href="/features/worktrees">
Complete guide to Git worktrees, VS Code integration, and .worktreeinclude
</Card>
<Card title="Model Orchestration" icon="layer-group" href="/cline-cli/samples/model-orchestration">
Use different models strategically with --config and --thinking flags
</Card>
<Card title="CLI Reference" icon="terminal" href="/cline-cli/cli-reference">
Complete documentation for --cwd and all other CLI flags
</Card>
<Card title="Headless Mode" icon="robot" href="/cline-cli/three-core-flows">
Run Cline autonomously in scripts, CI/CD pipelines, and automated workflows.
</Card>
</Columns>
-244
View File
@@ -1,244 +0,0 @@
---
title: "Headless Mode"
description: "Run Cline autonomously in scripts, CI/CD pipelines, and automated workflows"
---
Headless mode runs Cline without an interactive interface — perfect for automation, scripting, and CI/CD pipelines where human interaction isn't possible or desired. Cline executes tasks, produces clean text or JSON output, and exits when complete.
For collaborative, conversational development, see [Interactive Mode](/cline-cli/interactive-mode) instead.
<Note>
**Migrating from an older CLI version?** Instance commands (`cline instance new/list/kill`) have been removed in Cline CLI 2.0. The new architecture is simpler — just use `cline -y "task"` for headless execution.
</Note>
## When Headless Mode Activates
Cline automatically enters headless mode when any of these conditions are met:
| Invocation | Reason |
|------------|--------|
| `cline -y "task"` | `-y`/`--yolo` flag forces headless |
| `cline --json "task"` | `--json` flag forces headless |
| `cat file \| cline "task"` | stdin is piped |
| `cline "task" > output.txt` | stdout is redirected |
If none of these apply (e.g., running `cline` or `cline "task"` in a terminal), Cline launches in [interactive mode](/cline-cli/interactive-mode).
## YOLO Mode (Fully Autonomous)
The `-y` or `--yolo` flag enables fully autonomous operation — Cline approves all actions and runs without prompts:
```bash
cline -y "Run the test suite and fix any failures"
```
In YOLO mode:
- All actions are auto-approved
- Output is plain text (non-interactive)
- Process exits automatically when complete
- Perfect for CI/CD and scripts
<Warning>
YOLO mode gives Cline full autonomy. Run on a clean git branch so you can easily revert changes if needed.
</Warning>
### Mode Selection
Control whether Cline plans first or acts immediately:
```bash
# Start in Plan mode (analyze before acting)
cline -y -p "Design a REST API for user management"
# Start in Act mode (default)
cline -y -a "Fix the typo in README.md"
```
## Piping Context
Pipe file contents or command output into Cline to provide context:
```bash
# Explain a file
cat README.md | cline "Summarize this document"
# Review git changes
git diff | cline "Review these changes and suggest improvements"
# Analyze command output
npm test 2>&1 | cline "Analyze these test failures and fix them"
# Pipe a GitHub PR diff
gh pr diff 123 | cline -y "Review this PR"
```
When stdin is piped, Cline automatically enters headless mode — the piped content becomes part of the task context.
## Chaining Commands
Pipe Cline's output into another Cline instance for multi-step workflows:
```bash
# Explain changes, then write a commit message
git diff | cline -y "explain these changes" | cline -y "write a commit message for this"
# Generate code, then write tests
cline -y "create a fibonacci function" | cline -y "write unit tests for this code"
# Fun: Generate a poem about your code
git diff | cline -y "explain" | cline -y "write a haiku about this"
```
## JSON Output
Use `--json` for machine-readable output that's easy to parse in scripts:
```bash
cline --json "List all TODO comments in the codebase" | jq '.text'
```
JSON output follows the same format as task files in `~/.cline/data/tasks/<id>/ui_messages.json`.
**JSON Message Schema:**
| Field | Type | Description |
|-------|------|-------------|
| `type` | `"ask"` or `"say"` | Message category |
| `text` | `string` | Message content |
| `ts` | `number` | Unix timestamp (ms) |
| `reasoning` | `string` | (Optional) Model reasoning |
| `partial` | `boolean` | (Optional) Streaming flag |
## Including Images
Attach images to your headless task:
```bash
cline -y -i screenshot.png "Fix the layout issue shown in this screenshot"
# Or reference inline
cline -y "Fix the UI shown in @./design-mockup.png"
```
## Timeout Control
Set a maximum execution time to prevent runaway tasks:
```bash
cline -y --timeout 600 "Run full test suite"
```
## Environment Variables
Control Cline behavior via environment variables — useful for CI/CD where you can't use interactive configuration.
**CLINE_DIR** — Custom configuration directory:
```bash
export CLINE_DIR=/path/to/config
cline -y "your task"
```
**CLINE_COMMAND_PERMISSIONS** — Restrict allowed commands:
```bash
export CLINE_COMMAND_PERMISSIONS='{"allow": ["npm *", "git *"], "deny": ["rm -rf *"]}'
cline -y "your task"
```
See [Configuration](/cline-cli/configuration#environment-variables) for full documentation.
## CI/CD Integration
### GitHub Actions Example
Automate PR reviews with Cline:
```yaml
name: AI Code Review
on:
pull_request:
types: [opened, synchronize]
jobs:
review:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-node@v4
with:
node-version: '22'
- name: Install Cline
run: npm install -g cline
- name: Configure Cline
run: cline auth -p anthropic -k ${{ secrets.ANTHROPIC_API_KEY }}
- name: Review PR
run: |
git diff origin/main...HEAD | cline -y "Review this PR for:
- Potential bugs
- Security issues
- Performance concerns
- Code style violations
Provide a summary of findings."
```
### Shell Script Example
Create a reusable code review script:
```bash
#!/bin/bash
# review.sh - AI-powered code review
set -e
# Get the diff
DIFF=$(git diff HEAD~1)
if [ -z "$DIFF" ]; then
echo "No changes to review"
exit 0
fi
# Run Cline review
echo "$DIFF" | cline -y --json "Review this code diff for issues" | jq -r '.text'
```
## Common Use Cases
| Use Case | Example |
|----------|---------|
| Code review | `git diff \| cline -y "Review these changes"` |
| Fix test failures | `cline -y "Run tests and fix any failures"` |
| Generate release notes | `git log --oneline v1.0..v1.1 \| cline -y "Write release notes"` |
| Fix lint errors | `cline -y "Fix all ESLint errors in src/"` |
| Update dependencies | `cline -y "Update dependencies with known vulnerabilities"` |
| Migrate code patterns | `cline -y "Update all deprecated React lifecycle methods"` |
| PR automation | `gh pr diff 123 \| cline -y "Review this PR"` |
| Batch processing | `cline -y --json "List all TODO comments" \| jq '.text'` |
## Next Steps
<Columns cols={2}>
<Card title="Interactive Mode" icon="terminal" href="/cline-cli/interactive-mode">
For hands-on development with keyboard shortcuts, slash commands, and file mentions.
</Card>
<Card title="CLI Reference" icon="book" href="/cline-cli/cli-reference">
Complete command documentation with all flags and options.
</Card>
<Card title="Configuration" icon="gear" href="/cline-cli/configuration">
Environment variables, rules, and advanced settings.
</Card>
<Card title="CLI Samples" icon="flask" href="/cline-cli/samples/overview">
Real-world examples of headless workflows and automation patterns.
</Card>
</Columns>
+68
View File
@@ -0,0 +1,68 @@
---
title: "Cline Overview"
sidebarTitle: "Cline Overview"
description: "Your AI-powered coding agent for complex work. Read files, write code, run commands, all with your approval."
---
Welcome to the Cline documentation. Whether you're just getting started or looking to unlock advanced capabilities, you'll find everything you need here.
## What is Cline?
Cline is an AI coding agent that lives in your editor and your terminal. It can read and write files, run terminal commands, use a browser, and help you build features through natural conversation. Every action requires your explicit approval. You're always in control.
### Agent Core (SDK)
The SDK is Cline's agent core—use it to build your own applications, automations, and integrations. See SDK section for detailed functionality and architectural design of the Cline Agent.
<CardGroup cols={1}>
<Card title="SDK" icon="cube" href="https://docs.cline.bot/sdk/overview">
Build AI agents and integrations powered by the same core engine behind the CLI, Kanban, VS Code extension, and JetBrains plugin.
`npm install @cline/sdk`
</Card>
</CardGroup>
### Applications
These are end-user applications built on top of Cline's agent core:
<CardGroup cols={2}>
<Card title="CLI" icon="terminal" href="/usage/cli-overview">
Run Cline in your terminal with interactive chat or fully headless automation for CI/CD and scripting.
`npm i -g cline`
</Card>
<Card title="Kanban" icon="table-columns" href="https://github.com/cline/kanban">
Run many agents in parallel from a web-based task board with per-card worktrees, auto-commit, and dependency chains.
`npx kanban`
</Card>
<Card title="VS Code Extension" icon="code" href="https://marketplace.visualstudio.com/items?itemName=saoudrizwan.claude-dev">
AI coding assistant in your editor. Create files, run commands, browse the web, and use tools with human-in-the-loop approval.
</Card>
<Card title="JetBrains Plugin" icon="brain" href="https://plugins.jetbrains.com/plugin/27189-cline">
The same Cline experience in IntelliJ IDEA, PyCharm, WebStorm, GoLand, and the rest of the JetBrains family.
</Card>
</CardGroup>
## Other IDE Supports
Cline works across all major editors: **VS Code**, **Cursor**, **Windsurf**, **JetBrains** (IntelliJ, PyCharm, WebStorm), **Antigravity**, and **Zed**, **Neovim** via ACP mode.
## Enterprise Solutions
<CardGroup cols={2}>
<Card title="Security & Governance" icon="shield-halved" href="/enterprise-solutions/overview">
SSO, role-based access control, model and tool controls per team, and remote configuration.
</Card>
<Card title="Observability" icon="chart-line" href="/enterprise-solutions/monitoring/overview">
OpenTelemetry, Datadog, Grafana, Splunk integrations with real-time analytics.
</Card>
<Card title="Team Management" icon="users-gear" href="/enterprise-solutions/team-management/managing-members">
Manage members, roles, and permissions across your organization.
</Card>
<Card title="API Reference" icon="code" href="/enterprise-solutions/api-reference">
Programmatic access to Cline's enterprise features.
</Card>
</CardGroup>
-675
View File
@@ -1,675 +0,0 @@
---
title: "Cline SDK"
description: "Embed Cline as a programmable coding agent in your Node.js applications using an ACP-compatible TypeScript API."
---
# Cline SDK
The Cline SDK lets you embed Cline as a programmable coding agent in your Node.js applications. It exposes the same capabilities as the Cline CLI and VS Code extension — file editing, command execution, browser use, MCP servers — through a TypeScript API that conforms to the [Agent Client Protocol (ACP)](https://agentclientprotocol.com/protocol/schema).
## Installation
```bash
npm install cline
```
If you want direct ACP type imports as well:
```bash
npm install @agentclientprotocol/sdk
```
Requires Node.js 20+.
## Quick Start
```typescript
import { ClineAgent } from "cline";
const CLINE_DIR = "/Users/username/.cline";
const agent = new ClineAgent({ clineDir: CLINE_DIR });
// 1. Initialize — negotiates capabilities
const initializeResponse = await agent.initialize({
protocolVersion: 1,
// these are the capabilities that the client (you) supports
// The cline agent may or may not use them, but it needs to know about them to make informed decisions about what tools to use.
clientCapabilities: {
fs: { readTextFile: true, writeTextFile: true },
terminal: true,
},
});
const { agentInfo, authMethods } = initializeResponse;
console.log("Agent info:", agentInfo); // contains things like agent name and version
console.log("Auth methods:", authMethods); // contains a list of supported authentication methods. More auth methods coming soon
// 2. Authenticate if needed
// If you skip this step, ClineAgent will look in CLINE_DIR for any existing credentials and authenticate with those
await agent.authenticate({ methodId: "cline-oauth" });
// 3. Create a session.
// A session represents a conversation or task with the agent. You can have multiple sessions for different tasks or conversations.
const { sessionId } = await agent.newSession({
cwd: process.cwd(),
mcpServers: [], // mcpServers field not supported yet, but exposed here to maintain conformance with acp protocol
});
// 4. Agent updates are sent via events. You can subscribe to these events to get real-time updates on the agent's progress, tool calls, and more.
const emitter = agent.emitterForSession(sessionId);
emitter.on("agent_message_chunk", (payload) => {
process.stdout.write(
payload.content.type === "text"
? payload.content.text
: `[${payload.content.type}]`,
);
});
emitter.on("agent_thought_chunk", (payload) => {
process.stdout.write(
payload.content.type === "text"
? payload.content.text
: `[${payload.content.type}]`,
);
});
emitter.on("tool_call", (payload) => {
console.log(`[tool] ${payload.title}`);
});
emitter.on("error", (err) => {
console.error("[session error]", err);
});
// 5. Send a prompt and wait for completion
const { stopReason } = await agent.prompt({
sessionId,
prompt: [{ type: "text", text: "Create a hello world Express server" }],
});
console.log("Done:", stopReason);
// 6. Clean up
await agent.shutdown();
```
## Core Concepts
### Agent Lifecycle
The SDK follows the ACP lifecycle:
```
initialize() → authenticate() → newSession() → prompt() ⇄ events → shutdown()
```
| Step | Method | Purpose |
|------|--------|---------|
| Init | `initialize()` | Exchange protocol version and capabilities |
| Auth | `authenticate()` | OAuth flow for Cline or OpenAI Codex accounts. Optional step if cline config directory already has credentials |
| Session | `newSession()` | Create an isolated conversation context |
| Prompt | `prompt()` | Send user messages; blocks until the turn ends |
| Cancel | `cancel()` | Abort an in-progress prompt turn |
| Mode | `setSessionMode()` | Switch between `"plan"` and `"act"` modes |
| Model | `unstable_setSessionModel()` | Change the backing LLM (experimental) |
| Shutdown | `shutdown()` | Abort all tasks, flush state, release resources |
### Sessions
A session is an independent conversation with its own task history and working directory. You can run multiple sessions concurrently.
```typescript
const { sessionId, modes, models } = await agent.newSession({
cwd: "/path/to/project",
mcpServers: [], // mcpServers field not supported yet, but exposed here to maintain conformance with acp protocol
})
```
The response includes:
- `sessionId` — use this in all subsequent calls
- `modes` — available modes (`plan`, `act`) and the current mode
- `models` — available models and the current model ID
Access session metadata via the read-only `sessions` map:
```typescript
const session = agent.sessions.get(sessionId)
// { sessionId, cwd, mode, mcpServers, createdAt, lastActivityAt, ... }
```
### Prompting
`prompt()` sends a user message and blocks until the agent finishes its turn. While the prompt is processing, the agent streams output via session events.
```typescript
const response = await agent.prompt({
sessionId,
prompt: [
{ type: "text", text: "Refactor the auth module to use JWT" },
],
})
```
The prompt array accepts multiple content blocks:
```typescript
// Text + image + file context
await agent.prompt({
sessionId,
prompt: [
{ type: "text", text: "What's in this screenshot?" },
{ type: "image", data: base64ImageData, mimeType: "image/png" },
{
type: "resource",
resource: {
uri: "file:///path/to/relevant-file.ts",
mimeType: "text/plain",
text: fileContents,
},
},
],
})
```
#### Content Block Types
| Type | Fields | Description |
|------|--------|-------------|
| `TextContent` | `{ type: "text", text: string }` | Plain text message |
| `ImageContent` | `{ type: "image", mimeType: string, data: string }` | Base64-encoded image |
| `EmbeddedResource` | `{ type: "resource", resource: { uri: string, mimeType?: string, text?: string, blob?: string } }` | File or resource context |
#### Stop Reasons
`prompt()` resolves with a `stopReason`:
| Value | Meaning |
|-------|---------|
| `"end_turn"` | Agent finished normally (completed task or waiting for user input) |
| `"error"` | An error occurred |
### Streaming Events
Subscribe to real-time output via `ClineSessionEmitter`. Each session has its own emitter.
```typescript
const emitter = agent.emitterForSession(sessionId)
```
#### Event Types
All events correspond to [ACP `SessionUpdate` types](https://agentclientprotocol.com/protocol/schema#SessionUpdate):
| Event | Payload | Description |
|-------|---------|-------------|
| `agent_message_chunk` | `{ content: ContentBlock }` | Streamed text from the agent |
| `agent_thought_chunk` | `{ content: ContentBlock }` | Internal reasoning / chain-of-thought |
| `tool_call` | `ToolCall` | New tool invocation (file edit, command, etc.) |
| `tool_call_update` | `ToolCallUpdate` | Progress/result update for an existing tool call |
| `plan` | `{ entries: PlanEntry[] }` | Agent's execution plan |
| `available_commands_update` | `{ availableCommands: AvailableCommand[] }` | Slash commands the agent supports |
| `current_mode_update` | `{ currentModeId: string }` | Mode changed (plan/act) |
| `user_message_chunk` | `{ content: ContentBlock }` | User message chunks (for multi-turn) |
| `config_option_update` | `{ configOptions: SessionConfigOption[] }` | Configuration changed |
| `session_info_update` | Session metadata | Session metadata changed |
| `error` | `Error` | Session-level error (not an ACP update) |
```typescript
emitter.on("agent_message_chunk", (payload) => {
// payload.content is a ContentBlock — usually { type: "text", text: "..." }
process.stdout.write(payload.content.text)
})
emitter.on("agent_thought_chunk", (payload) => {
console.log("[thinking]", payload.content.text)
})
emitter.on("tool_call", (payload) => {
console.log(`[${payload.kind}] ${payload.title} (${payload.status})`)
})
emitter.on("tool_call_update", (payload) => {
console.log(` → ${payload.toolCallId}: ${payload.status}`)
})
emitter.on("error", (err) => {
console.error("Session error:", err)
})
```
The emitter supports `on`, `once`, `off`, and `removeAllListeners`.
### Permission Handling
When the agent wants to execute a tool (edit a file, run a command, etc.), it requests permission. You **must** set a permission handler or all tool calls will be auto-rejected.
```typescript
agent.setPermissionHandler(async (request) => {
// request.toolCall — details about what the agent wants to do
// request.options — available choices (allow_once, reject_once, etc.)
console.log(`Permission requested: ${request.toolCall.title}`)
console.log("Options:", request.options.map(o => `${o.optionId} (${o.kind})`))
// Auto-approve everything:
const allowOption = request.options.find(o => o.kind.includes("allow"))
if (allowOption) {
return { outcome: { outcome: "selected", optionId: allowOption.optionId } }
} else {
return { outcome: { outcome: "rejected" } }
}
})
```
#### Permission Options
Each permission request includes an array of `PermissionOption` objects:
| `kind` | Meaning |
|--------|---------|
| `allow_once` | Approve this single operation |
| `allow_always` | Approve and remember for future operations |
| `reject_once` | Deny this single operation |
| `reject_always` | Deny and remember for future operations |
**Important:** If no permission handler is set, all tool calls are rejected for safety.
### Modes
Cline supports two modes:
- **`plan`** — The agent gathers information and creates a plan without executing actions
- **`act`** — The agent executes actions (file edits, commands, etc.)
```typescript
// Switch to plan mode
await agent.setSessionMode({ sessionId, modeId: "plan" })
// Switch back to act mode
await agent.setSessionMode({ sessionId, modeId: "act" })
```
The current mode is returned in `newSession()`
### Model Selection
Change the backing model with `unstable_setSessionModel()`. The model ID format is `"provider/modelId"`.
```typescript
await agent.unstable_setSessionModel({
sessionId,
modelId: "anthropic/claude-sonnet-4-20250514",
})
```
This sets the model for both plan and act modes. Available providers include `anthropic`, `openai-native`, `gemini`, `bedrock`, `deepseek`, `mistral`, `groq`, `xai`, and others. Model Ids can be found in the NewSessionResponse object after calling `agent.newSession(..)`
> **Note:** This API is experimental and may change.
### Authentication
The SDK supports two OAuth flows:
```typescript
// Cline account (uses browser OAuth)
await agent.authenticate({ methodId: "cline-oauth" })
// OpenAI Codex / ChatGPT subscription
await agent.authenticate({ methodId: "openai-codex-oauth" })
```
Both methods open a browser window for the OAuth flow and block until authentication completes (5-minute timeout for Cline OAuth).
For BYO (bring-your-own) API key providers, configure the key through the cline config directory before creating a session. The `authenticate()` call is not needed for BYO providers. We plan to support more auth providers in the near future.
### Cancellation
Cancel an in-progress prompt turn:
```typescript
await agent.cancel({ sessionId })
```
## API Reference
### Constructor
```typescript
new ClineAgent(options: ClineAgentOptions)
```
```typescript
interface ClineAgentOptions {
/** Enable debug logging (default: false) */
debug?: boolean
/** Custom Cline config directory (default: ~/.cline) */
clineDir?: string
}
```
The `clineDir` option lets you isolate configuration and task history per-application:
```typescript
const agent = new ClineAgent({
clineDir: "/tmp/my-app-cline",
})
```
### Methods
#### `initialize(params): Promise<InitializeResponse>`
Initialize the agent and negotiate protocol capabilities.
```typescript
const response = await agent.initialize({
clientCapabilities: {},
protocolVersion: 1,
})
// Response includes:
{
protocolVersion: "0.9.0",
agentCapabilities: {
loadSession: true,
promptCapabilities: { image: true, audio: false, embeddedContext: true },
mcpCapabilities: { http: true, sse: false }
},
agentInfo: { name: "cline", version: "2.2.3" },
authMethods: [
{ id: "cline-oauth", name: "Sign in with Cline", description: "..." },
{ id: "openai-codex-oauth", name: "Sign in with ChatGPT", description: "..." }
]
}
```
#### `newSession(params): Promise<NewSessionResponse>`
Create a new conversation session.
```typescript
const session = await agent.newSession({
cwd: "/path/to/project",
mcpServers: [
{
type: "stdio",
name: "filesystem",
command: "npx",
args: ["-y", "@modelcontextprotocol/server-filesystem", "/path/to/dir"],
env: {},
},
],
})
// Response includes:
{
sessionId: "uuid-string",
modes: {
availableModes: [
{ id: "plan", name: "Plan", description: "Gather information and create a detailed plan" },
{ id: "act", name: "Act", description: "Execute actions to accomplish the task" }
],
currentModeId: "act"
},
models: {
currentModelId: "anthropic/claude-sonnet-4-5-20241022",
availableModels: [{ modelId: "anthropic/claude-3-5-sonnet-20241022", name: "..." }]
}
}
```
> **Note:** `newSession()` may throw an auth-required error if credentials are not configured yet.
#### `prompt(params): Promise<PromptResponse>`
Send a user prompt to the agent. This is the main method for interacting with Cline. Blocks until the agent finishes its turn.
```typescript
const response = await agent.prompt({
sessionId: session.sessionId,
prompt: [
{ type: "text", text: "Create a function that adds two numbers" },
],
})
// Response: { stopReason: "end_turn" | "max_tokens" | "cancelled" | "error" }
```
#### `cancel(params): Promise<void>`
Cancel an ongoing prompt operation.
```typescript
await agent.cancel({ sessionId: session.sessionId })
```
#### `setSessionMode(params): Promise<SetSessionModeResponse>`
Switch between plan and act modes.
```typescript
await agent.setSessionMode({ sessionId, modeId: "plan" })
```
#### `unstable_setSessionModel(params): Promise<SetSessionModelResponse>`
Change the model for the session. Model ID format depends on the inference provider. See NewSessionResponse object to get modelIds.
```typescript
await agent.unstable_setSessionModel({
sessionId,
modelId: "anthropic/claude-sonnet-4-20250514",
})
```
#### `authenticate(params): Promise<AuthenticateResponse>`
Authenticate with a provider. Opens a browser window for OAuth flow.
```typescript
await agent.authenticate({ methodId: "cline-oauth" })
```
Current methodIds we support:
| methodId | Description |
| -------------------- | ----------------------------- |
| `cline-oauth` | use cline inference provider |
| `openai-codex-oauth` | use your chatgpt subscription |
| more coming soon!... | |
#### `shutdown(): Promise<void>`
Clean up all resources. Call this when done.
```typescript
await agent.shutdown()
```
#### `setPermissionHandler(handler)`
Set a callback to handle tool permission requests.
```typescript
agent.setPermissionHandler((request, resolve) => {
resolve({ outcome: { outcome: "selected", optionId: "allow_once" } })
})
```
#### `emitterForSession(sessionId): ClineSessionEmitter`
Get the typed event emitter for a session.
```typescript
const emitter = agent.emitterForSession(session.sessionId)
```
#### `sessions` (read-only Map)
Access active sessions:
```typescript
for (const [sessionId, session] of agent.sessions) {
console.log(sessionId, session.cwd, session.mode)
}
```
## Full Example: Auto-Approve Agent
```typescript
import { ClineAgent } from "cline";
async function runTask(taskPrompt: string, cwd: string) {
const agent = new ClineAgent({ clineDir: "/Users/maxpaulus/.cline" });
await agent.initialize({
protocolVersion: 1,
clientCapabilities: {},
});
const { sessionId } = await agent.newSession({ cwd, mcpServers: [] });
// Auto-approve all tool calls
agent.setPermissionHandler(async (request) => {
const allow = request.options.find((o) => o.kind === "allow_once");
return {
outcome: allow
? { outcome: "selected", optionId: allow.optionId }
: { outcome: "cancelled" },
};
});
// Collect output
const output: string[] = [];
const emitter = agent.emitterForSession(sessionId);
emitter.on("agent_message_chunk", (p) => {
if (p.content.type === "text") output.push(p.content.text);
});
emitter.on("tool_call", (p) => {
console.log(`[tool] ${p.title}`);
});
const { stopReason } = await agent.prompt({
sessionId,
prompt: [{ type: "text", text: taskPrompt }],
});
console.log("\n--- Agent Output ---");
console.log(output.join(""));
console.log(`\nStop reason: ${stopReason}`);
await agent.shutdown();
}
runTask("Create a README.md for this project", process.cwd());
```
## Full Example: Interactive Permission Flow
```typescript
import { ClineAgent, type PermissionHandler } from "cline";
import * as readline from "readline";
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
const ask = (q: string) => new Promise<string>((res) => rl.question(q, res));
const interactivePermissions: PermissionHandler = async (request) => {
console.log(`\n⚠️ Permission: ${request.toolCall.title}`);
for (const [i, opt] of request.options.entries()) {
console.log(` ${i + 1}. [${opt.kind}] ${opt.name}`);
}
const choice = await ask("Choose (number): ");
const idx = parseInt(choice, 10) - 1;
const selected = request.options[idx];
if (selected) {
return {
outcome: { outcome: "selected", optionId: selected.optionId },
};
} else {
return { outcome: { outcome: "cancelled" } };
}
};
async function main() {
const agent = new ClineAgent({});
await agent.initialize({ protocolVersion: 1, clientCapabilities: {} });
const { sessionId } = await agent.newSession({
cwd: process.cwd(),
mcpServers: [],
});
agent.setPermissionHandler(interactivePermissions);
const emitter = agent.emitterForSession(sessionId);
emitter.on("agent_message_chunk", (p) => {
if (p.content.type === "text") process.stdout.write(p.content.text);
});
// Multi-turn conversation
while (true) {
const userInput = await ask("\n> ");
if (userInput === "exit") break;
const { stopReason } = await agent.prompt({
sessionId,
prompt: [{ type: "text", text: userInput }],
});
console.log(`\n[${stopReason}]`);
}
await agent.shutdown();
rl.close();
}
main();
```
## Exported Types
All types are re-exported from the `cline` package. Key types:
| Type | Description |
|------|-------------|
| `ClineAgent` | Main agent class |
| `ClineSessionEmitter` | Typed event emitter for session events |
| `ClineAgentOptions` | Constructor options |
| `ClineAcpSession` | Session metadata (read-only) |
| `ClineSessionEvents` | Event name → handler signature map |
| `PermissionHandler` | `(request, resolve) => void` callback |
| `PermissionResolver` | `(response) => void` callback |
| `SessionUpdate` | Union of all session update types |
| `SessionUpdateType` | Discriminator values (`"agent_message_chunk"`, `"tool_call"`, etc.) |
| `ToolCall` | Tool call details (id, title, kind, status, content) |
| `ToolCallUpdate` | Partial update to an existing tool call |
| `ToolCallStatus` | `"pending" \| "in_progress" \| "completed" \| "failed"` |
| `ToolKind` | `"read" \| "edit" \| "delete" \| "execute" \| "search" \| ...` |
| `StopReason` | `"end_turn" \| "cancelled" \| "error" \| "max_tokens" \| ...` |
| `ContentBlock` | `TextContent \| ImageContent \| AudioContent \| ...` |
| `McpServer` | MCP server configuration (stdio, http) |
| `PromptRequest` / `PromptResponse` | Prompt call types |
| `NewSessionRequest` / `NewSessionResponse` | Session creation types |
| `InitializeRequest` / `InitializeResponse` | Initialization types |
See the [ACP Schema](https://agentclientprotocol.com/protocol/schema) for the full type definitions.
## Relationship to ACP
The Cline SDK implements the [Agent Client Protocol](https://agentclientprotocol.com) `Agent` interface. The key difference from a standard ACP stdio agent is that the SDK uses an **event emitter pattern** instead of a transport connection:
| ACP Stdio (via `AcpAgent`) | SDK (via `ClineAgent`) |
|-----------------------------|------------------------|
| Session updates sent over JSON-RPC stdio | Session updates emitted via `ClineSessionEmitter` |
| Permissions requested via `connection.requestPermission()` | Permissions requested via `setPermissionHandler()` callback |
| Single process, single connection | Embeddable, multiple concurrent sessions |
If you need stdio-based ACP communication (e.g., for IDE integration), use the `cline` CLI binary directly. The SDK is for embedding Cline in your own Node.js processes.
-316
View File
@@ -1,316 +0,0 @@
---
title: "Documentation Templates"
sidebarTitle: "Templates"
description: "Templates for different types of Cline documentation"
---
Use these templates as starting points for new documentation. Each template is designed for a specific purpose. Choose the one that best fits what you're documenting.
## Choosing a Template
| If you're documenting... | Use this template |
|--------------------------|-------------------|
| What a feature does and how to use it | Feature Doc |
| How to accomplish a specific task | How-To Guide |
| Technical specifications or API details | Reference Doc |
| A complete project walkthrough | Tutorial |
## Feature Doc
Use this template when explaining a Cline feature. Focus on what it does, how to use it, and real examples.
````text
---
title: "Feature Name"
sidebarTitle: "Feature Name"
---
[One sentence explaining what this feature does.]
<Frame>
<img src="..." alt="Feature in action" />
</Frame>
[1-2 paragraphs explaining the feature in plain terms. What problem does it
solve? Why would someone use it?]
## How It Works
[Explain the mechanics without jargon. What happens when you use this feature?]
## Using [Feature Name]
[Show how to access and use it. Include the exact UI path.]
### [Option or Variation 1]
[Details with examples]
### [Option or Variation 2]
[Details with examples]
## Inspiration
[Share how you personally use this feature. Use "I" voice. Give 2-3 real
examples that spark imagination about what's possible.]
<Note>
[Important caveat, limitation, or requirement]
</Note>
````
### Example: Checkpoints Feature
Here's how the [Checkpoints](/core-workflows/checkpoints) doc follows this pattern:
- Opens with one clear sentence about what checkpoints do
- Shows a screenshot of the feature in action
- Explains how checkpoints work under the hood
- Shows exact steps to create and restore checkpoints
- Includes real examples of when checkpoints save the day
## How-To Guide
Use this template when showing how to accomplish a specific task. Focus on clear steps and troubleshooting.
````text
---
title: "How to [Accomplish Task]"
sidebarTitle: "[Short Title]"
description: "[One sentence describing what the reader will learn]"
---
[Brief intro explaining what problem this guide solves and what you'll end up
with after following it.]
## Prerequisites
[What the reader needs before starting. Keep it short. Link to other docs
rather than explaining setup here.]
- Cline installed and configured
- [Other requirement]
## Steps
<Steps>
<Step title="[First Action]">
[Clear instructions. Show exactly what to click or type.]
```bash
example command if needed
```
</Step>
<Step title="[Second Action]">
[Next step. Include screenshots for complex UI interactions.]
<Frame>
<img src="..." alt="What you should see" />
</Frame>
</Step>
<Step title="[Final Action]">
[Complete the task. Show the expected result.]
</Step>
</Steps>
## Troubleshooting
Common issues and how to fix them:
- **Problem description**: Solution in one or two sentences.
- **Another problem**: Another solution.
## Next Steps
<Card title="Related Feature" icon="arrow-right" href="/path/to/related">
Continue learning with this related guide.
</Card>
````
### Example: Your First Project
The [Your First Project](/getting-started/your-first-project) guide follows this pattern:
- Clear goal stated upfront
- Prerequisites listed briefly
- Step-by-step instructions with the Steps component
- Troubleshooting section for common issues
## Reference Doc
Use this template for technical specifications, API documentation, or detailed configuration options.
````text
---
title: "[Component/API] Reference"
sidebarTitle: "[Short Title]"
description: "[What this reference covers]"
---
[Brief description of what this reference documents and when you'd need it.]
## Overview
[High-level explanation. What is this component? What role does it play?]
## [Category 1]
### [Item Name]
[What it does in one sentence.]
| Property | Type | Default | Description |
|----------|------|---------|-------------|
| `propertyName` | `string` | `"default"` | What this property controls |
| `anotherProp` | `boolean` | `false` | What this does |
**Example:**
```typescript
// Show practical usage
const example = {
propertyName: "custom value",
anotherProp: true
}
```
### [Another Item]
[Continue for each item in this category.]
## [Category 2]
[Continue with other categories as needed.]
## Examples
[Show 2-3 complete, practical examples that combine multiple concepts.]
### [Example 1 Title]
```typescript
// Complete working example
```
### [Example 2 Title]
```typescript
// Another complete example
```
## Related
- [Related Doc 1](/path/to/doc) - Brief description
- [Related Doc 2](/path/to/doc) - Brief description
````
### Example: Cline Tools Guide
The [Cline Tools Guide](/tools-reference/all-cline-tools) follows this pattern:
- Overview of the tool system
- Each tool documented with parameters and examples
- Practical examples showing tools in context
## Tutorial
Use this template for comprehensive project walkthroughs where users build something from start to finish.
````text
---
title: "[Build/Create X] Tutorial"
sidebarTitle: "[Short Title]"
description: "[What the reader will build]"
---
In this tutorial, you'll build [specific outcome]. By the end, you'll have
[tangible result you can see/use].
<Frame>
<img src="..." alt="Preview of what you'll build" />
</Frame>
## What You'll Learn
- [Skill or concept 1]
- [Skill or concept 2]
- [Skill or concept 3]
## Prerequisites
[Required setup. Link to installation guides rather than repeating them.]
- [Prerequisite 1]
- [Prerequisite 2]
## Part 1: [First Major Section]
[Introduction to this section. What are we doing and why?]
### [Subsection]
[Detailed walkthrough with code blocks and explanations.]
```typescript
// Code that the reader should write or understand
```
[Explain what the code does and why.]
## Part 2: [Second Major Section]
[Continue building on Part 1.]
### [Subsection]
[More detailed walkthrough.]
## Part 3: [Final Section]
[Complete the project.]
## Summary
You built [what they built]. Along the way, you learned:
- [Key takeaway 1]
- [Key takeaway 2]
- [Key takeaway 3]
## Next Steps
<CardGroup cols={2}>
<Card title="Go Deeper" icon="book" href="/path/to/advanced">
Learn more advanced techniques.
</Card>
<Card title="Related Tutorial" icon="code" href="/path/to/related">
Build something else with similar concepts.
</Card>
</CardGroup>
````
### Example Structure
A good tutorial:
- Shows the end result upfront so readers know what they're building
- Breaks the work into logical parts
- Explains the "why" alongside the "how"
- Ends with clear next steps
## Quick Tips
When using these templates:
1. **Delete sections you don't need.** Templates are starting points, not rigid structures.
2. **Add sections that make sense.** If your doc needs something not in the template, add it.
3. **Keep the reader moving forward.** Every section should lead naturally to the next.
4. **Test your own instructions.** Follow your guide from scratch to catch missing steps.
<Tip>
Use the `/write-docs` workflow to generate documentation from these templates automatically.
Cline helps you fill in each section based on your project.
</Tip>
-200
View File
@@ -1,200 +0,0 @@
---
title: "Documentation Guide"
sidebarTitle: "Documentation Guide"
description: "How to write and contribute to Cline documentation"
---
Cline's documentation lives in the `docs/` directory and uses [Mintlify](https://mintlify.com) for rendering. This guide covers how to write docs that match Cline's established style.
## Using the Documentation Workflow
The fastest way to create documentation is using the `/write-docs` workflow. Type `/write-docs` in Cline and describe what you want to document. Cline guides you through a 4-step process:
1. **Research**: Examine existing docs structure and patterns
2. **Scope**: Clarify audience, doc type, and key use cases
3. **Outline**: Select a template and create structure
4. **Write**: Generate documentation following style guidelines
The workflow file lives at `.clinerules/workflows/write-docs.md` and contains templates, style rules, and examples.
## Documentation Principles
### Write for Developers
Your audience is developers who value their time. Get to the point. Every sentence should either help them understand something or help them do something.
```markdown
# Good
Switch to bash in Cline Settings → Terminal → Default Terminal Profile.
# Bad
Users who are experiencing issues may find it helpful to navigate to the
Cline settings menu where they can locate the terminal configuration
options and subsequently modify the default terminal profile setting.
```
### Show Real Examples
Abstract descriptions don't help anyone. Show actual code, real file paths, and concrete implementations.
```markdown
# Good
I use `/deep-planning` whenever I'm building features that touch multiple
parts of the codebase. For example, when adding authentication, Cline
mapped every endpoint and created a migration plan that avoided breaking changes.
# Bad
The deep planning feature can be utilized for various complex tasks
that may require careful consideration and planning.
```
### Use Active Voice
Cline does things. Files don't get created by Cline, Cline creates files.
```markdown
# Good
Cline reads your project files and builds context automatically.
# Bad
Project files are read and context is built automatically.
```
### Use Neutral Pronouns for Cline
Refer to Cline as "it" not "he". Cline is software, not a person.
```markdown
# Good
When Cline encounters an error, it suggests fixes.
# Bad
When Cline encounters an error, he suggests fixes.
```
## File Format
All documentation uses MDX format with YAML frontmatter:
```yaml
---
title: "Full Page Title"
sidebarTitle: "Shorter Nav Title" # optional
description: "One sentence for SEO" # optional but recommended
---
```
### Adding New Pages
After creating a new `.mdx` file, add it to `docs/docs.json` in the appropriate navigation group:
```json
{
"group": "Features",
"pages": [
"features/existing-page",
"features/your-new-page"
]
}
```
## Mintlify Components
Use these components appropriately throughout your docs.
### Frame
Wrap all images and videos:
```jsx
<Frame>
<img
src="https://storage.googleapis.com/cline_public_images/docs/assets/filename.png"
alt="Descriptive alt text"
/>
</Frame>
```
### Callouts
Use sparingly and purposefully:
```jsx
<Tip>Helpful suggestions that improve the experience.</Tip>
<Note>Important information the reader needs to know.</Note>
<Warning>Something that could cause problems if ignored.</Warning>
```
### Steps
For sequential procedures:
```jsx
<Steps>
<Step title="Install the Extension">
Search for "Cline" in the VS Code marketplace.
</Step>
<Step title="Configure Your Model">
Open settings and add your API key.
</Step>
</Steps>
```
### Cards
For navigation and feature overviews:
```jsx
<CardGroup cols={2}>
<Card title="Getting Started" icon="rocket" href="/getting-started/installing-cline">
Install Cline and set up your first project.
</Card>
<Card title="Features" icon="wand-magic-sparkles" href="/core-workflows/plan-and-act">
Explore what Cline can do.
</Card>
</CardGroup>
```
## Style Rules
Quick reference for consistent documentation:
| Do | Don't |
|---|---|
| Use "use" | Use "utilize" |
| Keep sentences under 25 words | Write run-on sentences |
| Use bullet points for lists | Write walls of text |
| Show where things are in the UI | Assume users can find features |
| Cross-link related docs | Leave readers stranded |
| Use code blocks with language tags | Use inline code for long snippets |
### Avoid These Patterns
- Em dashes and emojis
- Starting with "This document explains..."
- The **Bold Text**: description pattern
- Explaining obvious things
- Passive voice
## Previewing Changes
Run the docs locally to preview your changes:
```bash
cd docs
npm install # first time only
npm run dev
```
Open `http://localhost:3000` to see your changes in real time.
## Related Resources
<CardGroup cols={2}>
<Card title="Documentation Templates" icon="file-lines" href="/contributing/doc-templates">
Templates for different documentation types.
</Card>
<Card title="Workflows" icon="diagram-project" href="/customization/workflows">
Learn about Cline's workflow system.
</Card>
</CardGroup>
@@ -1,220 +0,0 @@
---
title: "Model Selection Guide"
description: "Choose the right AI model for your workflow based on reliability, speed, cost, and context window size."
---
New models drop constantly, so this guide focuses on what's working well with Cline right now. We'll keep it updated as the landscape shifts.
<Callout type="tip">
**New to model selection?** Start with [Module 2 of Cline's Learning Path](https://cline.bot/learn) for a comprehensive guide to choosing and configuring models.
</Callout>
## What is an AI Model?
Think of an AI model as the "brain" that powers Cline. When you ask Cline to write code, fix bugs, or refactor your project, it's the model that actually understands your request and generates the response.
**Key points:**
- **Models are trained AI systems** that understand natural language and code
- **Different models have different strengths** some excel at complex reasoning, others prioritize speed or cost
- **You choose which model Cline uses** like picking between different experts for different tasks
- **Models are accessed via API providers** - companies like Anthropic, OpenAI, and OpenRouter host these models
**Why it matters:** The model you choose directly impacts Cline's capabilities, response quality, speed, and cost. A premium model might handle complex refactoring beautifully but cost more, while a budget model works great for routine tasks at a fraction of the price.
## How to Select a Model in Cline
Follow these 5 simple steps to get Cline up and running with your preferred AI model:
### Step 1: Open Cline Settings
First, you need to access Cline's configuration panel.
**Two ways to open settings:**
- **Quick method**: Click the **gear icon (⚙️)** in the top-right corner of Cline's chat interface
- **Command palette**: Press **Cmd/Ctrl + Shift + P** → type "Cline: Open Settings"
<Frame>
<img src="https://storage.googleapis.com/cline_public_images/step1-config.png" alt="Cline Settings Panel" />
</Frame>
The settings panel will open, showing configuration options with "API Provider" at the top.
<Note>
The settings panel remembers your last configuration, so you'll only need to set this up once.
</Note>
### Step 2: Select an API Provider
Choose your preferred AI provider from the dropdown menu.
<Frame>
<img src="https://storage.googleapis.com/cline_public_images/step2-provider.png" alt="Cline Settings Panel" />
</Frame>
**Popular providers at a glance:**
| Provider | Best For | Notes |
|----------|----------|-------|
| **Cline** | Easiest setup | No API keys needed, access to multiple models including stealth models |
| **OpenRouter** | Value seekers | Multiple models, competitive pricing |
| **Anthropic** | Reliability | Claude models, most dependable tool usage |
| **OpenAI** | Latest tech | GPT-5, o3, o4-mini models |
| **OpenAI Codex** | ChatGPT subscribers | Use your ChatGPT subscription — no API key needed |
| **Google Gemini** | Large context | Gemini 3/2.5 with up to 2M context |
| **DeepSeek** | Budget reasoning | V3.2, R1 models at low cost |
| **Alibaba Qwen** | Open source coding | Qwen3 Coder with 1M context |
| **Moonshot** | Agentic coding | Kimi K2.5 with 262K context |
| **Cerebras** | Speed | Up to 2,600 tokens/sec |
| **AWS Bedrock** | Enterprise | Advanced features |
| **Ollama** | Privacy | Run models locally |
See the [full provider list](/getting-started/authorizing-with-cline) for all 30+ supported providers including xAI Grok, Mistral, Groq, Fireworks, Together, Baseten, SambaNova, Nebius, Hugging Face, and more.
<Info>
**Recommended for beginners:** Start with **Cline** as your provider - no API key management needed, instant access to multiple models, and occasional free inferencing through partner providers.
</Info>
### Step 3: Add Your API Key (or Sign In)
The next step depends on which provider you selected.
#### If you selected **Cline** as your provider:
- **No API key needed!** Simply sign in with your Cline account
- Click the **Sign In** button when prompted
- You'll be redirected to [app.cline.bot](https://app.cline.bot) to authenticate
- After signing in, return to your IDE
<Note>
For detailed information about the Cline authentication flow, OAuth tokens, and troubleshooting, see [Authorizing with Cline](/getting-started/authorizing-with-cline).
</Note>
#### If you selected **OpenAI Codex** as your provider:
- **No API key needed!** If you have a ChatGPT subscription (Plus, Pro, or Team), you can use it directly in Cline
- Click **"Sign in with OpenAI"** to authenticate via your browser
- Once authorized, all models available on your OpenAI plan will appear automatically
- Usage is governed by your ChatGPT subscription — no separate API billing
See the full [OpenAI Codex setup guide](/provider-config/openai-codex) for details.
#### If you selected any other provider:
You'll need to get an API key from your chosen provider:
1. **Visit your provider's website to get an API key:**
- **Anthropic**: [console.anthropic.com](https://console.anthropic.com/)
- **OpenRouter**: [openrouter.ai/keys](https://openrouter.ai/keys)
- **OpenAI**: [platform.openai.com/api-keys](https://platform.openai.com/api-keys)
- **Google**: [aistudio.google.com/apikey](https://aistudio.google.com/apikey)
- **Others**: See [Provider Setup Guide](/getting-started/authorizing-with-cline)
2. **Generate a new API key** on the provider's website
3. **Copy the API key** to your clipboard
4. **Paste your key** in the **"API Key"** field in Cline settings
5. **Save automatically** - Your key is stored securely in your editor's secrets storage
<Frame>
<img src="https://storage.googleapis.com/cline_public_images/step3-API.png" alt="Cline API Selection" />
</Frame>
<Warning>
**Payment required for most providers**: Most providers need payment information before generating keys. You only pay for what you use (typically $0.01-$0.10 per coding task).
</Warning>
### Step 4: Choose Your Model
Once your API key is added (or you've signed in), the **"Model"** dropdown becomes available.
<Frame>
<img src="https://storage.googleapis.com/cline_public_images/step4-model.png" alt="Cline Model Selection" />
</Frame>
**Quick model selection guide:**
| Your Priority | Choose This Model | Why |
|---------------|-------------------|-----|
| **Maximum reliability** | Claude Sonnet 4.5 | Most reliable tool usage, excellent at complex tasks |
| **Best value** | DeepSeek V3 or Qwen3 Coder | Great performance at budget prices |
| **Fastest speed** | Qwen3 Coder on Cerebras | Lightning-fast responses |
| **Run locally** | Any Ollama model | Complete privacy, no internet needed |
| **Latest features** | GPT-5 | OpenAI's newest capabilities |
Not sure which to pick? Start with **Claude Sonnet 4.5** for reliability or **DeepSeek V3** for value.
<Tip>
You can switch models at any time without losing your conversation. Try different models to find what works best for your specific tasks.
</Tip>
See the [model comparison tables](#current-top-models) below for detailed specifications and pricing.
### Step 5: Start Using Cline
**Congratulations! You're all set up.** Here's how to start coding with Cline:
1. **Type your request** in the Cline chat box
- Example: "Create a React component for a login form"
- Example: "Debug this TypeScript error"
- Example: "Refactor this function to be more efficient"
2. **Press Enter** or click the send icon to submit
## Choosing the Right Model
Selecting the right model involves balancing several factors. Use this framework to find your ideal match:
<Note>
**Pro tips**: Configure separate models for Plan Mode and Act Mode. Make the most out the each model's strengths. For example, use a budget model for planning discussions and a premium model for implementation.
</Note>
### Key Selection Factors
| Factor | What to Consider | Recommendation |
|--------|------------------|----------------|
| **Task Complexity** | Simple fixes vs complex refactoring | Budget models for routine tasks; Premium models for complex work |
| **Budget** | Monthly spending capacity | \$10-\$30: Budget, \$30-\$100: Mid-tier, \$100+: Premium |
| **Context Window** | Project size and file count | Small: 32K-128K, Medium: 128K-200K, Large: 400K+ |
| **Speed** | Response time requirements | Interactive: Fast models, Background: Reasoning models OK |
| **Tool Reliability** | Complex operations | Claude excels at tool usage; Test others with your workflow |
| **Provider** | Access and pricing needs | OpenRouter: Many options, Direct: Faster/reliable, Local: Privacy |
## Model Comparison Resources
For detailed model comparisons and performance metrics, see:
- [**Context Window Guide**](/model-config/context-windows) - Understanding and optimizing context usage
## Open Source vs Closed Source
### Open Source Advantages
- **Multiple providers** compete to host them
- **Cheaper pricing** due to competition
- **Provider choice** - switch if one goes down
- **Faster innovation** cycles
### Open Source Models Available
- **Qwen3 Coder** (Apache 2.0)
- **Z AI GLM 4.5** (MIT)
- **Kimi K2** (Open source)
- **DeepSeek series** (Various licenses)
## Quick Decision Matrix
| If you want... | Use this |
|----------------|----------|
| Something that just works | Claude Sonnet 4.5 |
| To save money | DeepSeek V3 or Qwen3 variants |
| Huge context windows | Gemini 2.5 Pro or Claude Sonnet 4.5 |
| Open source | Qwen3 Coder, Z AI GLM 4.5, or Kimi K2 |
| Latest tech | GPT-5 |
| To use your ChatGPT subscription | [OpenAI Codex](/provider-config/openai-codex) — sign in with your OpenAI account, no API key needed |
| Speed | Qwen3 Coder on Cerebras (fastest available) |
## What Others Are Using
Check [Vercel's leaderboard](https://vercel.com/ai-gateway/leaderboards) to see real usage patterns from the community.
+1 -1
View File
@@ -89,7 +89,7 @@ For complex tasks that need thorough analysis, use the `/deep-planning` slash co
3. Creates a detailed implementation plan
4. Asks clarifying questions before proceeding
The deep planning prompt is optimized for each model family, so it adapts to the strengths of whatever model you're using. See the [Deep Planning docs](/features/deep-planning) for more details.
The deep planning prompt is optimized for each model family, so it adapts to the strengths of whatever model you're using. See [/deep-planning](/core-workflows/using-commands#deep-planning) for more details.
## Choosing the Right Approach by Task Size
+12 -6
View File
@@ -1,7 +1,7 @@
---
title: "Using Commands"
sidebarTitle: "Using Commands"
description: "Built-in slash commands to manage context, plan implementations, and create reusable workflows."
description: "Built-in slash commands to manage context, plan implementations, and trigger reusable skills."
---
Cline provides slash commands in chat that help you manage your conversation and plan complex implementations.
@@ -37,7 +37,7 @@ Use `/smol` when you're deep into a debugging session or brainstorming and need
### /newrule
`/newrule` creates a rule file that teaches Cline your preferences. Cline will guide you through setting up guidelines for communication style, coding standards, project context, and workflows. The rule is saved to your `.clinerules` directory and automatically loaded for future conversations.
`/newrule` creates a rule file that teaches Cline your preferences. Cline will guide you through setting up guidelines for communication style, coding standards, project context, and reusable practices. The rule is saved to your `.clinerules` directory and automatically loaded for future conversations.
Use `/newrule` when you find yourself repeating the same instructions across tasks. For more about rules, see [Cline Rules](/customization/cline-rules).
@@ -50,7 +50,7 @@ Transform Cline into a meticulous architect who investigates your codebase, asks
3. **Plan Creation** - Generates `implementation_plan.md` with detailed specifications
4. **Task Creation** - Creates a new task with trackable implementation steps
Use `/deep-planning` for features touching multiple parts of your codebase, architectural changes, or complex integrations. For detailed documentation, see [Deep Planning](/features/deep-planning).
Use `/deep-planning` for features touching multiple parts of your codebase, architectural changes, or complex integrations.
### /explain-changes
@@ -68,8 +68,14 @@ Use `/explain-changes` when reviewing code, onboarding to a new codebase, or und
Use `/reportbug` when you encounter unexpected behavior, crashes, or bugs you want to report.
## Custom Workflows
## Skills via Slash Commands
Beyond the built-in slash commands, you can create your own workflow files that work the same way. Store Markdown files in `.clinerules/workflows/` and invoke them with `/your-workflow.md`.
In addition to built-in commands, you can trigger enabled skills directly from chat using slash commands.
For a complete guide on creating and managing custom workflows, see [Workflows](/customization/workflows).
- Type `/` to open command suggestions.
- Select a skill command (for example, `/aws-deploy`).
- Cline loads that skill and applies its `SKILL.md` instructions for the task.
Any enabled skill can be triggered this way, which gives you a fast path to skill-specific guidance without rewriting the same instructions each time.
For setup and management details, see [Skills](/customization/skills#triggering-skills-with-slash-commands).
+6 -66
View File
@@ -1,18 +1,14 @@
---
title: "Adding Context"
sidebarTitle: "Adding Context"
description: "Use @ mentions and drag & drop to bring files, terminal output, errors, git changes, and web content into your conversations."
description: "Use @ mentions and drag & drop to bring files into your conversations."
---
Cline works best when it has the right context, not just more context. @ mentions let you pull in exactly the files, errors, terminal output, or documentation that matter for your task. No copying, no pasting, no context switching.
Cline works best when it has the right context, not just more context. `@` mentions let you pull in the files and folders that matter for your task — no copying, no pasting, no context switching.
You can add context two ways:
- Type `@` in the chat input and select what you want
- Click the **+** button in the bottom left to browse files, images, or mentions
<Tip>
**Want to learn more about managing context?** Watch [Adding Context with @ Mentions](https://youtu.be/7j6R75Dvj1Y) to see it in action.
</Tip>
- Type `@` in the chat input and select a file or folder
- Click the **+** button in the bottom left to browse files or images
## Quick Reference
@@ -20,11 +16,8 @@ You can add context two ways:
|---------------|--------|---------|
| File content | `@/path/to/file` | `@/src/index.ts` |
| Folder contents | `@/path/to/folder/` | `@/src/components/` |
| Workspace errors | `@problems` | `@problems` |
| Terminal output | `@terminal` | `@terminal` |
| Uncommitted changes | `@git-changes` | `@git-changes` |
| Specific commit | `@<commit-hash>` | `@a1b2c3d` |
| Web page | `@<url>` | `@https://react.dev/learn` |
For other context — git history, web pages, terminal errors — just describe it. Cline will run `git log`, fetch the URL, or read the output itself.
## File Mentions
@@ -46,59 +39,6 @@ Explain how the components in @/src/components/auth/ work together.
In multi-root workspaces, prefix paths with the workspace name: `@workspace-name:/path/to/file`
</Note>
## Problem Mentions
Use `@problems` to share all errors and warnings from your workspace's Problems panel.
```text
@problems Can you fix these TypeScript errors?
```
## Terminal Mentions
Use `@terminal` to share recent terminal output. Perfect for debugging build errors or test failures.
```text
@terminal The build is failing. What's wrong?
```
## Git Mentions
Reference uncommitted changes with `@git-changes`:
```text
@git-changes Review my changes before I commit.
```
Reference specific commits with `@<commit-hash>` (7-40 character hex):
```text
What did @a1b2c3d change?
```
## URL Mentions
Reference web content with `@https://example.com`. Cline fetches the page content.
```text
Implement the pattern described in @https://react.dev/learn/scaling-up-with-reducer-and-context
```
## Combining Mentions
Combine multiple @ mentions for comprehensive context:
```text
I'm getting these errors: @problems
Here's my component: @/src/components/Form.jsx
And the API endpoint: @/src/api/users.js
The error happens when I submit: @terminal
I think this commit might have caused it: @a1b2c3d
```
## Drag & Drop
Drag files directly into the chat input to add them to your conversation.
+1 -1
View File
@@ -51,7 +51,7 @@ your-project/
Cline processes all `.md` and `.txt` files inside `.clinerules/`, combining them into a unified set of rules. Numeric prefixes (like `01-coding.md`) help organize files but are optional.
When both workspace and global rules exist, Cline combines them. Workspace rules take precedence when they conflict with global rules. See [Storage Locations](/customization/overview#storage-locations) for more guidance.
When both workspace and global rules exist, Cline combines them. Workspace rules take precedence when they conflict with global rules. See [Storage Locations](/getting-started/config#storage-locations) for more guidance.
### Global Rules Directory
-1
View File
@@ -107,4 +107,3 @@ You can still reference ignored files explicitly using [@ mentions](/core-workfl
- [Cline Rules](/customization/cline-rules) - Define persistent instructions for Cline
- [Task Management](/core-workflows/task-management#context-window) - Understand how context windows work
- [Auto-Compact](/features/auto-compact) - Automatic context compression during long tasks
- [Memory Bank](/features/memory-bank) - Structured documentation for cross-session context
+2 -504
View File
@@ -1,509 +1,7 @@
---
title: "Hooks"
sidebarTitle: "Hooks"
description: "Inject custom logic into Cline's workflow to validate operations and shape Cline's decisions."
description: "See details under SDK Hooks page."
---
Hooks are scripts that run at key moments in Cline's workflow. Because they execute at known points with consistent inputs and outputs, hooks bring determinism to the non-deterministic nature of AI models by enforcing guardrails, validations, and context injection. You can validate operations before they execute, monitor tool usage, and shape how Cline makes decisions.
## What You Can Build
- Stop operations before they cause problems (like creating `.js` files in a TypeScript project)
- Run linters or custom validators before files get saved
- Prevent operations that violate security policies
- Track everything for analytics or compliance
- Trigger external tools or services at the right moments
- Add context to the conversation based on what Cline is doing
## Hook Types
Cline supports 8 hook types that run at different points in the task lifecycle:
| Hook Type | When It Runs |
|-----------|--------------|
| TaskStart | When you start a new task |
| TaskResume | When you resume an interrupted task |
| TaskCancel | When you cancel a running task |
| TaskComplete | When a task finishes successfully |
| PreToolUse | Before Cline executes a tool (read_file, write_to_file, etc.) |
| PostToolUse | After a tool execution completes |
| UserPromptSubmit | When you submit a message to Cline |
| PreCompact | Before Cline truncates conversation history to free up context |
## Hook Lifecycle
```mermaid
flowchart TD
%% Styling
classDef hook fill:#FFB74D,stroke:#E65100,stroke-width:2px,color:black,rx:5,ry:5;
classDef state fill:#E1F5FE,stroke:#0277BD,stroke-width:2px,color:black;
classDef action fill:#FFFFFF,stroke:#333,stroke-width:1px,color:black,stroke-dasharray: 5 5;
%% Entry Points
Start((Start)) --> CheckType{New or<br/>Resume?}
%% Initialization Hooks
CheckType -- New Task --> H_Start[TaskStart]:::hook
CheckType -- Resume --> H_Resume[TaskResume]:::hook
%% Main Loop
H_Start --> Loop(Task Active Loop):::state
H_Resume --> Loop
subgraph Conversation Cycle
direction TB
Loop -- User sends message --> H_Submit[UserPromptSubmit]:::hook
H_Submit --> Thinking[Cline Processes Context]:::state
%% Context Compaction Path
Thinking -. Context Limit Reached .-> H_Compact[PreCompact]:::hook
H_Compact -.-> Thinking
%% Tool Execution Path
Thinking -- Decides to use tool --> H_PreTool[PreToolUse]:::hook
H_PreTool -- Allowed --> ToolExec[Tool Executes]:::action
H_PreTool -- Cancelled --> Thinking
ToolExec --> H_PostTool[PostToolUse]:::hook
H_PostTool --> Thinking
end
%% Termination Paths
Thinking -- Task Successfully Finished --> H_Complete[TaskComplete]:::hook
Loop -- User Cancels Task --> H_Cancel[TaskCancel]:::hook
%% End
H_Complete --> End((End))
H_Cancel --> End
```
The diagram shows the complete hook lifecycle:
1. **Entry**: When you start a task, either **TaskStart** (new task) or **TaskResume** (interrupted task) runs first
2. **Conversation Cycle**: Each time you send a message, **UserPromptSubmit** runs, then Cline processes your request
3. **Tool Execution**: When Cline decides to use a tool, **PreToolUse** runs first-if allowed, the tool executes, then **PostToolUse** runs
4. **Context Management**: If the conversation approaches context limits, **PreCompact** runs before truncation
5. **Exit**: The task ends with either **TaskComplete** (success) or **TaskCancel** (user cancellation)
Orange nodes represent hooks where you can inject custom logic. The cycle repeats as you continue the conversation.
## Hook Locations
Hooks can be stored globally or in a project workspace. See [Storage Locations](/customization/overview#storage-locations) for guidance on when to use each.
- **Global hooks**: `~/Documents/Cline/Hooks/`
- **Project hooks**: `.clinerules/hooks/` in your repo (can be committed to version control)
When both global and workspace hooks exist for the same hook type, both run. Global hooks execute first, then workspace hooks. If either returns `cancel: true`, the operation stops.
## Creating a Hook
<Steps>
<Step title="Open the Hooks tab">
Click the scale icon at the bottom of the Cline panel, to the left of the model selector. Switch to the Hooks tab.
</Step>
<Step title="Create a new hook">
Click **"New hook..."** dropdown and select a hook type (e.g., PreToolUse, TaskStart).
</Step>
<Step title="Review the hook's code">
Click the pencil icon to open and edit the hook script. Cline generates a template with examples.
</Step>
<Step title="Enable the hook">
Toggle the switch to activate the hook once you understand what it does.
</Step>
</Steps>
<Warning>
Always review a hook's code before enabling it. Hooks execute automatically during your workflow and can block operations or run shell commands.
</Warning>
## Quick Start: Your First Hook
Let's create a simple hook that logs every file Cline reads or writes. You'll see results in seconds.
### The Hook
Create a file called `file-logger` in your hooks directory with this content:
```bash
#!/bin/bash
# Logs all file operations to ~/cline-activity.log
INPUT=$(cat)
TOOL=$(echo "$INPUT" | jq -r '.preToolUse.tool')
FILE_PATH=$(echo "$INPUT" | jq -r '.preToolUse.parameters.path // "N/A"')
# Log to file
echo "$(date '+%H:%M:%S') - $TOOL: $FILE_PATH" >> ~/cline-activity.log
# Always allow the operation
echo '{"cancel":false}'
```
### Setup
<Steps>
<Step title="Create the hook file">
Save the script above as `~/Documents/Cline/Hooks/file-logger` or create it through the Hooks UI.
</Step>
<Step title="Make it executable">
On macOS/Linux, run `chmod +x ~/Documents/Cline/Hooks/file-logger`.
</Step>
<Step title="Enable it (macOS/Linux only)">
In Cline's Hooks tab, find "file-logger" under PreToolUse hooks and toggle it on.
</Step>
</Steps>
<Note>
On Windows, hooks are executed with PowerShell and run whenever the hook file exists. In this
foundation PR, hook enable/disable toggling is not yet supported on Windows.
</Note>
<Note>
Coming next: JSON-backed hook enabled/disabled state across platforms, so toggle behavior is
consistent on Windows, macOS, and Linux.
</Note>
<Note>
Hook filenames are platform-specific:
- **Windows**: only `HookName.ps1` is supported (PowerShell script files)
- **macOS/Linux**: only extensionless `HookName` is supported (executable files like bash scripts or binaries)
Wrong-platform naming is ignored by hook discovery.
</Note>
### Test It
Ask Cline to read any file in your project: "What's in package.json?"
Then check the log:
```bash
cat ~/cline-activity.log
```
You'll see entries like:
```text
14:23:45 - read_file: /path/to/package.json
14:23:47 - search_files: /path/to/src
```
### Customize It
Try modifying the hook to:
- Filter specific file types (only log `.ts` files)
- Add the task ID to each log entry
- Send notifications for write operations
- Block operations on certain paths
The sections below explain how hooks receive input and return output, plus more examples.
## How Hooks Work
Hooks are executable scripts that receive JSON input via stdin and return JSON output via stdout.
### Input Structure
Every hook receives a JSON object with common fields plus hook-specific data:
```json
{
"taskId": "abc123",
"hookName": "PreToolUse",
"clineVersion": "3.17.0",
"timestamp": "1736654400000",
"workspaceRoots": ["/path/to/project"],
"userId": "user_123",
"model": {
"provider": "openrouter",
"slug": "anthropic/claude-sonnet-4.5"
},
// Hook-specific field (name matches hook type in camelCase)
"taskStart": {
"task": "Add authentication to the API"
}
}
```
`model.provider` and `model.slug` are machine-stable identifiers for the active provider/model at hook execution time. If unavailable, Cline sends deterministic fallback values: `"unknown"`.
<Note>
Migration note for existing hook scripts:
- `timestamp` is a string (milliseconds since epoch), not a number
- `workspaceRoots` is an array of workspace root paths and replaces the old singular `workspacePath`
If your scripts previously read `.workspacePath`, switch to `.workspaceRoots[0]` (or iterate all roots).
</Note>
The hook-specific field name matches the hook type:
- `taskStart`, `taskResume`, `taskCancel`, `taskComplete` contain `{ task: string }`
- `preToolUse` contains `{ tool: string, parameters: object }`
- `postToolUse` contains `{ tool: string, parameters: object, result: string, success: boolean, durationMs: number }`
- `userPromptSubmit` contains `{ prompt: string }`
- `preCompact` contains `{ conversationLength: number, estimatedTokens: number }`
### Output Structure
Hooks return a JSON object to stdout:
```json
{
"cancel": false,
"contextModification": "Optional text to add to the conversation",
"errorMessage": ""
}
```
| Field | Type | Description |
|-------|------|-------------|
| `cancel` | boolean | If `true`, stops the operation (blocks the tool, cancels the task start, etc.) |
| `contextModification` | string | Optional text that gets injected into the conversation as context for Cline |
| `errorMessage` | string | Shown to the user if `cancel` is `true` |
### Context Modification
The `contextModification` field lets hooks inject information into the conversation. This is useful for:
- Adding project-specific context when a task starts
- Providing validation results that Cline should consider
- Injecting environment information before tool execution
For example, a PreToolUse hook could add: `"Note: This file is auto-generated. Edits may be overwritten."`
## Hook Reference
### Task Lifecycle Hooks
#### TaskStart
Runs when you start a new task. Use it to:
- Log task start time for analytics
- Add project context to the conversation
- Check prerequisites before work begins
- Notify external systems (Slack, issue trackers)
```bash
#!/bin/bash
INPUT=$(cat)
TASK=$(echo "$INPUT" | jq -r '.taskStart.task')
echo "[TaskStart] Starting: $TASK" >&2
echo '{"cancel":false,"contextModification":"","errorMessage":""}'
```
#### TaskResume
Runs when you resume an interrupted task (instead of TaskStart). Use it to:
- Check for changes since the task was paused
- Refresh context with latest project state
- Notify that work is resuming
#### TaskCancel
Runs when you cancel a running task. Use it to:
- Clean up temporary files or resources
- Notify external systems about cancellation
- Log cancellation for analytics
#### TaskComplete
Runs when a task completes successfully. Use it to:
- Run tests or validation after changes
- Generate reports or summaries
- Notify stakeholders
- Trigger CI/CD pipelines
### Tool Hooks
#### PreToolUse
Runs before any tool executes. This is the most powerful hook for validation and safety. Use it to:
- Block dangerous operations
- Validate parameters before execution
- Add context about the file or resource being accessed
- Log tool usage
The input includes the tool name and its parameters:
```json
{
"preToolUse": {
"tool": "write_to_file",
"parameters": {
"path": "src/config.ts",
"content": "..."
}
}
}
```
Example that blocks `.js` files in a TypeScript project:
```bash
#!/bin/bash
INPUT=$(cat)
TOOL=$(echo "$INPUT" | jq -r '.preToolUse.tool')
FILE_PATH=$(echo "$INPUT" | jq -r '.preToolUse.parameters.path // empty')
if [[ "$TOOL" == "write_to_file" && "$FILE_PATH" == *.js ]]; then
echo '{"cancel":true,"errorMessage":"Use .ts files instead of .js in this TypeScript project"}'
exit 0
fi
echo '{"cancel":false}'
```
#### PostToolUse
Runs after a tool completes (success or failure). Use it to:
- Audit tool usage
- Validate results
- Trigger follow-up actions
- Monitor performance
The input includes execution results:
```json
{
"postToolUse": {
"tool": "execute_command",
"parameters": { "command": "npm test" },
"result": "All tests passed",
"success": true,
"durationMs": 3450
}
}
```
<Note>
PostToolUse hooks can return `cancel: true` to stop the task, but they cannot undo the tool execution that already happened.
</Note>
### Other Hooks
#### UserPromptSubmit
Runs when you send a message to Cline. Use it to:
- Log prompts for analytics
- Add context based on prompt content
- Validate or sanitize prompts
#### PreCompact
Runs before Cline truncates conversation history to stay within context limits. Use it to:
- Archive important conversation parts before they're removed
- Log compaction events
- Add a summary of what's being removed
The input includes context metrics:
```json
{
"preCompact": {
"conversationLength": 45,
"estimatedTokens": 125000
}
}
```
## Examples
### TypeScript Enforcement
Block creation of `.js` files in a TypeScript project:
```bash
#!/bin/bash
# PreToolUse hook
INPUT=$(cat)
TOOL=$(echo "$INPUT" | jq -r '.preToolUse.tool')
FILE_PATH=$(echo "$INPUT" | jq -r '.preToolUse.parameters.path // empty')
if [[ "$TOOL" == "write_to_file" && "$FILE_PATH" == *.js ]]; then
echo '{"cancel":true,"errorMessage":"Use .ts files instead of .js in this TypeScript project"}'
exit 0
fi
echo '{"cancel":false}'
```
### Tool Usage Logging
Log all tool executions to a file:
```bash
#!/bin/bash
# PostToolUse hook
INPUT=$(cat)
TOOL=$(echo "$INPUT" | jq -r '.postToolUse.tool')
SUCCESS=$(echo "$INPUT" | jq -r '.postToolUse.success')
DURATION=$(echo "$INPUT" | jq -r '.postToolUse.durationMs')
echo "$(date -Iseconds) | $TOOL | success=$SUCCESS | ${DURATION}ms" >> ~/.cline-tool-log.txt
echo '{"cancel":false}'
```
### Add Project Context on Task Start
Inject project-specific information when a task begins:
```bash
#!/bin/bash
# TaskStart hook
INPUT=$(cat)
WORKSPACE=$(echo "$INPUT" | jq -r '.workspaceRoots[0] // empty')
# Read project info if available
if [[ -f "$WORKSPACE/.project-context" ]]; then
CONTEXT=$(cat "$WORKSPACE/.project-context")
echo "{\"cancel\":false,\"contextModification\":\"Project context: $CONTEXT\"}"
else
echo '{"cancel":false}'
fi
```
## CLI Support
Hooks are available in the [Cline CLI](/cline-cli/getting-started):
```bash
# Enable hooks for a task
cline "What does this repo do?" -s hooks_enabled=true
# Configure hooks globally
cline config set hooks-enabled=true
```
<Note>
Windows hooks require PowerShell (`powershell.exe`) available on your PATH.
</Note>
## Troubleshooting
**Hook not running?**
- On macOS/Linux, check that the file is executable (`chmod +x hookname`)
- On Windows, ensure PowerShell is available (`powershell -NoProfile -Command "$PSVersionTable.PSVersion"`)
- On Windows, ensure the hook file is named `<HookName>.ps1` (for example `PreToolUse.ps1`)
- On macOS/Linux, ensure the hook file uses extensionless `<HookName>` naming (for example `PreToolUse`)
- On macOS/Linux, verify the hook is enabled (toggle is on in the Hooks tab)
- Check that Hooks are enabled globally in Settings
**Hook output not parsed?**
- Ensure output is valid JSON on a single line to stdout
- Use stderr (`>&2`) for debug logging, not stdout
- Check for trailing characters or newlines before the JSON
**Hook blocking unexpectedly?**
- Review the hook's logic and test with sample input
- Check both global and workspace hooks (both run if they exist)
## Related Features
- [Rules](/customization/cline-rules) define high-level guidance that hooks can enforce programmatically
- [Checkpoints](/core-workflows/checkpoints) let you roll back if a hook didn't catch an issue
- [Auto-Approve](/features/auto-approve) works well with hooks as safety nets
See details under [SDK Plugins](/sdk/plugins).
-86
View File
@@ -1,86 +0,0 @@
---
title: "Overview"
sidebarTitle: "Overview"
description: "Understand how Rules, Skills, Workflows, Hooks, and .clineignore work together to customize Cline."
---
Out of the box, Cline is a general-purpose AI assistant. Customizations transform it into an expert on your codebase, your team's conventions, and your workflows. Instead of repeating the same instructions every task, you define them once and Cline follows them automatically.
Cline offers five systems for this: Rules, Skills, Workflows, Hooks, and .clineignore. Each serves a different purpose and activates at different times.
## Quick Comparison
| Feature | Purpose | When Active | Best For |
|---------|---------|-------------|----------|
| **[Rules](/customization/cline-rules)** | Define how Cline behaves | Always (or contextually) | Coding standards, project constraints, team conventions |
| **[Skills](/customization/skills)** | Domain expertise loaded on-demand | Triggered by matching requests | Specialized knowledge, complex procedures, institutional expertise |
| **[Workflows](/customization/workflows)** | Step-by-step task automation | Invoked with `/workflow.md` | Repetitive processes, release procedures, setup scripts |
| **[Hooks](/customization/hooks)** | Inject custom logic at key moments | Automatically on specific events | Validation, enforcement, monitoring, automation triggers |
| **[.clineignore](/customization/clineignore)** | Control file access | Always | Excluding dependencies, build artifacts, large data files |
## Understanding Each Tool
**[Rules](/customization/cline-rules)** are always-on guidance. Use them when you want Cline to consistently follow certain patterns: coding standards, naming conventions, architectural constraints, or project-specific context. Rules shape *how* Cline works across all tasks. For example, a rule might say "always use TypeScript" or "follow the repository pattern for data access."
**[Skills](/customization/skills)** are domain expertise that loads only when relevant. Use them when you have extensive knowledge that would waste context if always active. Cline sees skill descriptions at startup and activates the full instructions only when your request matches. A data analysis skill might include pandas patterns, visualization preferences, and output formats that Cline only loads when you're working with data files.
**[Workflows](/customization/workflows)** are explicit task scripts you invoke on demand. Use them when you have a repeatable multi-step process that should run the same way every time. Type `/release.md` and Cline executes your release sequence: bump version, run tests, update changelog, commit, tag, push. Workflows define *what* to do, step by step.
**[Hooks](/customization/hooks)** are programmatic guardrails that run automatically at key moments. Use them when you need to validate, enforce, or extend Cline's behavior with custom code. A hook might block `.js` file creation in a TypeScript project, run linters before saves, or notify external services after deployments.
**[.clineignore](/customization/clineignore)** controls which files and directories Cline can access. Use it to exclude dependencies, build artifacts, generated files, and large data files from Cline's context. This reduces token usage, lowers costs, and keeps Cline focused on the code that matters. It works like `.gitignore`: add patterns to a `.clineignore` file in your project root and matching files are automatically excluded.
### Example: A Release Process
Consider how all five work together for releasing a new version:
1. **Rules** ensure Cline follows your team's commit message format and versioning policy
2. **Skills** offer deep knowledge about your CI/CD system that Cline loads when deployment questions arise
3. **Workflows** provide the explicit `/release.md` sequence: bump version, update changelog, tag, push
4. **Hooks** validate that tests pass before allowing any commit or that the changelog was actually updated
5. **.clineignore** keeps build artifacts, `node_modules/`, and generated files out of Cline's context so it stays focused
## Storage Locations
All five systems support both global and project-specific configurations:
| System | Global Location | Project Location |
|--------|-----------------|------------------|
| Rules | `~/Documents/Cline/Rules/` | `.clinerules/` |
| Skills | `~/.cline/skills/` | `.cline/skills/` |
| Workflows | `~/Documents/Cline/Workflows/` | `.clinerules/workflows/` |
| Hooks | `~/Documents/Cline/Hooks/` | `.clinerules/hooks/` |
| .clineignore | N/A | `.clineignore` |
### When to Use Each
**Start with project storage.** Most customizations belong in your project's directory because they're tied to that specific codebase. Team coding standards, deployment workflows, and architectural constraints all live with the code they describe. This also means your customizations travel with the repository, so collaborators get them automatically and changes can be reviewed in pull requests.
**Use global storage for personal preferences.** If you find yourself adding the same customization to every project, move it to global storage. Your preferred communication style, personal productivity workflows, and tools you use everywhere belong here. Global customizations apply to all projects but stay out of version control, so they won't affect your teammates.
When names conflict, project-specific configurations take precedence (except for Skills, where global takes precedence). This lets you override global defaults for specific projects when needed.
## Security Considerations
<Warning>
Always review customizations before adding them to your projects. Only use customizations from sources you trust.
</Warning>
Customizations are powerful. They shape how Cline writes code, execute commands automatically, and influence every interaction. Treat customization files with the same scrutiny you'd give any code running in your environment.
### Best Practices
Review any customization file before adding it to your project or global configuration. Understand what it does and why.
When downloading customizations from GitHub repositories, community shares, or other external sources, verify the source:
- Is the author reputable?
- Has the community reviewed it?
- Does the code do what it claims?
Look for dangerous commands:
- Shell commands that delete files (`rm`, `del`)
- Commands that transmit data (`curl`, `wget` with POST)
- File operations outside your project directory
- Commands that modify system configuration
Keep your customizations in version control so you can track changes, review diffs, and roll back if something goes wrong. When creating hooks, use the most restrictive event triggers necessary. Don't run hooks on every file save if you only need them before commits.
+138
View File
@@ -0,0 +1,138 @@
---
title: "Plugins"
sidebarTitle: "Plugins"
description: "Install and manage plugins that extend Cline with custom tools, hooks, and capabilities."
---
<Warning>
This feature currently only applies to Cline SDK, CLI, and Kanban. This feature is not applicable on VSCode and JetBrains Extension for now.
</Warning>
Plugins extend Cline with custom tools, lifecycle hooks, slash commands, and more. They can be installed globally (available in all sessions) or per-project.
## Installing Plugins via CLI
The `cline plugin install` command installs plugins from three source types:
<Tabs>
<Tab title="Git Repository">
```bash
cline plugin install https://github.com/owner/repo.git
cline plugin install git@github.com:owner/repo.git
```
The installer clones the repository, installs production dependencies, and registers the plugin entry files.
To install a specific branch or tag, append `@ref`:
```bash
cline plugin install https://github.com/owner/repo.git@v1.2.0
cline plugin install https://github.com/owner/repo.git@main
```
</Tab>
<Tab title="npm Package">
```bash
cline plugin install npm:@scope/my-plugin
cline plugin install --npm my-plugin
```
</Tab>
<Tab title="Local Path">
```bash
cline plugin install ./my-plugin
cline plugin install ~/plugins/my-tool
cline plugin install /absolute/path/to/plugin.ts
```
Local installs copy the file or directory into the plugin store. Both single `.ts`/`.js` files and directories with a `package.json` are supported.
</Tab>
</Tabs>
Additional flags:
| Flag | Description |
|------|-------------|
| `--force` | Replace an existing install for the same source |
| `--json` | Output the result as JSON (useful for scripting) |
| `--cwd <path>` | Install to `<path>/.cline/plugins` instead of the global directory |
After installation, confirm the plugin is loaded by running `cline config` and checking the plugin tab.
### Example: TypeScript Navigation Plugin
The [typescript-lsp-plugin](https://github.com/cline/typescript-lsp-plugin) is a good reference for how plugins work. It adds a `goto_definition` tool that uses the TypeScript Language Service API to resolve symbol definitions through imports, re-exports, and type aliases.
Install it with:
```bash
cline plugin install https://github.com/cline/typescript-lsp-plugin.git
```
Once installed, Cline can call `goto_definition` with a file path and line number to find where symbols are defined, which is much more precise than text search.
## Plugin Manifest Format
For a repository or npm package to be installable as a Cline plugin, its `package.json` should include a `cline` field that declares plugin entry points:
```json
{
"name": "my-cline-plugin",
"version": "1.0.0",
"cline": {
"plugins": [
{
"paths": ["./index.ts"],
"capabilities": ["tools", "hooks"]
}
]
}
}
```
The `cline.plugins` array accepts:
| Format | Example |
|--------|---------|
| Object with `paths` array | `{ "paths": ["./src/plugin.ts"], "capabilities": ["tools"] }` |
| Plain string | `"./index.ts"` |
Each path should point to a `.ts` or `.js` file that exports an `AgentExtension` (either as the default export or a named export).
If no `cline.plugins` field is present, the installer falls back to auto-discovery: it looks for standard entry points, then recursively scans for `.ts` and `.js` files (skipping `node_modules` and `.git`).
### Host-Provided Dependencies
Dependencies under the `@cline/` scope (like `@cline/core`, `@cline/shared`) are provided by the host runtime. The installer automatically strips these from the plugin's dependency list before running `npm install`, so you should declare them as `peerDependencies`:
```json
{
"peerDependencies": {
"@cline/core": "*"
},
"peerDependenciesMeta": {
"@cline/core": {
"optional": true
}
}
}
```
## Plugin Directory Structure
Plugins are stored in the `plugins` directory at two levels:
```
~/.cline/
plugins/ # Global plugins
_installed/ # Managed by `cline plugin install`
npm/ # npm-sourced plugins
git/ # git-sourced plugins
local/ # local-sourced plugins
.cline/ # Project root
plugins/ # Project-scoped plugins
```
Global plugins (`~/.cline/plugins/`) are available across all sessions. Project plugins (`.cline/plugins/` in your repo) are available only when working in that project.
## Writing Plugins
For a guide on building plugins with the SDK, see [Writing Plugins](/sdk/guides/writing-plugins). For the plugin API reference, see [SDK Plugins](/sdk/plugins).
+14 -4
View File
@@ -4,7 +4,7 @@ sidebarTitle: "Skills"
description: "Modular instruction sets that extend Cline's capabilities for specific tasks."
---
Skills are modular instruction sets that extend Cline's capabilities for specific tasks. Each skill packages detailed guidance, workflows, and optional resources that Cline loads only when relevant to your request.
Skills are modular instruction sets that extend Cline's capabilities for specific tasks. Each skill packages detailed guidance, processes, and optional resources that Cline loads only when relevant to your request.
Install multiple skills and Cline only loads what it needs. A deployment skill stays dormant until you ask about deploying. Unlike [rules](/customization/cline-rules) (which are always active), skills load on-demand so they don't consume context when you're working on something unrelated.
@@ -24,6 +24,16 @@ Skills use progressive loading to maximize efficiency:
When you send a message, Cline sees a list of available skills with their descriptions. If your request matches a skill's description, Cline activates it using the `use_skill` tool, which loads the full instructions from SKILL.md.
## Triggering Skills with Slash Commands
You can also invoke enabled skills explicitly from the chat input using slash commands.
1. Type `/` in chat to open command suggestions.
2. Select the skill command you want to run (for example, `/aws-deploy`).
3. Cline triggers that skill and loads its `SKILL.md` instructions.
This is useful when you want to force a specific skill immediately instead of waiting for auto-matching based on description.
## Skill Structure
Every skill is a directory containing a `SKILL.md` file with YAML frontmatter.
@@ -138,7 +148,7 @@ Include real examples. Show what commands to run, what output to expect, and wha
## Where Skills Live
Skills can be stored globally or in a project workspace. See [Storage Locations](/customization/overview#storage-locations) for guidance on when to use each.
Skills can be stored globally or in a project workspace. See [Storage Locations](/getting-started/config#storage-locations) for guidance on when to use each.
Project skills:
- `.cline/skills/` (recommended)
@@ -215,7 +225,7 @@ Cline reads documentation files using `read_file` when the instructions referenc
| Use Scripts For | Use Instructions For |
|-----------------|---------------------|
| Deterministic operations (validation, formatting) | Flexible guidance that adapts to context |
| Complex computations | Decision-making workflows |
| Complex computations | Decision-making processes |
| Operations that need reliability | Steps that might vary by situation |
| Anything you'd rather not consume tokens explaining | Best practices and patterns |
@@ -231,7 +241,7 @@ description: Analyze data files and generate insights. Use when working with CSV
# Data Analysis
When analyzing data files, follow this workflow:
When analyzing data files, follow this process:
## 1. Understand the Data
- Read a sample of the file to understand its structure
-221
View File
@@ -1,221 +0,0 @@
---
title: "Workflows"
sidebarTitle: "Workflows"
description: "Automate repetitive tasks with Markdown-based workflow files."
---
Workflows are Markdown files that define a series of steps to guide Cline through repetitive or complex tasks. Type `/` followed by the workflow's filename to invoke it (e.g., `/deploy.md`).
Deploying, setting up a new project, running through a release checklist: these tasks often require remembering a dozen steps, running commands in the right order, and updating files manually. Mess up one step and you're debugging for an hour. Workflows turn those multi-step processes into one command. Type `/release.md` and Cline handles the version bump, runs tests, updates the changelog, commits, tags, and pushes. You just review and approve.
## Workflow Structure
A workflow is a markdown file with a title and steps. The filename becomes the command: `demo-workflow.md` is invoked with `/demo-workflow.md`.
````markdown title="demo-workflow.md"
# Demo Workflow
Brief description of what this workflow accomplishes.
## Step 1: Check prerequisites
Verify the environment is ready. Look for required tools and dependencies.
## Step 2: Run the build
Execute the build command:
```bash
npm run build
```
## Step 3: Verify results
Check that the build completed successfully and report any issues.
````
Steps can be written at different levels of detail:
- **High-level**: "Run the test suite and fix any failures" lets Cline decide how to accomplish the goal
- **Specific**: Use XML tool syntax or exact commands when you need precise control
## Creating Workflows
<Steps>
<Step title="Open the Workflows menu">
Click the scale icon at the bottom of the Cline panel, to the left of the model selector. Switch to the Workflows tab.
</Step>
<Step title="Create a new workflow file">
Click "New workflow file..." and enter a filename (e.g., `deploy`). The file will be created with a `.md` extension.
</Step>
<Step title="Write your workflow">
Add a title and numbered steps in markdown format. Describe what each step should accomplish.
</Step>
</Steps>
<Tip>
**Create workflows from completed tasks.** After finishing something you'll need to repeat, tell Cline: "Create a workflow for the process I just completed." Cline analyzes the conversation, identifies the steps, and generates the workflow file. Your accumulated context becomes reusable automation.
</Tip>
### Invoking Workflows
Type `/` in the chat input to see available workflows. Cline shows autocomplete suggestions as you type, so `/rel` would match `release-prep.md`. Select a workflow and press Enter to start it.
Cline executes each step in sequence, pausing for your approval when needed. You can stop a workflow at any point by rejecting a step.
### Toggling Workflows
Every workflow has a toggle to enable or disable it. This lets you control which workflows appear in the `/` menu without deleting the file.
## Where Workflows Live
Workflows can be stored in two locations: your project workspace or globally on your system.
**Workspace workflows** go in `.clinerules/workflows/` at your project root. Use these for project-specific automation like deployment scripts, release processes, or setup procedures that your team shares.
**Global workflows** go in your system's Cline Workflows directory. Use these for personal productivity workflows you use across all projects.
### Global Workflows Directory
| Operating System | Default Location |
|------------------|------------------|
| Windows | `Documents\Cline\Workflows` |
| macOS | `~/Documents/Cline/Workflows` |
| Linux/WSL | `~/Documents/Cline/Workflows` |
Workspace workflows take precedence when names match global workflows. See [Storage Locations](/customization/overview#storage-locations) for more guidance.
## What Workflows Can Use
Workflows can combine natural language instructions with specific tool calls. This flexibility lets you write workflows that are as simple or as precise as your task requires.
### Natural Language
Write steps as plain instructions. Cline interprets them and figures out which tools to use:
```markdown
## Step 1: Check for uncommitted changes
Look at the git status. If there are uncommitted changes, ask whether to continue or abort.
## Step 2: Run the test suite
Execute all tests. If any fail, show the failures and stop.
```
This approach works well when you want Cline to adapt to the situation rather than follow rigid steps.
### Cline Tools
For precise control, use Cline's built-in tools with XML syntax. This guarantees specific actions:
```xml
<execute_command>
<command>npm run test</command>
<requires_approval>false</requires_approval>
</execute_command>
```
```xml
<read_file>
<path>src/config.json</path>
</read_file>
```
```xml
<ask_followup_question>
<question>Deploy to production or staging?</question>
<options>["Production", "Staging", "Cancel"]</options>
</ask_followup_question>
```
See the full list in the [Cline Tools Reference](/tools-reference/all-cline-tools).
### CLI Tools
Reference any command-line tool installed on your machine. Git, npm, docker, gh, make, curl: whatever you have available.
```bash
git log --author="$(git config user.name)" --since="yesterday" --oneline
```
### MCP Tools
If you have [MCP servers](/mcp/mcp-overview) connected, use them in your workflows with the `use_mcp_tool` syntax. This lets you integrate with external services like GitHub, Slack, databases, or custom internal tools.
```xml
<use_mcp_tool>
<server_name>github-server</server_name>
<tool_name>create_release</tool_name>
<arguments>{"tag": "v1.2.0", "name": "Release v1.2.0", "body": "Changelog content here"}</arguments>
</use_mcp_tool>
```
Or describe the intent in natural language and let Cline figure out the tool call:
```markdown
## Step 3: Create GitHub release
Use the GitHub MCP server to create a release tagged with the version from package.json.
Include the changelog as the release body.
```
## Writing Effective Workflows
**Start simple.** Write natural language steps first. Only add XML tool calls when you need guaranteed behavior.
**Be specific about decisions.** If a step requires user input, make that explicit: "Ask whether to deploy to production or staging."
**Include failure handling.** Tell Cline what to do when something goes wrong: "If tests fail, show the failures and stop the workflow."
**Keep workflows focused.** A `deploy.md` should deploy. A `setup-db.md` should set up the database. Split complex processes into multiple workflows that can be run independently.
**Version control your workflows.** Store workflows in `.clinerules/workflows/` and commit them. Your team can share, review, and improve them together.
<Warning>
Workflows execute with your permissions. Review workflows before running them, especially those from external sources.
</Warning>
## Example: Release Preparation
This workflow automates the tedious pre-release checklist. It verifies your working directory is clean, runs tests and builds, prompts you for the version bump, and generates a changelog from recent commits.
The workflow demonstrates both approaches: XML tool syntax (`<execute_command>`, `<ask_followup_question>`) for steps that need precise control, and natural language for steps where Cline should adapt to the situation.
````markdown title="release-prep.md"
# Release Preparation
Prepare a new release by running tests, building, and updating version info.
## Step 1: Check for clean working directory
<execute_command>
<command>git status --porcelain</command>
</execute_command>
If there are uncommitted changes, ask whether to continue or stash them first.
## Step 2: Run the test suite
<execute_command>
<command>npm run test</command>
</execute_command>
If any tests fail, stop the workflow and report the failures.
## Step 3: Build the project
<execute_command>
<command>npm run build</command>
</execute_command>
Verify the build completes without errors.
## Step 4: Ask for new version
<ask_followup_question>
<question>What should the new version be?</question>
<options>["Patch (x.x.X)", "Minor (x.X.0)", "Major (X.0.0)", "Custom"]</options>
</ask_followup_question>
## Step 5: Update version
Update the version in `package.json` to the new version specified by the user.
## Step 6: Generate changelog entry
<execute_command>
<command>git log --oneline $(git describe --tags --abbrev=0)..HEAD</command>
</execute_command>
Use these commits to write a changelog entry for the new version.
````
Invoke it with `/release-prep.md` and Cline walks through each step.

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