Compare commits
174 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 43ddc8c846 | |||
| 386ded5126 | |||
| 44e15319e4 | |||
| e424b28702 | |||
| db9971890e | |||
| d7cc9b6155 | |||
| 05042d3ff7 | |||
| dc2c662de6 | |||
| 4b68826cb5 | |||
| 85be70bb25 | |||
| c824d8380a | |||
| fb80840086 | |||
| 42e4ea60db | |||
| 31a118fc0c | |||
| c33c3176ef | |||
| f5a3c591c8 | |||
| be930a69a8 | |||
| 3651fe9a55 | |||
| 526d8e9c93 | |||
| fad8271f41 | |||
| d6837edda8 | |||
| d0f20ca135 | |||
| b2a113a2a5 | |||
| 5efa8cfd3f | |||
| 9f42aea85d | |||
| b87f61f9e4 | |||
| 38f1c7eb14 | |||
| 81121663a4 | |||
| 854ac75fe0 | |||
| 107f0f8337 | |||
| e330695cb5 | |||
| c2879dba43 | |||
| f2d692cfc2 | |||
| 0c90bd9bcf | |||
| 33e521e551 | |||
| 9e942fbb6b | |||
| 6f609e8945 | |||
| 762e3c42ab | |||
| 49e8c1b324 | |||
| 71b8f43a7a | |||
| 3068fcfedf | |||
| 7530900166 | |||
| 2b45b7b7aa | |||
| 791d238996 | |||
| 9ee618111d | |||
| 4bec5931f6 | |||
| 3115679031 | |||
| 8a6441fddd | |||
| 7ef5b1e7af | |||
| 3e58e7b024 | |||
| d16667f0b8 | |||
| a740c49524 | |||
| 3bc1ee8382 | |||
| ecf354c753 | |||
| a66c5ee973 | |||
| 46659eebeb | |||
| 899ee0bfea | |||
| db4598e310 | |||
| 627e539266 | |||
| 87ff61915f | |||
| a305556452 | |||
| ca12e97288 | |||
| 0c89716849 | |||
| 1c401dbe1a | |||
| ed78404e4b | |||
| 0157ed9efb | |||
| 5574c95ff2 | |||
| 31ee8eb744 | |||
| 7952e230ae | |||
| 6bcfa34ba9 | |||
| cf18793434 | |||
| a7ba25d6e1 | |||
| 2af2028861 | |||
| 932c8e68e1 | |||
| 3cd13352eb | |||
| 2c842328a8 | |||
| d3b3ff1c33 | |||
| 2508e76af8 | |||
| a702bf9b38 | |||
| a8cea1dca3 | |||
| 2a351ffdd5 | |||
| 69f148bad9 | |||
| 6ca92794e1 | |||
| 27bd4c6c65 | |||
| 015b61924f | |||
| d238239a0f | |||
| e81c35d7c0 | |||
| 1ac5525fee | |||
| a4d0937030 | |||
| 6d5c61f044 | |||
| 8a6d031e8f | |||
| 3f808b369e | |||
| e3b3e2306e | |||
| b601b6e623 | |||
| ed008bd36e | |||
| 6e964c3ef0 | |||
| 2e8579ebec | |||
| cb0515d5f4 | |||
| 8c00eb9a77 | |||
| 1a4122eb53 | |||
| e6fa037c4c | |||
| 6e0ae8db58 | |||
| 0b1fe6a982 | |||
| c18eef5d4c | |||
| 65443a01ae | |||
| 7d56dd1e69 | |||
| 501f512066 | |||
| 5810a5d1e7 | |||
| 6a262b395a | |||
| 5117b7b0b8 | |||
| 30ce8b1e72 | |||
| 2fcac58e24 | |||
| 112dbaa15b | |||
| 996a2f6a9c | |||
| 47f6d00f61 | |||
| 9ba2c932b4 | |||
| 01aaa5dd04 | |||
| 38bac25721 | |||
| b4d1b83bad | |||
| 8e6780201c | |||
| 087c6449c3 | |||
| 5cd72e5892 | |||
| 2b2fa8a473 | |||
| acca9186f1 | |||
| f3bddc4ec1 | |||
| 5c9cd557c7 | |||
| 0383be5375 | |||
| 4ee1ce507e | |||
| 22b3d654db | |||
| 52493c5ba1 | |||
| e65e5d5900 | |||
| b144baf102 | |||
| f70725dd57 | |||
| 3821f7e52e | |||
| 8bbd1ea0f1 | |||
| 9739e4d11f | |||
| c8d6efd831 | |||
| b17dfa1593 | |||
| d70b0d913f | |||
| 1468a7445d | |||
| 83dcd4c178 | |||
| 18181829f2 | |||
| 8688678ea7 | |||
| da58ddd35e | |||
| 1f5e2086a1 | |||
| 59e5eac3e0 | |||
| fd2c5e1a73 | |||
| 6d93c8af2d | |||
| 1acb2b428c | |||
| be55c3ac5d | |||
| 52872f300c | |||
| 8c7617cead | |||
| d62077d59a | |||
| fece9f232e | |||
| 99b65b421a | |||
| 80783b62cb | |||
| c7f836bcd7 | |||
| 9e64e8966a | |||
| a88b84785e | |||
| 27539b589c | |||
| e6f27c70f4 | |||
| 6b9f2c0803 | |||
| 6c9f72049f | |||
| 95ae525853 | |||
| 7547229d6a | |||
| 7b48ead590 | |||
| 11202ee476 | |||
| d364700d9b | |||
| 95db8638ad | |||
| 0e1a41133e | |||
| 0b0ca9b1fa | |||
| 1f0246655c | |||
| 4494a29541 | |||
| 006ce519ff |
@@ -0,0 +1,208 @@
|
||||
---
|
||||
name: cline-sdk
|
||||
description: Comprehensive Cline SDK skill for building AI agents. Covers the Agent runtime, ClineCore sessions, custom tools, plugins, events, LLM providers, scheduling, multi-agent teams, and production deployment. Use for any task involving @cline/sdk or its sub-packages.
|
||||
metadata:
|
||||
references: agent, clinecore
|
||||
---
|
||||
|
||||
# Cline SDK Skill
|
||||
|
||||
Consolidated skill for building AI agents with the Cline SDK. Use the decision trees below to find the right entry point and API surface, then load detailed references.
|
||||
|
||||
## Critical Rules
|
||||
|
||||
Follow these rules in all Cline SDK code:
|
||||
|
||||
1. Install with `npm install @cline/sdk`. The `@cline/sdk` package re-exports everything from `@cline/core`, `@cline/agents`, `@cline/llms`, and `@cline/shared`.
|
||||
2. Requires Node.js 22 or later.
|
||||
3. Use `createTool()` from `@cline/sdk` (or `@cline/shared`) to define tools. Tool names must be `snake_case`.
|
||||
4. Return errors as structured data from tool `execute` functions. Throwing counts as a "mistake" against the agent's mistake limit.
|
||||
5. Use `lifecycle: { completesRun: true }` on tools that should end the agent loop (e.g. a "submit answer" tool).
|
||||
6. When using `ClineCore`, always call `dispose()` when done to clean up resources.
|
||||
7. The standalone `Agent` and `ClineCore` have different event systems. For `Agent`: use `agent.subscribe()` to get `AgentRuntimeEvent` types (text streaming is `"assistant-text-delta"`, result text is `result.outputText`). For `ClineCore`: use `cline.subscribe()` to get `CoreSessionEvent` types (text streaming is `"chunk"` with `payload.type === "text"`, result text is `result.text`). There is no top-level `onEvent` field on `AgentRuntimeConfig` -- use `agent.subscribe()` or `hooks.onEvent` instead. Do not use event types like `"content_update"` or `"content_start"` with `agent.subscribe()` -- those are internal legacy types from the ClineCore adapter layer.
|
||||
|
||||
## How to Use This Skill
|
||||
|
||||
### Reference File Structure
|
||||
|
||||
The two main API surfaces (`Agent` and `ClineCore`) follow a 4-file pattern. Cross-cutting concepts are single-file guides.
|
||||
|
||||
Each main API surface in `./references/<api>/` contains:
|
||||
|
||||
| File | Purpose | When to Read |
|
||||
|------|---------|--------------|
|
||||
| `REFERENCE.md` | Overview, when to use, quick start | Always read first |
|
||||
| `api.md` | Full API: classes, methods, config, types | Writing code |
|
||||
| `patterns.md` | Common patterns, best practices | Implementation guidance |
|
||||
| `gotchas.md` | Pitfalls, limitations, debugging | Troubleshooting |
|
||||
|
||||
Cross-cutting concepts in `./references/<concept>/` have `REFERENCE.md` as the entry point.
|
||||
|
||||
### Reading Order
|
||||
|
||||
1. Start with `REFERENCE.md` for your chosen API surface
|
||||
2. Then read additional files relevant to your task:
|
||||
- Writing agent code -> `api.md`
|
||||
- Common patterns -> `patterns.md`
|
||||
- Creating tools -> `tools/REFERENCE.md`
|
||||
- Adding plugins/hooks -> `plugins/REFERENCE.md`
|
||||
- Configuring LLM providers -> `providers/REFERENCE.md`
|
||||
- Streaming events -> `events/REFERENCE.md`
|
||||
- Deploying to production -> `production/REFERENCE.md`
|
||||
- Scheduling agents -> `scheduling/REFERENCE.md`
|
||||
- Multi-agent orchestration -> `multi-agent/REFERENCE.md`
|
||||
- Debugging -> `gotchas.md`
|
||||
|
||||
### Example Paths
|
||||
|
||||
```
|
||||
./references/agent/REFERENCE.md # Start here for lightweight agents
|
||||
./references/clinecore/REFERENCE.md # Start here for full runtime
|
||||
./references/agent/api.md # Agent class, config, methods
|
||||
./references/tools/REFERENCE.md # Creating and using tools
|
||||
./references/plugins/REFERENCE.md # Plugin system
|
||||
./references/providers/REFERENCE.md # LLM provider configuration
|
||||
```
|
||||
|
||||
## Quick Decision Trees
|
||||
|
||||
### "Which API surface should I use?"
|
||||
|
||||
```
|
||||
Which API?
|
||||
+-- I want a simple, stateless agent with custom tools
|
||||
| +-- agent/ (Agent class from @cline/agents)
|
||||
+-- I need session persistence, built-in tools, config discovery
|
||||
| +-- clinecore/ (ClineCore from @cline/core)
|
||||
+-- I want built-in file/shell/search/web tools
|
||||
| +-- clinecore/ (has built-in tools; Agent does not)
|
||||
+-- I want scheduled or recurring agents
|
||||
| +-- clinecore/ (automation API)
|
||||
+-- I need multi-process or multi-client session sharing
|
||||
| +-- clinecore/ (hub-backed runtime)
|
||||
+-- I'm building a browser-compatible agent
|
||||
| +-- agent/ (no Node.js dependencies)
|
||||
```
|
||||
|
||||
### "I need to create tools"
|
||||
|
||||
```
|
||||
Tools?
|
||||
+-- Define a custom tool with schema -> tools/REFERENCE.md
|
||||
+-- Use built-in tools (bash, editor, read_files) -> tools/REFERENCE.md (built-in section)
|
||||
+-- Control tool approval/policies -> tools/REFERENCE.md (policies section)
|
||||
+-- Tool that ends the agent loop -> tools/REFERENCE.md (completion tools)
|
||||
+-- Package tools as a reusable plugin -> plugins/REFERENCE.md
|
||||
```
|
||||
|
||||
### "I need to handle events"
|
||||
|
||||
```
|
||||
Events?
|
||||
+-- Stream text/reasoning in real time -> events/REFERENCE.md
|
||||
+-- Track token usage and costs -> events/REFERENCE.md
|
||||
+-- Watch tool calls -> events/REFERENCE.md
|
||||
+-- Detect completion/errors -> events/REFERENCE.md
|
||||
+-- Hook into lifecycle stages -> plugins/REFERENCE.md
|
||||
```
|
||||
|
||||
### "I need to configure a model provider"
|
||||
|
||||
```
|
||||
Providers?
|
||||
+-- Anthropic (Claude) -> providers/REFERENCE.md
|
||||
+-- OpenAI (GPT) -> providers/REFERENCE.md
|
||||
+-- Google (Gemini/Vertex) -> providers/REFERENCE.md
|
||||
+-- AWS Bedrock -> providers/REFERENCE.md
|
||||
+-- Mistral -> providers/REFERENCE.md
|
||||
+-- OpenAI-compatible (vLLM, Together, etc.) -> providers/REFERENCE.md
|
||||
+-- Custom/self-hosted provider -> providers/REFERENCE.md
|
||||
```
|
||||
|
||||
### "I need plugins or hooks"
|
||||
|
||||
```
|
||||
Plugins?
|
||||
+-- Package tools + hooks together -> plugins/REFERENCE.md
|
||||
+-- Observe tool calls (logging, metrics) -> plugins/REFERENCE.md
|
||||
+-- Intercept lifecycle events -> plugins/REFERENCE.md
|
||||
+-- Add system prompt rules -> plugins/REFERENCE.md
|
||||
+-- Distribute via npm/git -> plugins/REFERENCE.md
|
||||
```
|
||||
|
||||
### "I need multi-agent coordination"
|
||||
|
||||
```
|
||||
Multi-agent?
|
||||
+-- Spawn one-off background agents -> multi-agent/REFERENCE.md (sub-agents)
|
||||
+-- Persistent cross-session teams -> multi-agent/REFERENCE.md (teams)
|
||||
+-- Parent-child delegation -> multi-agent/REFERENCE.md (sub-agents)
|
||||
+-- Peer-to-peer task board -> multi-agent/REFERENCE.md (teams)
|
||||
```
|
||||
|
||||
### "I need scheduling or automation"
|
||||
|
||||
```
|
||||
Scheduling?
|
||||
+-- Recurring cron jobs -> scheduling/REFERENCE.md
|
||||
+-- One-off scheduled tasks -> scheduling/REFERENCE.md
|
||||
+-- Event-driven triggers -> scheduling/REFERENCE.md
|
||||
+-- CLI schedule management -> scheduling/REFERENCE.md
|
||||
```
|
||||
|
||||
### "I need to go to production"
|
||||
|
||||
```
|
||||
Production?
|
||||
+-- Error handling and status checks -> production/REFERENCE.md
|
||||
+-- Cost control and token limits -> production/REFERENCE.md
|
||||
+-- Observability (OpenTelemetry) -> production/REFERENCE.md
|
||||
+-- Security and sandboxing -> production/REFERENCE.md
|
||||
+-- Deployment patterns -> production/REFERENCE.md
|
||||
```
|
||||
|
||||
### Troubleshooting Index
|
||||
|
||||
- Agent loop not stopping -> `tools/REFERENCE.md` (completion tools)
|
||||
- Tool errors crashing the agent -> `agent/gotchas.md` or `clinecore/gotchas.md`
|
||||
- Provider auth failures -> `providers/REFERENCE.md`
|
||||
- Session not persisting -> `clinecore/gotchas.md`
|
||||
- Token usage too high -> `production/REFERENCE.md` (cost control)
|
||||
- Hub connection issues -> `clinecore/gotchas.md`
|
||||
- Plugin not loading -> `plugins/REFERENCE.md`
|
||||
- Events not firing -> `events/REFERENCE.md`
|
||||
|
||||
## Product Index
|
||||
|
||||
### API Surfaces
|
||||
| API | Entry File | Description |
|
||||
|-----|------------|-------------|
|
||||
| Agent | `./references/agent/REFERENCE.md` | Lightweight stateless agent loop |
|
||||
| ClineCore | `./references/clinecore/REFERENCE.md` | Full runtime with sessions, persistence, built-in tools |
|
||||
|
||||
### Cross-Cutting Concepts
|
||||
| Concept | Entry File | Description |
|
||||
|---------|------------|-------------|
|
||||
| Tools | `./references/tools/REFERENCE.md` | Built-in and custom tool creation |
|
||||
| Plugins | `./references/plugins/REFERENCE.md` | Extension system with hooks |
|
||||
| Events | `./references/events/REFERENCE.md` | Real-time streaming events |
|
||||
| Providers | `./references/providers/REFERENCE.md` | LLM provider configuration |
|
||||
| Production | `./references/production/REFERENCE.md` | Deployment, security, observability |
|
||||
| Scheduling | `./references/scheduling/REFERENCE.md` | Cron jobs and automation |
|
||||
| Multi-Agent | `./references/multi-agent/REFERENCE.md` | Teams and sub-agents |
|
||||
|
||||
### Package Map
|
||||
| Package | Purpose |
|
||||
|---------|---------|
|
||||
| `@cline/sdk` | Everything you need, install this one |
|
||||
| `@cline/core` | Sessions, persistence, built-in tools, config, hub |
|
||||
| `@cline/agents` | Stateless agent loop, tool orchestration, streaming |
|
||||
| `@cline/llms` | LLM provider gateway |
|
||||
| `@cline/shared` | Types, tool helpers, hook engine |
|
||||
|
||||
## Resources
|
||||
|
||||
Repository: https://github.com/cline/cline
|
||||
SDK Source: https://github.com/cline/cline/tree/main/sdk
|
||||
Documentation: https://docs.cline.bot/sdk/overview
|
||||
Discord: https://discord.gg/cline
|
||||
@@ -0,0 +1,107 @@
|
||||
# Agent Runtime
|
||||
|
||||
The `Agent` class (also exported as `AgentRuntime`) is the lightweight, stateless agent loop from `@cline/agents`. It handles the core iteration cycle: send messages to an LLM, execute tool calls, collect results, and repeat until the task is done.
|
||||
|
||||
## When to Use Agent
|
||||
|
||||
| Use Agent when... | Use ClineCore instead when... |
|
||||
|---|---|
|
||||
| You want a simple agent with custom tools | You need built-in tools (bash, editor, etc.) |
|
||||
| You want minimal dependencies | You need session persistence |
|
||||
| You need browser compatibility | You need config discovery from `.cline/` |
|
||||
| You're building a stateless worker | You need multi-process session sharing |
|
||||
| You want full control over the runtime | You want batteries-included setup |
|
||||
|
||||
## Quick Start
|
||||
|
||||
```typescript
|
||||
import { Agent } from "@cline/sdk"
|
||||
|
||||
const agent = new Agent({
|
||||
providerId: "anthropic",
|
||||
modelId: "claude-sonnet-4-6",
|
||||
apiKey: process.env.ANTHROPIC_API_KEY,
|
||||
systemPrompt: "You are a helpful assistant.",
|
||||
tools: [],
|
||||
})
|
||||
|
||||
const result = await agent.run("What is the capital of France?")
|
||||
console.log(result.outputText)
|
||||
```
|
||||
|
||||
## Core Concepts
|
||||
|
||||
The Agent operates in a loop:
|
||||
1. Accept user input (string, message, or array of messages)
|
||||
2. Build turn context (system prompt, messages, tools)
|
||||
3. Call the LLM provider
|
||||
4. If the model returns tool calls, execute them and loop back to step 3
|
||||
5. If the model returns text without tool calls, the run completes
|
||||
6. Emit events throughout for streaming
|
||||
|
||||
The agent is stateless in the sense that it does not persist anything to disk. Conversation history is held in memory and can be accessed via `snapshot()`.
|
||||
|
||||
## Key APIs
|
||||
|
||||
- `new Agent(config)` or `createAgent(config)` - Create an agent
|
||||
- `agent.run(input)` - Start a run with user input
|
||||
- `agent.continue(input?)` - Continue an existing conversation
|
||||
- `agent.abort(reason?)` - Cancel an active run
|
||||
- `agent.subscribe(listener)` - Listen to streaming events
|
||||
- `agent.snapshot()` - Get current runtime state
|
||||
- `agent.restore(messages)` - Replace message history
|
||||
|
||||
See `api.md` for full API details.
|
||||
|
||||
## Multi-Turn Conversations
|
||||
|
||||
```typescript
|
||||
const agent = new Agent({
|
||||
providerId: "anthropic",
|
||||
modelId: "claude-sonnet-4-6",
|
||||
systemPrompt: "You are a helpful assistant.",
|
||||
tools: [],
|
||||
})
|
||||
|
||||
const first = await agent.run("What is 2 + 2?")
|
||||
console.log(first.outputText)
|
||||
|
||||
const second = await agent.continue("Now multiply that by 3")
|
||||
console.log(second.outputText)
|
||||
```
|
||||
|
||||
Use `agent.hasRun` to check if a run has already been executed, which determines whether to call `run()` or `continue()`.
|
||||
|
||||
## Event Streaming
|
||||
|
||||
Use `agent.subscribe()` to stream events in real time. Register the listener before calling `run()` to avoid missing early events.
|
||||
|
||||
There is no top-level `onEvent` field on the Agent config. For an async alternative, use `hooks.onEvent` (see `api.md` and `gotchas.md`).
|
||||
|
||||
```typescript
|
||||
const agent = new Agent({
|
||||
providerId: "anthropic",
|
||||
modelId: "claude-sonnet-4-6",
|
||||
systemPrompt: "You are a helpful assistant.",
|
||||
tools: [],
|
||||
})
|
||||
|
||||
agent.subscribe((event) => {
|
||||
if (event.type === "assistant-text-delta") {
|
||||
process.stdout.write(event.text)
|
||||
}
|
||||
})
|
||||
|
||||
const result = await agent.run("What is the capital of France?")
|
||||
```
|
||||
|
||||
See `events/REFERENCE.md` for the full event type catalog.
|
||||
|
||||
## Next Steps
|
||||
|
||||
- `api.md` - Full Agent API reference
|
||||
- `patterns.md` - Common patterns and best practices
|
||||
- `gotchas.md` - Pitfalls and debugging
|
||||
- `../tools/REFERENCE.md` - Creating custom tools
|
||||
- `../events/REFERENCE.md` - Event system details
|
||||
- `../providers/REFERENCE.md` - Provider configuration
|
||||
@@ -0,0 +1,231 @@
|
||||
# Agent API Reference
|
||||
|
||||
## Constructor
|
||||
|
||||
```typescript
|
||||
import { Agent } from "@cline/sdk"
|
||||
|
||||
const agent = new Agent(config: AgentRuntimeConfig)
|
||||
```
|
||||
|
||||
Also available via factory function:
|
||||
|
||||
```typescript
|
||||
import { createAgent } from "@cline/sdk"
|
||||
|
||||
const agent = createAgent(config)
|
||||
```
|
||||
|
||||
## AgentRuntimeConfig
|
||||
|
||||
Two config forms exist as a discriminated union:
|
||||
|
||||
### With Provider ID (recommended)
|
||||
|
||||
```typescript
|
||||
interface AgentRuntimeConfigWithProvider {
|
||||
providerId: string // e.g. "anthropic", "openai", "gemini"
|
||||
modelId: string // e.g. "claude-sonnet-4-6", "gpt-5.5"
|
||||
apiKey?: string // provider API key
|
||||
baseUrl?: string // custom endpoint
|
||||
headers?: Record<string, string>
|
||||
|
||||
systemPrompt?: string
|
||||
tools?: AgentTool[]
|
||||
initialMessages?: AgentMessage[]
|
||||
toolPolicies?: Record<string, ToolPolicy>
|
||||
hooks?: Partial<AgentRuntimeHooks>
|
||||
plugins?: AgentPlugin[]
|
||||
}
|
||||
```
|
||||
|
||||
### With Pre-built Model
|
||||
|
||||
```typescript
|
||||
interface AgentRuntimeConfigWithModel {
|
||||
model: AgentModel // pre-built model from gateway
|
||||
|
||||
systemPrompt?: string
|
||||
tools?: AgentTool[]
|
||||
initialMessages?: AgentMessage[]
|
||||
toolPolicies?: Record<string, ToolPolicy>
|
||||
hooks?: Partial<AgentRuntimeHooks>
|
||||
plugins?: AgentPlugin[]
|
||||
}
|
||||
```
|
||||
|
||||
Note: there is no top-level `onEvent` field on `AgentRuntimeConfig`. For event streaming, use `agent.subscribe()` or `hooks.onEvent` (see AgentRuntimeHooks below).
|
||||
|
||||
## Methods
|
||||
|
||||
### run(input)
|
||||
|
||||
Start the agent with user input. Returns when the agent loop completes.
|
||||
|
||||
```typescript
|
||||
const result: AgentRunResult = await agent.run("Build a REST API")
|
||||
```
|
||||
|
||||
Input can be a string, an `AgentMessage`, or an array of `AgentMessage[]`.
|
||||
|
||||
### continue(input?)
|
||||
|
||||
Continue an existing conversation with optional new input.
|
||||
|
||||
```typescript
|
||||
const result = await agent.continue("Now add authentication")
|
||||
```
|
||||
|
||||
### abort(reason?)
|
||||
|
||||
Cancel the currently active run.
|
||||
|
||||
```typescript
|
||||
agent.abort("User cancelled")
|
||||
```
|
||||
|
||||
### subscribe(listener)
|
||||
|
||||
Register a listener for streaming events.
|
||||
|
||||
```typescript
|
||||
const unsubscribe = agent.subscribe((event: AgentRuntimeEvent) => {
|
||||
// handle event
|
||||
})
|
||||
|
||||
// Later: stop listening
|
||||
unsubscribe()
|
||||
```
|
||||
|
||||
### snapshot()
|
||||
|
||||
Get the current runtime state including message history.
|
||||
|
||||
```typescript
|
||||
const state: AgentRuntimeStateSnapshot = agent.snapshot()
|
||||
```
|
||||
|
||||
### restore(messages)
|
||||
|
||||
Replace the agent's message history.
|
||||
|
||||
```typescript
|
||||
agent.restore(previousMessages)
|
||||
```
|
||||
|
||||
### hasRun
|
||||
|
||||
Boolean property indicating whether `run()` has been called at least once.
|
||||
|
||||
```typescript
|
||||
if (agent.hasRun) {
|
||||
await agent.continue(input)
|
||||
} else {
|
||||
await agent.run(input)
|
||||
}
|
||||
```
|
||||
|
||||
## AgentRunResult
|
||||
|
||||
Returned by `run()` and `continue()`.
|
||||
|
||||
```typescript
|
||||
interface AgentRunResult {
|
||||
agentId: string
|
||||
agentRole?: string
|
||||
runId: string
|
||||
status: "completed" | "aborted" | "failed"
|
||||
iterations: number
|
||||
outputText: string
|
||||
messages: readonly AgentMessage[]
|
||||
usage: AgentUsage
|
||||
error?: Error
|
||||
}
|
||||
```
|
||||
|
||||
### Status Values
|
||||
|
||||
- `"completed"` - Agent finished normally
|
||||
- `"aborted"` - Cancelled via `abort()`
|
||||
- `"failed"` - Unrecoverable error
|
||||
|
||||
## AgentMessage
|
||||
|
||||
```typescript
|
||||
interface AgentMessage {
|
||||
id: string
|
||||
role: "user" | "assistant" | "tool"
|
||||
content: AgentMessagePart[]
|
||||
createdAt: number
|
||||
metadata?: Record<string, unknown>
|
||||
modelInfo?: { id: string; provider: string; family?: string }
|
||||
metrics?: {
|
||||
inputTokens: number
|
||||
outputTokens: number
|
||||
cacheReadTokens?: number
|
||||
cacheWriteTokens?: number
|
||||
cost?: number
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## AgentUsage
|
||||
|
||||
```typescript
|
||||
interface AgentUsage {
|
||||
inputTokens: number
|
||||
outputTokens: number
|
||||
cacheReadTokens: number
|
||||
cacheWriteTokens: number
|
||||
totalInputTokens: number
|
||||
totalOutputTokens: number
|
||||
totalCost?: number
|
||||
}
|
||||
```
|
||||
|
||||
## AgentRuntimeHooks
|
||||
|
||||
```typescript
|
||||
interface AgentRuntimeHooks {
|
||||
beforeRun?(context): AgentStopControl | undefined
|
||||
afterRun?(context): void
|
||||
beforeModel?(context): AgentBeforeModelResult | undefined
|
||||
afterModel?(context): AgentStopControl | undefined
|
||||
beforeTool?(context): AgentBeforeToolResult | undefined
|
||||
afterTool?(context): AgentAfterToolResult | undefined
|
||||
onEvent?(event: AgentRuntimeEvent): void | Promise<void>
|
||||
}
|
||||
```
|
||||
|
||||
Hooks can intercept and modify behavior at each stage. Return a stop control from `beforeRun`, `afterModel`, or `beforeTool` to halt the agent loop.
|
||||
|
||||
`hooks.onEvent` receives the same `AgentRuntimeEvent` types as `agent.subscribe()`, but hook callbacks are awaited (can be async), while `subscribe()` listeners are called synchronously. Use `subscribe()` for UI streaming and `hooks.onEvent` for async side effects like logging to an external service.
|
||||
|
||||
## AgentRuntimeStateSnapshot
|
||||
|
||||
```typescript
|
||||
interface AgentRuntimeStateSnapshot {
|
||||
messages: readonly AgentMessage[]
|
||||
usage: AgentUsage
|
||||
iterations: number
|
||||
status: string
|
||||
}
|
||||
```
|
||||
|
||||
## Factory: createAgentRuntime
|
||||
|
||||
Lower-level factory that returns the same `Agent` class:
|
||||
|
||||
```typescript
|
||||
import { createAgentRuntime } from "@cline/sdk"
|
||||
|
||||
const runtime = createAgentRuntime(config)
|
||||
```
|
||||
|
||||
## See Also
|
||||
|
||||
- `REFERENCE.md` - Overview and quick start
|
||||
- `patterns.md` - Common patterns
|
||||
- `../tools/REFERENCE.md` - Tool creation
|
||||
- `../events/REFERENCE.md` - Event types
|
||||
- `../providers/REFERENCE.md` - Provider setup
|
||||
@@ -0,0 +1,134 @@
|
||||
# Agent Gotchas
|
||||
|
||||
## Agent Loop Never Stops
|
||||
|
||||
If the agent keeps iterating without completing:
|
||||
|
||||
- Make sure at least one tool has `lifecycle: { completesRun: true }` if you want the agent to explicitly finish.
|
||||
- Without any tools, the agent will complete after the model returns text without tool calls.
|
||||
- If using tools, ensure the system prompt guides the model toward calling the completion tool when done.
|
||||
- Check that `completesRun` tools return successfully (not throwing errors).
|
||||
|
||||
## Tool Errors Count as Mistakes
|
||||
|
||||
When a tool's `execute` function throws an exception, the SDK counts it as a "mistake." After too many mistakes, the agent stops with a `mistake_limit` finish reason.
|
||||
|
||||
Instead, return errors as structured data:
|
||||
|
||||
```typescript
|
||||
// Bad: throwing
|
||||
execute: async (input) => {
|
||||
throw new Error("File not found")
|
||||
}
|
||||
|
||||
// Good: returning error data
|
||||
execute: async (input) => {
|
||||
return { error: "File not found", path: input.path }
|
||||
}
|
||||
```
|
||||
|
||||
## run() vs continue()
|
||||
|
||||
- Call `run()` for the first interaction. It sets up the conversation.
|
||||
- Call `continue()` for subsequent messages. It appends to the existing conversation.
|
||||
- Calling `run()` a second time resets the conversation history.
|
||||
- Use `agent.hasRun` to check which method to call.
|
||||
|
||||
## Browser Compatibility
|
||||
|
||||
`@cline/agents` (and by extension, the `Agent` class) is browser-safe with no Node.js dependencies. However, `@cline/core` and `ClineCore` require Node.js 22+. If you import from `@cline/sdk`, you get everything including the Node-only code. For browser usage, import directly from `@cline/agents`:
|
||||
|
||||
```typescript
|
||||
import { Agent } from "@cline/agents"
|
||||
```
|
||||
|
||||
## No Top-Level onEvent on Agent Config
|
||||
|
||||
`AgentRuntimeConfig` does not have a top-level `onEvent` field. Passing `onEvent` to `new Agent({ onEvent: ... })` has no effect. There are two ways to receive events:
|
||||
|
||||
```typescript
|
||||
// Option 1: subscribe() - synchronous, best for UI streaming
|
||||
const agent = new Agent({ ...config })
|
||||
agent.subscribe((event) => {
|
||||
if (event.type === "assistant-text-delta") {
|
||||
process.stdout.write(event.text)
|
||||
}
|
||||
})
|
||||
|
||||
// Option 2: hooks.onEvent - awaited, best for async side effects
|
||||
const agent = new Agent({
|
||||
...config,
|
||||
hooks: {
|
||||
onEvent: async (event) => {
|
||||
if (event.type === "assistant-text-delta") {
|
||||
await logToService(event.text)
|
||||
}
|
||||
},
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
Both receive the same `AgentRuntimeEvent` types. Prefer `subscribe()` for streaming UI.
|
||||
|
||||
## Event Listener Timing
|
||||
|
||||
Register event listeners via `subscribe()` before calling `run()`:
|
||||
|
||||
```typescript
|
||||
// Good: subscribe before run
|
||||
agent.subscribe(handler)
|
||||
const result = await agent.run(input)
|
||||
|
||||
// Bad: subscribing after run starts loses early events
|
||||
const promise = agent.run(input)
|
||||
agent.subscribe(handler) // may miss events
|
||||
```
|
||||
|
||||
## Tool Input Schema Matters
|
||||
|
||||
The model uses the tool's `inputSchema` to decide what arguments to pass. A vague or missing schema leads to incorrect tool calls.
|
||||
|
||||
- Use `z.enum()` for fixed value sets, not free-form strings
|
||||
- Describe every property with `.describe()` in Zod or `description` in JSON Schema
|
||||
- Include constraints (rate limits, max values) in the tool description
|
||||
|
||||
## Memory and Long Conversations
|
||||
|
||||
The Agent holds all messages in memory. For long-running conversations, memory usage grows with each turn. Consider:
|
||||
|
||||
- Using `ClineCore` with compaction for long sessions
|
||||
- Periodically creating a new agent with a summary of the conversation
|
||||
- Monitoring `result.usage.totalInputTokens` to track context growth
|
||||
|
||||
## Abort Signal Handling in Tools
|
||||
|
||||
Long-running tools should respect the abort signal:
|
||||
|
||||
```typescript
|
||||
execute: async (input, context) => {
|
||||
for (const item of items) {
|
||||
if (context.abortSignal?.aborted) {
|
||||
return { partial: results, aborted: true }
|
||||
}
|
||||
results.push(await process(item))
|
||||
}
|
||||
return { results }
|
||||
}
|
||||
```
|
||||
|
||||
## Provider API Key
|
||||
|
||||
If you get authentication errors, check:
|
||||
|
||||
- `apiKey` is set in the config or via environment variables
|
||||
- The key matches the `providerId` (e.g., Anthropic key for `providerId: "anthropic"`)
|
||||
- For OpenAI-compatible providers, both `apiKey` and `baseUrl` are set
|
||||
|
||||
See `../providers/REFERENCE.md` for provider-specific setup.
|
||||
|
||||
## See Also
|
||||
|
||||
- `api.md` - Full API reference
|
||||
- `patterns.md` - Common patterns
|
||||
- `../tools/REFERENCE.md` - Tool creation
|
||||
- `../clinecore/REFERENCE.md` - Use ClineCore for persistence
|
||||
@@ -0,0 +1,258 @@
|
||||
# Agent Patterns
|
||||
|
||||
## Interactive CLI Agent
|
||||
|
||||
A multi-turn conversational agent in the terminal with streaming output:
|
||||
|
||||
```typescript
|
||||
import { Agent } from "@cline/sdk"
|
||||
import * as readline from "node:readline"
|
||||
|
||||
const agent = new Agent({
|
||||
providerId: "anthropic",
|
||||
modelId: "claude-sonnet-4-6",
|
||||
apiKey: process.env.ANTHROPIC_API_KEY,
|
||||
systemPrompt: "You are a helpful assistant. Keep responses concise.",
|
||||
tools: [],
|
||||
})
|
||||
|
||||
agent.subscribe((event) => {
|
||||
if (event.type === "assistant-text-delta") {
|
||||
process.stdout.write(event.text)
|
||||
}
|
||||
})
|
||||
|
||||
const rl = readline.createInterface({
|
||||
input: process.stdin,
|
||||
output: process.stdout,
|
||||
})
|
||||
|
||||
function prompt(): void {
|
||||
rl.question("\nYou: ", async (input) => {
|
||||
const trimmed = input.trim()
|
||||
if (!trimmed || trimmed === "exit") {
|
||||
rl.close()
|
||||
return
|
||||
}
|
||||
|
||||
process.stdout.write("\nAssistant: ")
|
||||
|
||||
if (agent.hasRun) {
|
||||
await agent.continue(trimmed)
|
||||
} else {
|
||||
await agent.run(trimmed)
|
||||
}
|
||||
|
||||
process.stdout.write("\n")
|
||||
prompt()
|
||||
})
|
||||
}
|
||||
|
||||
prompt()
|
||||
```
|
||||
|
||||
## Conversational Agent (Slack Bot, Chat App)
|
||||
|
||||
Maintain per-thread agents with conversation memory:
|
||||
|
||||
```typescript
|
||||
import { Agent } from "@cline/sdk"
|
||||
|
||||
const agents = new Map<string, Agent>()
|
||||
|
||||
async function handleMessage(threadId: string, message: string) {
|
||||
let agent = agents.get(threadId)
|
||||
if (!agent) {
|
||||
agent = new Agent({
|
||||
providerId: "anthropic",
|
||||
modelId: "claude-sonnet-4-6",
|
||||
systemPrompt: "You are a concise assistant.",
|
||||
tools: [],
|
||||
})
|
||||
agents.set(threadId, agent)
|
||||
}
|
||||
|
||||
const result = agent.hasRun
|
||||
? await agent.continue(message)
|
||||
: await agent.run(message)
|
||||
|
||||
return result.outputText
|
||||
}
|
||||
```
|
||||
|
||||
## Streaming UI
|
||||
|
||||
Build a real-time UI by handling events via `subscribe()`:
|
||||
|
||||
```typescript
|
||||
const agent = new Agent({
|
||||
providerId: "anthropic",
|
||||
modelId: "claude-sonnet-4-6",
|
||||
systemPrompt: "You are a helpful assistant.",
|
||||
tools: [myTool],
|
||||
})
|
||||
|
||||
agent.subscribe((event) => {
|
||||
switch (event.type) {
|
||||
case "assistant-text-delta":
|
||||
ui.appendText(event.text)
|
||||
break
|
||||
case "assistant-message":
|
||||
ui.endText()
|
||||
break
|
||||
case "turn-started":
|
||||
ui.startTurn(event.iteration)
|
||||
break
|
||||
case "turn-finished":
|
||||
if (event.toolCallCount > 0) ui.showToolCount(event.toolCallCount)
|
||||
break
|
||||
case "usage-updated":
|
||||
ui.updateUsage(event.usage.inputTokens, event.usage.outputTokens)
|
||||
break
|
||||
}
|
||||
})
|
||||
|
||||
const result = await agent.run("Hello!")
|
||||
```
|
||||
|
||||
## Structured Output via Completion Tool
|
||||
|
||||
Use a tool with `completesRun: true` to extract structured data:
|
||||
|
||||
```typescript
|
||||
import { Agent, createTool } from "@cline/sdk"
|
||||
import { z } from "zod"
|
||||
|
||||
const submitReview = createTool({
|
||||
name: "submit_review",
|
||||
description: "Submit the final code review with structured feedback.",
|
||||
inputSchema: z.object({
|
||||
summary: z.string(),
|
||||
issues: z.array(z.object({
|
||||
file: z.string(),
|
||||
line: z.number(),
|
||||
severity: z.enum(["error", "warning", "info"]),
|
||||
message: z.string(),
|
||||
})),
|
||||
approved: z.boolean(),
|
||||
}),
|
||||
lifecycle: { completesRun: true },
|
||||
execute: async (input) => input,
|
||||
})
|
||||
|
||||
const agent = new Agent({
|
||||
providerId: "anthropic",
|
||||
modelId: "claude-sonnet-4-6",
|
||||
systemPrompt: "Review the code diff and submit structured feedback.",
|
||||
tools: [submitReview],
|
||||
})
|
||||
|
||||
const result = await agent.run(diffContent)
|
||||
const review = result.toolCalls.find(tc => tc.name === "submit_review")
|
||||
console.log(review?.output)
|
||||
```
|
||||
|
||||
## Agent with Abort/Timeout
|
||||
|
||||
```typescript
|
||||
const agent = new Agent({
|
||||
providerId: "anthropic",
|
||||
modelId: "claude-sonnet-4-6",
|
||||
systemPrompt: "Analyze this data.",
|
||||
tools: [],
|
||||
})
|
||||
|
||||
const timeout = setTimeout(() => agent.abort("Timeout"), 30_000)
|
||||
|
||||
try {
|
||||
const result = await agent.run(data)
|
||||
if (result.status === "aborted") {
|
||||
console.log("Agent was aborted")
|
||||
} else {
|
||||
console.log(result.outputText)
|
||||
}
|
||||
} finally {
|
||||
clearTimeout(timeout)
|
||||
}
|
||||
```
|
||||
|
||||
## Agent with Plugins
|
||||
|
||||
```typescript
|
||||
import { Agent } from "@cline/sdk"
|
||||
import type { AgentPlugin } from "@cline/sdk"
|
||||
|
||||
const loggingPlugin: AgentPlugin = {
|
||||
name: "logging",
|
||||
manifest: { capabilities: ["hooks"] },
|
||||
setup() {},
|
||||
hooks: {
|
||||
beforeTool({ toolCall }) {
|
||||
console.log(`Calling tool: ${toolCall.toolName}`)
|
||||
},
|
||||
afterRun({ result }) {
|
||||
console.log(`Completed in ${result.iterations} iterations`)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
const agent = new Agent({
|
||||
providerId: "anthropic",
|
||||
modelId: "claude-sonnet-4-6",
|
||||
systemPrompt: "You are a helpful assistant.",
|
||||
tools: [myTool],
|
||||
plugins: [loggingPlugin],
|
||||
})
|
||||
```
|
||||
|
||||
## Restoring State Across Sessions
|
||||
|
||||
Save and restore agent state manually:
|
||||
|
||||
```typescript
|
||||
// Save state
|
||||
const snapshot = agent.snapshot()
|
||||
const serialized = JSON.stringify(snapshot.messages)
|
||||
|
||||
// Later: restore
|
||||
const agent2 = new Agent({ ...config })
|
||||
const messages = JSON.parse(serialized)
|
||||
agent2.restore(messages)
|
||||
const result = await agent2.continue("Continue where we left off")
|
||||
```
|
||||
|
||||
For automatic persistence, use `ClineCore` instead.
|
||||
|
||||
## Pre-Built Model via Gateway
|
||||
|
||||
For advanced provider configuration:
|
||||
|
||||
```typescript
|
||||
import { Agent } from "@cline/sdk"
|
||||
import { createGateway } from "@cline/llms"
|
||||
|
||||
const gateway = createGateway({
|
||||
providerConfigs: [
|
||||
{ providerId: "anthropic", apiKey: process.env.ANTHROPIC_API_KEY },
|
||||
{ providerId: "openai", apiKey: process.env.OPENAI_API_KEY },
|
||||
],
|
||||
})
|
||||
|
||||
const model = gateway.createAgentModel({
|
||||
providerId: "anthropic",
|
||||
modelId: "claude-opus-4-7",
|
||||
})
|
||||
|
||||
const agent = new Agent({
|
||||
model,
|
||||
systemPrompt: "You are a helpful assistant.",
|
||||
tools: [],
|
||||
})
|
||||
```
|
||||
|
||||
## See Also
|
||||
|
||||
- `api.md` - Full API reference
|
||||
- `gotchas.md` - Common pitfalls
|
||||
- `../tools/REFERENCE.md` - Creating tools
|
||||
- `../plugins/REFERENCE.md` - Plugin system
|
||||
@@ -0,0 +1,131 @@
|
||||
# ClineCore Runtime
|
||||
|
||||
`ClineCore` is the full-featured runtime from `@cline/core`. It wraps the `Agent` loop with session persistence, built-in tools (bash, editor, file reading, search, web fetch), config discovery, plugin loading, and optional hub-backed multi-process support.
|
||||
|
||||
## When to Use ClineCore
|
||||
|
||||
| Use ClineCore when... | Use Agent instead when... |
|
||||
|---|---|
|
||||
| You need built-in tools (bash, editor, etc.) | You only need custom tools |
|
||||
| You want session persistence to disk | Stateless is fine |
|
||||
| You need config discovery from `.cline/` dirs | You handle config yourself |
|
||||
| You want scheduled/automated agents | You don't need scheduling |
|
||||
| You need multi-client session sharing | Single-process is fine |
|
||||
| You're building a full application | You want minimal dependencies |
|
||||
|
||||
## Quick Start
|
||||
|
||||
```typescript
|
||||
import { ClineCore } from "@cline/sdk"
|
||||
|
||||
const cline = await ClineCore.create({ clientName: "my-app" })
|
||||
|
||||
const session = await cline.start({
|
||||
prompt: "Set up CI with GitHub Actions",
|
||||
config: {
|
||||
providerId: "anthropic",
|
||||
modelId: "claude-sonnet-4-6",
|
||||
apiKey: process.env.ANTHROPIC_API_KEY,
|
||||
cwd: "/path/to/project",
|
||||
enableTools: true,
|
||||
},
|
||||
})
|
||||
|
||||
console.log(session.result?.text)
|
||||
await cline.dispose()
|
||||
```
|
||||
|
||||
## Core Concepts
|
||||
|
||||
### Sessions
|
||||
|
||||
Every `cline.start()` call creates a session with a unique ID. Sessions persist their messages and metadata to SQLite. You can list, read, resume, and delete sessions.
|
||||
|
||||
### Built-in Tools
|
||||
|
||||
ClineCore provides these tools automatically when `enableTools: true`:
|
||||
|
||||
| Tool | Description |
|
||||
|------|-------------|
|
||||
| `bash` | Execute shell commands |
|
||||
| `editor` | Edit files |
|
||||
| `read_files` | Read file contents |
|
||||
| `apply_patch` | Apply unified diffs |
|
||||
| `search` | Search file contents and structure |
|
||||
| `fetch_web` | HTTP requests and web content |
|
||||
|
||||
### Config Discovery
|
||||
|
||||
ClineCore watches `.cline/` directories for:
|
||||
- Rules (system prompt additions)
|
||||
- Skills (domain knowledge)
|
||||
- Workflows (multi-step procedures)
|
||||
- Hooks (lifecycle logic)
|
||||
- Plugins (tool + hook bundles)
|
||||
- MCP servers (external tool providers)
|
||||
|
||||
### Backend Modes
|
||||
|
||||
| Mode | Description |
|
||||
|------|-------------|
|
||||
| `"auto"` (default) | Tries to connect to a local hub; falls back to in-process if unavailable |
|
||||
| `"local"` | In-process execution, local SQLite storage, no hub |
|
||||
| `"hub"` | Requires a compatible local WebSocket hub; fails if unavailable |
|
||||
| `"remote"` | Connects to an explicit remote hub endpoint |
|
||||
|
||||
The default mode is `"auto"`. For simple scripts and CLI tools, `"local"` avoids hub discovery overhead. Hub mode enables multi-client session sharing (e.g., a dashboard watching a running session from another process).
|
||||
|
||||
## Key APIs
|
||||
|
||||
- `ClineCore.create(options)` - Create and initialize
|
||||
- `cline.start(input)` - Start a new session
|
||||
- `cline.send({ sessionId, prompt })` - Send follow-up message
|
||||
- `cline.subscribe(listener)` - Listen to session events
|
||||
- `cline.list()` - List sessions
|
||||
- `cline.get(sessionId)` - Get session metadata
|
||||
- `cline.readMessages(sessionId)` - Read persisted messages
|
||||
- `cline.getAccumulatedUsage(sessionId)` - Token/cost totals
|
||||
- `cline.abort(sessionId)` - Abort a session
|
||||
- `cline.delete(sessionId)` - Delete a session
|
||||
- `cline.dispose()` - Clean up resources
|
||||
|
||||
See `api.md` for full API details.
|
||||
|
||||
## Event Streaming
|
||||
|
||||
`cline.subscribe()` emits `CoreSessionEvent` types. These are different from the `AgentRuntimeEvent` types emitted by the standalone `Agent` class -- see `../events/REFERENCE.md` for the full comparison.
|
||||
|
||||
```typescript
|
||||
cline.subscribe((event) => {
|
||||
switch (event.type) {
|
||||
case "chunk":
|
||||
if (event.payload.type === "text") {
|
||||
process.stdout.write(event.payload.text)
|
||||
}
|
||||
break
|
||||
case "ended":
|
||||
console.log(`Session ended: ${event.payload.finishReason}`)
|
||||
break
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
ClineCore results use `AgentResult` with `.text` (not `.outputText` like the standalone Agent's `AgentRunResult`).
|
||||
|
||||
## Session Persistence
|
||||
|
||||
Sessions are stored at:
|
||||
```
|
||||
~/.cline/data/sessions/
|
||||
sessions.db # SQLite database
|
||||
[session-id].json # Message history
|
||||
```
|
||||
|
||||
## Next Steps
|
||||
|
||||
- `api.md` - Full ClineCore API reference
|
||||
- `patterns.md` - Common patterns and best practices
|
||||
- `gotchas.md` - Pitfalls and debugging
|
||||
- `../tools/REFERENCE.md` - Custom tool creation
|
||||
- `../plugins/REFERENCE.md` - Plugin system
|
||||
- `../scheduling/REFERENCE.md` - Scheduled agents
|
||||
@@ -0,0 +1,304 @@
|
||||
# ClineCore API Reference
|
||||
|
||||
## Creating ClineCore
|
||||
|
||||
```typescript
|
||||
import { ClineCore } from "@cline/sdk"
|
||||
|
||||
const cline = await ClineCore.create(options: ClineCoreOptions)
|
||||
```
|
||||
|
||||
### ClineCoreOptions
|
||||
|
||||
```typescript
|
||||
interface ClineCoreOptions {
|
||||
clientName: string // identifies your app
|
||||
distinctId?: string // user/instance identifier
|
||||
backendMode?: "auto" | "local" | "hub" | "remote"
|
||||
hub?: HubOptions
|
||||
remote?: RemoteOptions
|
||||
capabilities?: RuntimeCapabilities
|
||||
toolPolicies?: Record<string, ToolPolicy>
|
||||
automation?: boolean | ClineCoreAutomationOptions
|
||||
fetch?: typeof fetch
|
||||
}
|
||||
```
|
||||
|
||||
### RuntimeCapabilities
|
||||
|
||||
```typescript
|
||||
interface RuntimeCapabilities {
|
||||
requestToolApproval?: (request: ToolApprovalRequest) => Promise<ToolApprovalResult>
|
||||
// ... other capability callbacks
|
||||
}
|
||||
```
|
||||
|
||||
## Starting Sessions
|
||||
|
||||
### start(input)
|
||||
|
||||
```typescript
|
||||
const session = await cline.start(input: ClineCoreStartInput)
|
||||
```
|
||||
|
||||
Returns a `StartSessionResult`:
|
||||
|
||||
```typescript
|
||||
interface StartSessionResult {
|
||||
sessionId: string
|
||||
manifest: SessionManifest
|
||||
manifestPath: string
|
||||
messagesPath: string
|
||||
result?: AgentResult
|
||||
}
|
||||
```
|
||||
|
||||
### ClineCoreStartInput
|
||||
|
||||
```typescript
|
||||
interface ClineCoreStartInput {
|
||||
prompt: string
|
||||
config: CoreSessionConfig
|
||||
source?: string
|
||||
interactive?: boolean
|
||||
sessionMetadata?: Record<string, unknown>
|
||||
initialMessages?: AgentMessage[]
|
||||
toolPolicies?: Record<string, ToolPolicy>
|
||||
capabilities?: RuntimeCapabilities
|
||||
}
|
||||
```
|
||||
|
||||
### CoreSessionConfig
|
||||
|
||||
```typescript
|
||||
interface CoreSessionConfig {
|
||||
cwd?: string // working directory
|
||||
providerId: string // LLM provider
|
||||
modelId: string // model identifier
|
||||
apiKey?: string // provider API key
|
||||
systemPrompt?: string // custom system prompt
|
||||
tools?: readonly AgentTool[] // additional custom tools
|
||||
enableTools?: boolean // enable built-in tools
|
||||
hooks?: Partial<AgentRuntimeHooks> // runtime hooks
|
||||
extensions?: AgentPlugin[] // plugins loaded inline
|
||||
pluginPaths?: string[] // paths to plugin packages
|
||||
extensionLoading?: "isolated" | "direct"
|
||||
extensionContext?: { // context passed to plugin setup()
|
||||
workspace?: { rootPath: string; cwd: string }
|
||||
}
|
||||
checkpointConfig?: CoreCheckpointConfig
|
||||
compactionConfig?: CoreCompactionConfig
|
||||
telemetry?: ITelemetryService
|
||||
logger?: BasicLogger
|
||||
enableSpawnAgent?: boolean // enable sub-agent spawning
|
||||
enableAgentTeams?: boolean // enable team coordination
|
||||
teamName?: string // team identifier
|
||||
}
|
||||
```
|
||||
|
||||
`extensions` passes plugin objects directly. `pluginPaths` points to directories with `package.json` containing a `cline.plugins` field. Set `extensionContext.workspace` so plugins receive `ctx.workspaceInfo` in their `setup()` call -- without it, `ctx.workspaceInfo` is undefined.
|
||||
|
||||
## Follow-Up Messages
|
||||
|
||||
### send({ sessionId, prompt })
|
||||
|
||||
Send a follow-up message to an existing session:
|
||||
|
||||
```typescript
|
||||
const result = await cline.send({
|
||||
sessionId: session.sessionId,
|
||||
prompt: "Now add authentication",
|
||||
})
|
||||
```
|
||||
|
||||
Returns `AgentResult | undefined`.
|
||||
|
||||
## Event Subscription
|
||||
|
||||
### subscribe(listener, options?)
|
||||
|
||||
```typescript
|
||||
const unsubscribe = cline.subscribe(
|
||||
(event: CoreSessionEvent) => {
|
||||
// handle events
|
||||
},
|
||||
{ sessionId: "optional-filter" }
|
||||
)
|
||||
```
|
||||
|
||||
### CoreSessionEvent
|
||||
|
||||
```typescript
|
||||
type CoreSessionEvent =
|
||||
| { type: "chunk"; payload: SessionChunkEvent }
|
||||
| { type: "agent_event"; payload: { sessionId: string, event: AgentEvent } }
|
||||
| { type: "ended"; payload: SessionEndedEvent }
|
||||
| { type: "team_progress"; payload: SessionTeamProgressEvent }
|
||||
| { type: "status"; payload: { sessionId: string, status: string } }
|
||||
| { type: "hook"; payload: SessionToolEvent }
|
||||
```
|
||||
|
||||
## Session Management
|
||||
|
||||
### list(limit?, options?)
|
||||
|
||||
```typescript
|
||||
const sessions: SessionRecord[] = await cline.list(50)
|
||||
```
|
||||
|
||||
### get(sessionId)
|
||||
|
||||
```typescript
|
||||
const session: SessionRecord = await cline.get(sessionId)
|
||||
```
|
||||
|
||||
### readMessages(sessionId)
|
||||
|
||||
```typescript
|
||||
const messages: AgentMessage[] = await cline.readMessages(sessionId)
|
||||
```
|
||||
|
||||
### getAccumulatedUsage(sessionId)
|
||||
|
||||
```typescript
|
||||
const usage = await cline.getAccumulatedUsage(sessionId)
|
||||
// usage.usage - root agent only
|
||||
// usage.aggregateUsage - root + subagents/teammates
|
||||
```
|
||||
|
||||
### update(sessionId, updates)
|
||||
|
||||
```typescript
|
||||
await cline.update(sessionId, { title: "New title" })
|
||||
```
|
||||
|
||||
### abort(sessionId, reason?)
|
||||
|
||||
```typescript
|
||||
await cline.abort(sessionId, "User cancelled")
|
||||
```
|
||||
|
||||
### stop(sessionId)
|
||||
|
||||
```typescript
|
||||
await cline.stop(sessionId)
|
||||
```
|
||||
|
||||
### delete(sessionId)
|
||||
|
||||
```typescript
|
||||
await cline.delete(sessionId)
|
||||
```
|
||||
|
||||
### restore(input)
|
||||
|
||||
Restore a session from a checkpoint:
|
||||
|
||||
```typescript
|
||||
await cline.restore({ sessionId, checkpointId })
|
||||
```
|
||||
|
||||
### dispose(reason?)
|
||||
|
||||
Clean up all resources. Always call this when done:
|
||||
|
||||
```typescript
|
||||
await cline.dispose("Shutting down")
|
||||
```
|
||||
|
||||
## AgentResult
|
||||
|
||||
Returned by session operations:
|
||||
|
||||
```typescript
|
||||
interface AgentResult {
|
||||
text: string
|
||||
usage: LegacyAgentUsage
|
||||
messages: MessageWithMetadata[]
|
||||
toolCalls: ToolCallRecord[]
|
||||
iterations: number
|
||||
finishReason: "completed" | "max_iterations" | "aborted" | "mistake_limit" | "error"
|
||||
model: { id: string; provider: string; info?: ModelInfo }
|
||||
startedAt: Date
|
||||
endedAt: Date
|
||||
durationMs: number
|
||||
}
|
||||
```
|
||||
|
||||
## Tool Policies
|
||||
|
||||
Control tool access at the session level:
|
||||
|
||||
```typescript
|
||||
const session = await cline.start({
|
||||
prompt: "Review the code",
|
||||
config: { ... },
|
||||
toolPolicies: {
|
||||
read_files: { autoApprove: true },
|
||||
bash: { autoApprove: false },
|
||||
editor: { enabled: false },
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
### ToolPolicy
|
||||
|
||||
```typescript
|
||||
interface ToolPolicy {
|
||||
enabled?: boolean // false = tool is hidden from the model
|
||||
autoApprove?: boolean // false = requires approval callback
|
||||
}
|
||||
```
|
||||
|
||||
## Interactive Approval
|
||||
|
||||
```typescript
|
||||
const cline = await ClineCore.create({
|
||||
clientName: "my-app",
|
||||
capabilities: {
|
||||
requestToolApproval: async (request) => {
|
||||
console.log(`Tool: ${request.toolName}, Input: ${JSON.stringify(request.input)}`)
|
||||
const approved = await askUser(`Allow ${request.toolName}?`)
|
||||
return { approved }
|
||||
},
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
## Automation API
|
||||
|
||||
When `automation` is enabled in `ClineCore.create()`:
|
||||
|
||||
```typescript
|
||||
const cline = await ClineCore.create({
|
||||
clientName: "my-app",
|
||||
automation: true,
|
||||
})
|
||||
|
||||
// Access automation methods
|
||||
cline.automation.start()
|
||||
cline.automation.stop()
|
||||
cline.automation.reconcile(specs)
|
||||
cline.automation.ingestEvent(event)
|
||||
cline.automation.listEvents()
|
||||
cline.automation.listSpecs()
|
||||
cline.automation.listRuns()
|
||||
```
|
||||
|
||||
## Settings API
|
||||
|
||||
```typescript
|
||||
// Read settings
|
||||
const settings = await cline.settings.list()
|
||||
|
||||
// Toggle tools, plugins, MCP servers
|
||||
await cline.settings.toggle({ type: "tool", name: "bash", enabled: true })
|
||||
```
|
||||
|
||||
## See Also
|
||||
|
||||
- `REFERENCE.md` - Overview and quick start
|
||||
- `patterns.md` - Common patterns
|
||||
- `gotchas.md` - Pitfalls
|
||||
- `../tools/REFERENCE.md` - Tool creation
|
||||
- `../plugins/REFERENCE.md` - Plugin system
|
||||
@@ -0,0 +1,148 @@
|
||||
# ClineCore Gotchas
|
||||
|
||||
## Always Call dispose()
|
||||
|
||||
`ClineCore` holds resources (file watchers, database connections, hub connections). Failing to call `dispose()` can leave orphan processes and file locks.
|
||||
|
||||
```typescript
|
||||
const cline = await ClineCore.create({ clientName: "my-app" })
|
||||
try {
|
||||
// ... use cline
|
||||
} finally {
|
||||
await cline.dispose()
|
||||
}
|
||||
```
|
||||
|
||||
## Node.js 22 Required
|
||||
|
||||
ClineCore and `@cline/core` require Node.js 22 or later. If you're on an older version, you'll get runtime errors. Check with `node --version`.
|
||||
|
||||
## Session Config vs Global Config
|
||||
|
||||
Tool policies can be set at two levels:
|
||||
- Global: in `ClineCore.create({ toolPolicies })` -- applies to all sessions
|
||||
- Per-session: in `cline.start({ toolPolicies })` -- overrides global for that session
|
||||
|
||||
Per-session policies take precedence.
|
||||
|
||||
## enableTools Must Be Explicit
|
||||
|
||||
Built-in tools (bash, editor, read_files, etc.) are not available unless you set `enableTools: true` in the session config:
|
||||
|
||||
```typescript
|
||||
await cline.start({
|
||||
prompt: "Read package.json",
|
||||
config: {
|
||||
providerId: "anthropic",
|
||||
modelId: "claude-sonnet-4-6",
|
||||
enableTools: true, // required for built-in tools
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
Without this, the agent only has access to custom tools you provide via `config.tools`.
|
||||
|
||||
## cwd Matters for Built-in Tools
|
||||
|
||||
Built-in tools like `bash`, `editor`, and `read_files` operate relative to `config.cwd`. If not set, they use the process working directory. Always set it explicitly for predictable behavior:
|
||||
|
||||
```typescript
|
||||
config: {
|
||||
cwd: "/absolute/path/to/project",
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
## Hub Startup Latency
|
||||
|
||||
With `backendMode: "auto"`, the first session may be slow if a hub daemon needs to be spawned. For immediate responsiveness:
|
||||
- Use `backendMode: "local"` for in-process execution (fastest startup)
|
||||
- Pre-warm the hub with `cline hub ensure` CLI command
|
||||
- Accept the one-time startup cost and let subsequent sessions reuse the hub
|
||||
|
||||
## Session Storage Location
|
||||
|
||||
Sessions are stored at `~/.cline/data/sessions/`. This includes:
|
||||
- `sessions.db` - SQLite database with session metadata
|
||||
- `[session-id].json` - Individual message history files
|
||||
|
||||
If you're running in a container or ephemeral environment, these paths may not persist across restarts.
|
||||
|
||||
## requestToolApproval Blocks Execution
|
||||
|
||||
When a tool policy has `autoApprove: false` and you provide a `requestToolApproval` callback, the agent loop blocks until your callback resolves. If your callback never resolves (e.g., waiting for user input that never comes), the session hangs.
|
||||
|
||||
For automated pipelines, either:
|
||||
- Set all tools to `autoApprove: true`
|
||||
- Implement a timeout in your approval callback
|
||||
|
||||
## Plugin Discovery Paths
|
||||
|
||||
ClineCore discovers plugins from:
|
||||
- Global: `~/.cline/plugins/`
|
||||
- Workspace: `.cline/plugins/`
|
||||
|
||||
For SDK consumers, pass plugins via `extensions: [plugin]` or `pluginPaths: ["./path"]` in the session config.
|
||||
|
||||
If a plugin isn't loading, verify:
|
||||
- The file is in one of the discovery directories, or passed via `extensions`/`pluginPaths`
|
||||
- The file exports a default plugin object with a non-empty `manifest.capabilities` array
|
||||
- Every `api.register*` call in `setup()` has a matching capability declared
|
||||
- If `hooks` is present on the plugin, `"hooks"` is in `capabilities`
|
||||
|
||||
## extensionContext.workspace Is Required for Plugins
|
||||
|
||||
If your plugins use `ctx.workspaceInfo` (e.g., to resolve workspace paths), you must set `extensionContext.workspace` in the session config. Without it, `ctx.workspaceInfo` is undefined:
|
||||
|
||||
```typescript
|
||||
await cline.start({
|
||||
config: {
|
||||
extensions: [myPlugin],
|
||||
extensionContext: {
|
||||
workspace: { rootPath: process.cwd(), cwd: process.cwd() },
|
||||
},
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
The CLI sets this automatically, but SDK consumers must set it explicitly.
|
||||
|
||||
## send() Requires an Active Session
|
||||
|
||||
`cline.send()` only works on sessions that are still active. If a session has already completed, `send()` may return `undefined` or fail. Check session status with `cline.get(sessionId)` first.
|
||||
|
||||
## Result May Be Undefined
|
||||
|
||||
`session.result` can be `undefined` if the session was started but hasn't completed yet (e.g., in a non-blocking hub mode). Check for this:
|
||||
|
||||
```typescript
|
||||
const session = await cline.start({ ... })
|
||||
if (session.result) {
|
||||
console.log(session.result.text)
|
||||
} else {
|
||||
console.log("Session started but not yet complete")
|
||||
}
|
||||
```
|
||||
|
||||
## Compaction and Long Sessions
|
||||
|
||||
For long-running sessions, message history grows and eventually exceeds the model's context window. ClineCore handles this via compaction, which summarizes older messages. Configure it via `compactionConfig`:
|
||||
|
||||
```typescript
|
||||
config: {
|
||||
compactionConfig: {
|
||||
strategy: "summarize",
|
||||
// ...
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
The default strategy works for most cases, but extremely long sessions may benefit from tuning.
|
||||
|
||||
## See Also
|
||||
|
||||
- `api.md` - Full API reference
|
||||
- `patterns.md` - Common patterns
|
||||
- `../agent/gotchas.md` - Agent-level gotchas
|
||||
- `../tools/REFERENCE.md` - Tool troubleshooting
|
||||
- `../providers/REFERENCE.md` - Provider troubleshooting
|
||||
@@ -0,0 +1,279 @@
|
||||
# ClineCore Patterns
|
||||
|
||||
## Basic Session with Built-in Tools
|
||||
|
||||
```typescript
|
||||
import { ClineCore } from "@cline/sdk"
|
||||
|
||||
const cline = await ClineCore.create({ clientName: "my-app" })
|
||||
|
||||
const session = await cline.start({
|
||||
prompt: "Read package.json and summarize the dependencies",
|
||||
config: {
|
||||
providerId: "anthropic",
|
||||
modelId: "claude-sonnet-4-6",
|
||||
apiKey: process.env.ANTHROPIC_API_KEY,
|
||||
cwd: process.cwd(),
|
||||
enableTools: true,
|
||||
},
|
||||
})
|
||||
|
||||
console.log(session.result?.text)
|
||||
await cline.dispose()
|
||||
```
|
||||
|
||||
## Streaming Session with UI Updates
|
||||
|
||||
```typescript
|
||||
const cline = await ClineCore.create({ clientName: "my-app" })
|
||||
|
||||
cline.subscribe((event) => {
|
||||
switch (event.type) {
|
||||
case "chunk":
|
||||
if (event.payload.type === "text") {
|
||||
ui.appendText(event.payload.text)
|
||||
}
|
||||
break
|
||||
case "ended":
|
||||
ui.showComplete(event.payload.finishReason)
|
||||
break
|
||||
}
|
||||
})
|
||||
|
||||
await cline.start({
|
||||
prompt: "Refactor the auth module",
|
||||
config: {
|
||||
providerId: "anthropic",
|
||||
modelId: "claude-sonnet-4-6",
|
||||
cwd: "/path/to/project",
|
||||
enableTools: true,
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
## Multi-Turn Session
|
||||
|
||||
```typescript
|
||||
const cline = await ClineCore.create({ clientName: "my-app" })
|
||||
|
||||
const session = await cline.start({
|
||||
prompt: "Create a new Express server",
|
||||
config: {
|
||||
providerId: "anthropic",
|
||||
modelId: "claude-sonnet-4-6",
|
||||
cwd: "/path/to/project",
|
||||
enableTools: true,
|
||||
},
|
||||
})
|
||||
|
||||
// Follow-up
|
||||
const result = await cline.send({
|
||||
sessionId: session.sessionId,
|
||||
prompt: "Now add a health check endpoint",
|
||||
})
|
||||
|
||||
console.log(result?.text)
|
||||
await cline.dispose()
|
||||
```
|
||||
|
||||
## Tiered Permission Model
|
||||
|
||||
Auto-approve reads, require approval for writes:
|
||||
|
||||
```typescript
|
||||
const cline = await ClineCore.create({
|
||||
clientName: "my-app",
|
||||
toolPolicies: {
|
||||
read_files: { autoApprove: true },
|
||||
search: { autoApprove: true },
|
||||
fetch_web: { autoApprove: true },
|
||||
bash: { autoApprove: false },
|
||||
editor: { autoApprove: false },
|
||||
apply_patch: { autoApprove: false },
|
||||
},
|
||||
capabilities: {
|
||||
requestToolApproval: async (request) => {
|
||||
const approved = await promptUser(
|
||||
`Allow ${request.toolName}?\n${JSON.stringify(request.input, null, 2)}`
|
||||
)
|
||||
return { approved }
|
||||
},
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
## Custom Tools Alongside Built-ins
|
||||
|
||||
```typescript
|
||||
import { ClineCore, createTool } from "@cline/sdk"
|
||||
import { z } from "zod"
|
||||
|
||||
const deployTool = createTool({
|
||||
name: "deploy",
|
||||
description: "Deploy the application to the specified environment.",
|
||||
inputSchema: z.object({
|
||||
environment: z.enum(["staging", "production"]),
|
||||
}),
|
||||
execute: async (input) => {
|
||||
const result = await runDeployment(input.environment)
|
||||
return { url: result.url, status: "deployed" }
|
||||
},
|
||||
})
|
||||
|
||||
const cline = await ClineCore.create({ clientName: "my-app" })
|
||||
|
||||
await cline.start({
|
||||
prompt: "Deploy the app to staging",
|
||||
config: {
|
||||
providerId: "anthropic",
|
||||
modelId: "claude-sonnet-4-6",
|
||||
cwd: process.cwd(),
|
||||
enableTools: true,
|
||||
tools: [deployTool],
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
## Session with Plugins
|
||||
|
||||
Load plugins inline with `extensions` and provide workspace context so plugins can access `ctx.workspaceInfo`:
|
||||
|
||||
```typescript
|
||||
import { ClineCore } from "@cline/sdk"
|
||||
import myPlugin from "./my-plugin"
|
||||
|
||||
const cline = await ClineCore.create({
|
||||
clientName: "my-app",
|
||||
backendMode: "local",
|
||||
})
|
||||
|
||||
await cline.start({
|
||||
prompt: "Do the thing my plugin enables",
|
||||
config: {
|
||||
providerId: "anthropic",
|
||||
modelId: "claude-sonnet-4-6",
|
||||
cwd: process.cwd(),
|
||||
enableTools: true,
|
||||
extensions: [myPlugin],
|
||||
extensionContext: {
|
||||
workspace: { rootPath: process.cwd(), cwd: process.cwd() },
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
await cline.dispose()
|
||||
```
|
||||
|
||||
For directory-based plugin packages, use `pluginPaths` instead:
|
||||
|
||||
```typescript
|
||||
config: {
|
||||
pluginPaths: ["./my-cline-plugin"],
|
||||
extensionContext: {
|
||||
workspace: { rootPath: process.cwd(), cwd: process.cwd() },
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
See `../plugins/REFERENCE.md` for the full plugin authoring guide.
|
||||
|
||||
## Session Listing and Replay
|
||||
|
||||
```typescript
|
||||
const cline = await ClineCore.create({ clientName: "my-app" })
|
||||
|
||||
// List recent sessions
|
||||
const sessions = await cline.list(10)
|
||||
for (const session of sessions) {
|
||||
console.log(`${session.id}: ${session.title}`)
|
||||
}
|
||||
|
||||
// Read messages from a past session
|
||||
const messages = await cline.readMessages(sessions[0].id)
|
||||
for (const msg of messages) {
|
||||
console.log(`[${msg.role}] ${msg.content}`)
|
||||
}
|
||||
|
||||
// Check usage
|
||||
const usage = await cline.getAccumulatedUsage(sessions[0].id)
|
||||
console.log(`Total tokens: ${usage.aggregateUsage.totalInputTokens + usage.aggregateUsage.totalOutputTokens}`)
|
||||
```
|
||||
|
||||
## Graceful Shutdown
|
||||
|
||||
```typescript
|
||||
const cline = await ClineCore.create({ clientName: "my-app" })
|
||||
|
||||
process.on("SIGTERM", async () => {
|
||||
await cline.dispose("SIGTERM received")
|
||||
process.exit(0)
|
||||
})
|
||||
|
||||
// Run sessions...
|
||||
```
|
||||
|
||||
## Stateless Worker Pattern
|
||||
|
||||
For request/response workloads (API endpoints, queue consumers):
|
||||
|
||||
```typescript
|
||||
import { ClineCore } from "@cline/sdk"
|
||||
|
||||
const cline = await ClineCore.create({
|
||||
clientName: "worker",
|
||||
backendMode: "local",
|
||||
})
|
||||
|
||||
async function handleRequest(prompt: string, workspace: string) {
|
||||
const session = await cline.start({
|
||||
prompt,
|
||||
config: {
|
||||
providerId: "anthropic",
|
||||
modelId: "claude-sonnet-4-6",
|
||||
cwd: workspace,
|
||||
enableTools: true,
|
||||
},
|
||||
})
|
||||
|
||||
return {
|
||||
text: session.result?.text,
|
||||
usage: session.result?.usage,
|
||||
sessionId: session.sessionId,
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Hub-Backed Multi-Client
|
||||
|
||||
Multiple clients can attach to the same session:
|
||||
|
||||
```typescript
|
||||
// Process 1: start session
|
||||
const cline = await ClineCore.create({
|
||||
clientName: "backend",
|
||||
backendMode: "hub",
|
||||
})
|
||||
|
||||
const session = await cline.start({
|
||||
prompt: "Long running refactor task",
|
||||
config: { ... },
|
||||
})
|
||||
|
||||
// Process 2: attach and stream events
|
||||
const viewer = await ClineCore.create({
|
||||
clientName: "dashboard",
|
||||
backendMode: "hub",
|
||||
})
|
||||
|
||||
viewer.subscribe((event) => {
|
||||
dashboard.render(event)
|
||||
}, { sessionId: session.sessionId })
|
||||
```
|
||||
|
||||
## See Also
|
||||
|
||||
- `api.md` - Full API reference
|
||||
- `gotchas.md` - Common pitfalls
|
||||
- `../tools/REFERENCE.md` - Tool creation
|
||||
- `../plugins/REFERENCE.md` - Plugin system
|
||||
- `../scheduling/REFERENCE.md` - Scheduled agents
|
||||
@@ -0,0 +1,269 @@
|
||||
# Events
|
||||
|
||||
The Cline SDK has three event layers. Which one you use depends on whether you're working with the standalone `Agent` class or `ClineCore`.
|
||||
|
||||
## Which Events Do I Get?
|
||||
|
||||
| If you use... | You subscribe with... | You receive... | Text streaming event |
|
||||
|---|---|---|---|
|
||||
| Standalone `Agent` | `agent.subscribe()` | `AgentRuntimeEvent` | `assistant-text-delta` |
|
||||
| `ClineCore` | `cline.subscribe()` | `CoreSessionEvent` | `chunk` (with `payload.type === "text"`) |
|
||||
|
||||
These are different event types with different shapes. Do not mix them up.
|
||||
|
||||
## Layer 1: AgentRuntimeEvent (Standalone Agent)
|
||||
|
||||
Emitted by the `Agent` class via `agent.subscribe()`. This is what you get when using `new Agent(...)` directly. Every event includes a `snapshot` field with the current `AgentRuntimeStateSnapshot`.
|
||||
|
||||
### Run Lifecycle
|
||||
|
||||
```typescript
|
||||
{ type: "run-started", snapshot }
|
||||
{ type: "run-finished", snapshot, result: AgentRunResult }
|
||||
{ type: "run-failed", snapshot, error: Error }
|
||||
```
|
||||
|
||||
### Turns
|
||||
|
||||
```typescript
|
||||
{ type: "turn-started", snapshot, iteration: number }
|
||||
{ type: "turn-finished", snapshot, iteration: number, toolCallCount: number }
|
||||
```
|
||||
|
||||
### Text Streaming
|
||||
|
||||
```typescript
|
||||
// Streaming text delta (arrives as chunks during generation)
|
||||
{ type: "assistant-text-delta", snapshot, iteration: number, text: string, accumulatedText: string }
|
||||
|
||||
// Streaming reasoning delta (when model uses extended thinking)
|
||||
{ type: "assistant-reasoning-delta", snapshot, iteration: number, text: string }
|
||||
|
||||
// Complete assistant message after model finishes
|
||||
{ type: "assistant-message", snapshot, iteration: number, message: AgentMessage, finishReason: string }
|
||||
```
|
||||
|
||||
### Messages
|
||||
|
||||
```typescript
|
||||
// Fired when any message (user or assistant) is added to conversation history
|
||||
{ type: "message-added", snapshot, message: AgentMessage }
|
||||
```
|
||||
|
||||
### Tool Events
|
||||
|
||||
```typescript
|
||||
{ type: "tool-started", snapshot, toolCall: { toolName: string, toolCallId: string, input: unknown } }
|
||||
{ type: "tool-updated", snapshot, toolCall: { toolName: string, toolCallId: string }, update: string }
|
||||
{ type: "tool-finished", snapshot, toolCall: { toolName: string, toolCallId: string }, message: AgentMessage }
|
||||
```
|
||||
|
||||
### Usage
|
||||
|
||||
```typescript
|
||||
{
|
||||
type: "usage-updated",
|
||||
snapshot,
|
||||
usage: {
|
||||
inputTokens: number,
|
||||
outputTokens: number,
|
||||
cacheReadTokens?: number,
|
||||
cacheWriteTokens?: number,
|
||||
totalCost?: number,
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
### Notices
|
||||
|
||||
```typescript
|
||||
{ type: "status-notice", snapshot, message: string, metadata?: Record<string, unknown> }
|
||||
```
|
||||
|
||||
### Subscribing
|
||||
|
||||
Use `agent.subscribe()`. Register the listener before calling `run()` to avoid missing early events.
|
||||
|
||||
```typescript
|
||||
const agent = new Agent({
|
||||
providerId: "anthropic",
|
||||
modelId: "claude-sonnet-4-6",
|
||||
apiKey: process.env.ANTHROPIC_API_KEY,
|
||||
systemPrompt: "You are a helpful assistant.",
|
||||
tools: [],
|
||||
})
|
||||
|
||||
agent.subscribe((event) => {
|
||||
switch (event.type) {
|
||||
case "assistant-text-delta":
|
||||
process.stdout.write(event.text)
|
||||
break
|
||||
case "tool-started":
|
||||
console.log(`\nUsing tool: ${event.toolCall.toolName}`)
|
||||
break
|
||||
case "usage-updated":
|
||||
console.log(`Cost: $${event.usage.totalCost?.toFixed(4)}`)
|
||||
break
|
||||
case "run-finished":
|
||||
console.log(`\nDone: ${event.result.status}`)
|
||||
break
|
||||
}
|
||||
})
|
||||
|
||||
const result = await agent.run("Hello!")
|
||||
```
|
||||
|
||||
You can also receive events through hooks (these are awaited, so they can be async):
|
||||
|
||||
```typescript
|
||||
const agent = new Agent({
|
||||
...config,
|
||||
hooks: {
|
||||
onEvent: async (event) => {
|
||||
// Same AgentRuntimeEvent types as subscribe()
|
||||
},
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
## Layer 2: AgentEvent (ClineCore Internal)
|
||||
|
||||
When using `ClineCore`, a `RuntimeEventAdapter` translates Layer 1 events into a legacy format called `AgentEvent`. You do not interact with this layer directly -- it is projected into `CoreSessionEvent` for subscribers. The key mappings:
|
||||
|
||||
| AgentRuntimeEvent (Layer 1) | AgentEvent (Layer 2) |
|
||||
|---|---|
|
||||
| `turn-started` | `iteration_start` |
|
||||
| `turn-finished` | `iteration_end` |
|
||||
| `assistant-text-delta` | `content_start` (text) |
|
||||
| `assistant-message` | `content_end` (text) |
|
||||
| `tool-started` | `content_start` (tool) |
|
||||
| `tool-updated` | `content_update` (tool) |
|
||||
| `tool-finished` | `content_end` (tool) |
|
||||
| `usage-updated` | `usage` (with computed deltas) |
|
||||
| `run-finished` | `done` |
|
||||
| `run-failed` | `error` |
|
||||
| `run-started`, `message-added` | (suppressed, not emitted) |
|
||||
|
||||
This layer exists for backwards compatibility. If you see event types like `content_update` or `iteration_start` in other documentation, they refer to this layer, not to what `agent.subscribe()` emits.
|
||||
|
||||
## Layer 3: CoreSessionEvent (ClineCore Subscriber)
|
||||
|
||||
Emitted by `ClineCore` via `cline.subscribe()`. These are higher-level session events.
|
||||
|
||||
```typescript
|
||||
type CoreSessionEvent =
|
||||
| { type: "chunk"; payload: SessionChunkEvent }
|
||||
| { type: "agent_event"; payload: { sessionId: string, event: AgentEvent } }
|
||||
| { type: "ended"; payload: SessionEndedEvent }
|
||||
| { type: "team_progress"; payload: SessionTeamProgressEvent }
|
||||
| { type: "status"; payload: { sessionId: string, status: string } }
|
||||
| { type: "hook"; payload: SessionToolEvent }
|
||||
```
|
||||
|
||||
### SessionChunkEvent
|
||||
|
||||
```typescript
|
||||
interface SessionChunkEvent {
|
||||
type: "text" | "reasoning"
|
||||
text: string
|
||||
sessionId: string
|
||||
}
|
||||
```
|
||||
|
||||
### SessionEndedEvent
|
||||
|
||||
```typescript
|
||||
interface SessionEndedEvent {
|
||||
sessionId: string
|
||||
finishReason: "completed" | "max_iterations" | "aborted" | "mistake_limit" | "error"
|
||||
result?: AgentResult
|
||||
}
|
||||
```
|
||||
|
||||
### Subscribing
|
||||
|
||||
```typescript
|
||||
cline.subscribe((event) => {
|
||||
switch (event.type) {
|
||||
case "chunk":
|
||||
if (event.payload.type === "text") {
|
||||
process.stdout.write(event.payload.text)
|
||||
}
|
||||
break
|
||||
case "ended":
|
||||
console.log(`Finished: ${event.payload.finishReason}`)
|
||||
break
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
Filter by session:
|
||||
|
||||
```typescript
|
||||
cline.subscribe(handler, { sessionId: "specific-session-id" })
|
||||
```
|
||||
|
||||
## Hub Events (Layer 3b)
|
||||
|
||||
When ClineCore runs in hub mode (via `backendMode: "hub"` or `"auto"` when a hub is available), events are projected over WebSocket using `HubEventName` types like `assistant.delta`, `iteration.started`, `tool.started`, etc. You do not interact with these directly -- `cline.subscribe()` still gives you `CoreSessionEvent` regardless of backend mode.
|
||||
|
||||
## Result Type Differences
|
||||
|
||||
The standalone Agent and ClineCore return different result types:
|
||||
|
||||
| API | Result type | Text property |
|
||||
|---|---|---|
|
||||
| `agent.run()` | `AgentRunResult` | `result.outputText` |
|
||||
| `cline.start()` / `cline.send()` | `AgentResult` | `result.text` |
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Streaming Text (Standalone Agent)
|
||||
|
||||
```typescript
|
||||
agent.subscribe((event) => {
|
||||
if (event.type === "assistant-text-delta") {
|
||||
process.stdout.write(event.text)
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### Streaming Text (ClineCore)
|
||||
|
||||
```typescript
|
||||
cline.subscribe((event) => {
|
||||
if (event.type === "chunk" && event.payload.type === "text") {
|
||||
process.stdout.write(event.payload.text)
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### Usage Tracking (Standalone Agent)
|
||||
|
||||
```typescript
|
||||
agent.subscribe((event) => {
|
||||
if (event.type === "usage-updated" && event.usage.totalCost) {
|
||||
console.log(`Running cost: $${event.usage.totalCost.toFixed(4)}`)
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### Tool Call Logging (Standalone Agent)
|
||||
|
||||
```typescript
|
||||
agent.subscribe((event) => {
|
||||
if (event.type === "tool-started") {
|
||||
console.log(`Tool started: ${event.toolCall.toolName}`)
|
||||
}
|
||||
if (event.type === "tool-finished") {
|
||||
console.log(`Tool finished: ${event.toolCall.toolName}`)
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
## See Also
|
||||
|
||||
- `../agent/REFERENCE.md` - Agent runtime overview
|
||||
- `../clinecore/REFERENCE.md` - ClineCore session management
|
||||
- `../plugins/REFERENCE.md` - Plugin hooks for lifecycle events
|
||||
- `../production/REFERENCE.md` - Observability in production
|
||||
@@ -0,0 +1,157 @@
|
||||
# Multi-Agent Coordination
|
||||
|
||||
The Cline SDK supports two models for multi-agent work: sub-agents (parent-child) and teams (peer-to-peer).
|
||||
|
||||
## Sub-Agents vs Teams
|
||||
|
||||
| Feature | Sub-Agents | Teams |
|
||||
|---------|-----------|-------|
|
||||
| Enable with | `enableSpawnAgent: true` | `enableAgentTeams: true` |
|
||||
| Persistence | Session-scoped only | Across sessions |
|
||||
| Coordination | Parent-child hierarchy | Peer-to-peer |
|
||||
| Shared state | None | Task board, mailbox, mission log |
|
||||
| Best for | One-off delegation | Complex multi-session projects |
|
||||
|
||||
## Sub-Agents
|
||||
|
||||
Sub-agents are spawned by a parent agent during a run. They execute independently and report results back.
|
||||
|
||||
### Enabling Sub-Agents
|
||||
|
||||
```typescript
|
||||
const cline = await ClineCore.create({ clientName: "my-app" })
|
||||
|
||||
await cline.start({
|
||||
prompt: "Refactor the auth module and update tests",
|
||||
config: {
|
||||
providerId: "anthropic",
|
||||
modelId: "claude-sonnet-4-6",
|
||||
enableSpawnAgent: true,
|
||||
enableTools: true,
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
When `enableSpawnAgent` is true, the agent gets access to sub-agent tools:
|
||||
|
||||
| Tool | Description |
|
||||
|------|-------------|
|
||||
| `start_subagent` | Spawn a background agent with a task |
|
||||
| `message_subagent` | Send a message to a running sub-agent |
|
||||
| `handoff_to_agent` | Delegate the current task entirely |
|
||||
| `submit_and_exit` | Signal completion |
|
||||
|
||||
### How Sub-Agents Work
|
||||
|
||||
1. The parent agent decides a subtask can be delegated
|
||||
2. It calls `start_subagent` with a role, task description, and optionally a preset
|
||||
3. The sub-agent runs independently in the background
|
||||
4. The parent can check status or send follow-up messages
|
||||
5. Sub-agent results are available to the parent when complete
|
||||
|
||||
## Teams
|
||||
|
||||
Teams provide persistent, cross-session coordination between agents.
|
||||
|
||||
### Enabling Teams
|
||||
|
||||
```typescript
|
||||
await cline.start({
|
||||
config: {
|
||||
providerId: "anthropic",
|
||||
modelId: "claude-sonnet-4-6",
|
||||
enableAgentTeams: true,
|
||||
teamName: "auth-sprint",
|
||||
enableTools: true,
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
### Team Tools
|
||||
|
||||
When `enableAgentTeams` is true, the coordinator agent gets:
|
||||
|
||||
| Tool | Description |
|
||||
|------|-------------|
|
||||
| `team_spawn_teammate` | Create a new agent with a role and task |
|
||||
| `team_delegate_task` | Assign a task to an existing teammate |
|
||||
| `team_check_status` | Check on a delegated task's progress |
|
||||
| `team_get_result` | Get the completed result from a teammate |
|
||||
|
||||
### Team Persistence
|
||||
|
||||
Teams store shared state in:
|
||||
|
||||
```
|
||||
~/.cline/data/teams/[team-name]/
|
||||
task-board.json # task assignments and status
|
||||
mailbox.json # inter-agent messages
|
||||
mission-log.json # coordination log
|
||||
```
|
||||
|
||||
This state persists across sessions, so team members can pick up where they left off.
|
||||
|
||||
### CLI Team Access
|
||||
|
||||
```bash
|
||||
cline --team-name auth-sprint "Continue the auth refactor"
|
||||
```
|
||||
|
||||
## Choosing Between Sub-Agents and Teams
|
||||
|
||||
Use sub-agents when:
|
||||
- You need one-off parallel execution within a single session
|
||||
- Tasks are independent and don't need to communicate with each other
|
||||
- Results only matter to the parent agent
|
||||
|
||||
Use teams when:
|
||||
- Work spans multiple sessions over time
|
||||
- Agents need to coordinate and share progress
|
||||
- Tasks have dependencies between them
|
||||
- You want a persistent record of multi-agent collaboration
|
||||
|
||||
## Patterns
|
||||
|
||||
### Parallel Research with Sub-Agents
|
||||
|
||||
A parent agent spawns multiple sub-agents to research different topics simultaneously:
|
||||
|
||||
```typescript
|
||||
await cline.start({
|
||||
prompt: `Research these three topics in parallel:
|
||||
1. Current best practices for JWT auth
|
||||
2. OAuth 2.0 provider comparison
|
||||
3. Session management patterns
|
||||
Spawn a sub-agent for each topic, then synthesize the results.`,
|
||||
config: {
|
||||
enableSpawnAgent: true,
|
||||
enableTools: true,
|
||||
// ...
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
### Team Sprint
|
||||
|
||||
A coordinator manages a multi-session project:
|
||||
|
||||
```typescript
|
||||
await cline.start({
|
||||
prompt: `You are the coordinator for the auth-sprint team.
|
||||
Review the task board and delegate the next highest-priority task
|
||||
to a teammate. Check status on any in-progress tasks.`,
|
||||
config: {
|
||||
enableAgentTeams: true,
|
||||
teamName: "auth-sprint",
|
||||
enableTools: true,
|
||||
// ...
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
## See Also
|
||||
|
||||
- `../clinecore/REFERENCE.md` - ClineCore runtime
|
||||
- `../clinecore/api.md` - Session config for teams
|
||||
- `../tools/REFERENCE.md` - Tool system
|
||||
- `../plugins/REFERENCE.md` - Plugin system
|
||||
@@ -0,0 +1,649 @@
|
||||
# Plugins
|
||||
|
||||
A Cline plugin is a TypeScript module that extends any agent built on the Cline SDK. The same plugin runs in the Cline CLI, VS Code and JetBrains extensions, and any custom app built on `@cline/core`.
|
||||
|
||||
A plugin can:
|
||||
|
||||
- Register tools the model can call.
|
||||
- Hook into the agent loop before/after runs, model calls, and tool calls.
|
||||
- Rewrite provider messages before they hit the model (custom compaction, redaction, context shaping).
|
||||
- Register slash commands, prompt rules, providers, and automation event types.
|
||||
|
||||
A plugin ships in one of two shapes:
|
||||
|
||||
1. Single-file plugin -- one `.ts` file that exports a default plugin object. Drop it in a discovery folder and it loads.
|
||||
2. Plugin package -- a directory with `package.json`, npm dependencies, and optionally bundled assets. Installable via `cline plugin install`.
|
||||
|
||||
Both shapes use the same plugin API.
|
||||
|
||||
## The Mental Model
|
||||
|
||||
When the host starts a session, it builds a registry of plugins and runs four phases:
|
||||
|
||||
1. resolve -- collect the plugin objects.
|
||||
2. validate -- check each plugin's `manifest`. Capabilities must be non-empty; declared hook stages must have matching handlers; if `hooks` is present, `"hooks"` must be in `capabilities`.
|
||||
3. setup -- call each plugin's `setup(api, ctx)` once. This is where you `registerTool`, `registerCommand`, etc.
|
||||
4. activate -- registry is frozen, the agent loop starts, and your hooks/tools are live.
|
||||
|
||||
Two invariants the registry enforces:
|
||||
|
||||
- Every contribution requires a matching capability. Calling `api.registerRule(...)` without `"rules"` in `manifest.capabilities` throws.
|
||||
- Capabilities and handlers must agree. Declaring `"hooks"` without a `hooks` object, or vice versa, fails validation.
|
||||
|
||||
After validation, registration is one-shot -- no dynamic register/unregister during the session.
|
||||
|
||||
## The Smallest Working Plugin
|
||||
|
||||
```typescript
|
||||
import type { AgentPlugin } from "@cline/core"
|
||||
import { createTool } from "@cline/core"
|
||||
|
||||
const plugin: AgentPlugin = {
|
||||
name: "hello-plugin",
|
||||
manifest: {
|
||||
capabilities: ["tools"],
|
||||
},
|
||||
setup(api, ctx) {
|
||||
api.registerTool(
|
||||
createTool({
|
||||
name: "say_hello",
|
||||
description: "Greet a person by name.",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: { name: { type: "string" } },
|
||||
required: ["name"],
|
||||
},
|
||||
async execute({ name }: { name: string }) {
|
||||
return { greeting: `Hello, ${name}!` }
|
||||
},
|
||||
}),
|
||||
)
|
||||
},
|
||||
}
|
||||
|
||||
export default plugin
|
||||
```
|
||||
|
||||
The agent will see `say_hello` as a callable tool.
|
||||
|
||||
## The Manifest
|
||||
|
||||
```typescript
|
||||
manifest: {
|
||||
capabilities: ["tools", "hooks"], // required, non-empty array
|
||||
paths?: string[], // optional, multi-entry packages
|
||||
providerIds?: string[], // optional, provider plugins
|
||||
modelIds?: string[], // optional, model plugins
|
||||
}
|
||||
```
|
||||
|
||||
### The Complete Capability List
|
||||
|
||||
| Capability | What It Unlocks in `api` |
|
||||
|-----------|--------------------------|
|
||||
| `"tools"` | `api.registerTool()` |
|
||||
| `"commands"` | `api.registerCommand()` (slash commands in chat surfaces) |
|
||||
| `"rules"` | `api.registerRule()` (string injected into the system prompt) |
|
||||
| `"messageBuilders"` | `api.registerMessageBuilder()` (rewrites provider-bound messages) |
|
||||
| `"providers"` | `api.registerProvider()` (custom model provider) |
|
||||
| `"automationEvents"` | `api.registerAutomationEventType()` and `ctx.automation?.ingestEvent()` |
|
||||
| `"hooks"` | The runtime `hooks` object on the plugin (lifecycle callbacks) |
|
||||
|
||||
You declare any combination -- most real plugins need 1-3 capabilities.
|
||||
|
||||
## setup(api, ctx) -- The Registration Phase
|
||||
|
||||
`setup()` runs once per session before the agent loop starts. Everything you register here is frozen for the lifetime of the session.
|
||||
|
||||
### The api Object
|
||||
|
||||
Each `register*` method requires the matching capability in your manifest:
|
||||
|
||||
```typescript
|
||||
api.registerTool(tool) // requires "tools"
|
||||
api.registerCommand({ name, description, handler }) // requires "commands"
|
||||
api.registerRule({ id, content, source }) // requires "rules"
|
||||
api.registerMessageBuilder({ name, build }) // requires "messageBuilders"
|
||||
api.registerProvider({ name, description }) // requires "providers"
|
||||
api.registerAutomationEventType({ eventType, source }) // requires "automationEvents"
|
||||
```
|
||||
|
||||
### The ctx Object -- Host-Provided Session Context
|
||||
|
||||
The second argument carries everything the host knows about the current session. All fields are optional, so feature-detect before using them -- the same plugin must work in hosts that supply less context (unit tests, sandboxed plugin processes).
|
||||
|
||||
```typescript
|
||||
ctx.session?.sessionId // string, stable core session id
|
||||
ctx.client?.name // host: "cline-cli", "cline-vscode", etc.
|
||||
ctx.user // authenticated user/org info, when available
|
||||
ctx.workspaceInfo // { rootPath, hint, latestGitBranchName,
|
||||
// latestGitCommitHash, associatedRemoteUrls }
|
||||
ctx.automation?.ingestEvent // emit normalized automation events
|
||||
ctx.logger?.log // structured logger scoped to this plugin
|
||||
ctx.telemetry // ITelemetryService, only present in-process
|
||||
```
|
||||
|
||||
Two rules about `ctx.workspaceInfo`:
|
||||
|
||||
1. Always prefer `ctx.workspaceInfo?.rootPath` over `process.cwd()`. The CLI may have been launched with `--cwd` without calling `chdir`, and VS Code workspaces don't share a single CWD. `workspaceInfo` is sourced from the session config and is always correct.
|
||||
2. Don't use `import.meta.url` tricks to find "the workspace". That gives you the plugin's own location, not the user's project.
|
||||
|
||||
### Persisting State Across Hooks
|
||||
|
||||
`setup()` runs first; hooks fire later. The simplest way to share state is module-level variables:
|
||||
|
||||
```typescript
|
||||
let sessionWorkspaceRoot: string | undefined
|
||||
let sessionBranch: string | undefined
|
||||
|
||||
const plugin: AgentPlugin = {
|
||||
name: "metrics",
|
||||
manifest: { capabilities: ["hooks"] },
|
||||
setup(api, ctx) {
|
||||
sessionWorkspaceRoot = ctx.workspaceInfo?.rootPath
|
||||
sessionBranch = ctx.workspaceInfo?.latestGitBranchName
|
||||
},
|
||||
hooks: {
|
||||
beforeTool({ toolCall, input }) {
|
||||
if (sessionBranch === "main" && toolCall.toolName === "run_commands") {
|
||||
// inspect input, optionally block
|
||||
}
|
||||
return undefined
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
A single Node process may host multiple sessions concurrently. If your plugin will run in a multi-session host, key your state by `ctx.session?.sessionId`:
|
||||
|
||||
```typescript
|
||||
const stateBySession = new Map<string, MyState>()
|
||||
setup(api, ctx) {
|
||||
const id = ctx.session?.sessionId
|
||||
if (id) stateBySession.set(id, /* ... */)
|
||||
}
|
||||
```
|
||||
|
||||
## Runtime Hooks
|
||||
|
||||
Runtime hooks are typed in-process callbacks on the same hook layer the runtime uses internally. They run inside the agent loop with full type information -- no IPC, no JSON marshaling.
|
||||
|
||||
Declare `"hooks"` in `manifest.capabilities`, then add a `hooks` property:
|
||||
|
||||
```typescript
|
||||
const plugin: AgentPlugin = {
|
||||
name: "metrics",
|
||||
manifest: { capabilities: ["hooks"] },
|
||||
hooks: {
|
||||
beforeRun(ctx) { /* ... */ },
|
||||
beforeTool({ toolCall, input }) { /* ... */ },
|
||||
afterTool({ toolCall, result }) { /* ... */ },
|
||||
afterRun({ result }) { /* ... */ },
|
||||
onEvent(event) { /* ... */ },
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
### The Seven Hooks
|
||||
|
||||
| Hook | Fires | Can Stop the Loop? | Common Uses |
|
||||
|------|-------|--------------------|-------------|
|
||||
| `beforeRun` | Before the runtime loop starts | Yes | Greet, log, attach session metadata |
|
||||
| `afterRun` | After the runtime loop finishes (success, abort, or fail) | No | Notifications, metrics, persistent logs |
|
||||
| `beforeModel` | Before each model request | Yes (mutate req) | Inject context, last-mile prompt edits |
|
||||
| `afterModel` | After each model response, before tool execution | Yes | Block based on model output |
|
||||
| `beforeTool` | Before each tool execution | Yes (`{ stop }`) | Audit, redact, block dangerous tools |
|
||||
| `afterTool` | After each tool execution | Can replace result | Post-process, redact secrets in tool output |
|
||||
| `onEvent` | On every `AgentRuntimeEvent` emitted by the runtime | No | Streaming UIs, telemetry pipes |
|
||||
|
||||
### Stopping the Loop from a Hook
|
||||
|
||||
Several hooks return an optional control object. The most common pattern is `beforeTool` blocking a destructive tool call:
|
||||
|
||||
```typescript
|
||||
beforeTool({ toolCall, input }) {
|
||||
if (toolCall.toolName === "run_commands") {
|
||||
const { commands } = input as { commands?: string[] }
|
||||
if (sessionBranch === "main" && commands?.some(c => c.startsWith("git push"))) {
|
||||
return { stop: true, reason: "Blocked git push on protected branch" }
|
||||
}
|
||||
}
|
||||
return undefined // explicit "continue"
|
||||
}
|
||||
```
|
||||
|
||||
Returning `undefined` (or omitting `return`) lets execution continue normally.
|
||||
|
||||
### afterRun Semantics
|
||||
|
||||
`afterRun` fires for every terminal status -- `completed`, `aborted`, `failed`. If you only want to act on success:
|
||||
|
||||
```typescript
|
||||
afterRun({ result }) {
|
||||
if (result.status !== "completed") return
|
||||
// notify, log success metrics, etc.
|
||||
}
|
||||
```
|
||||
|
||||
### Plugin Hooks vs File Hooks
|
||||
|
||||
The runtime supports two hook systems:
|
||||
|
||||
- File hooks -- external scripts in `.cline/hooks/` invoked with serialized JSON. Right for user/workspace-specific scripts that don't ship with code.
|
||||
- Plugin runtime hooks -- typed in-process callbacks. Right when the behavior belongs to a reusable extension and needs typed access to the runtime.
|
||||
|
||||
Core adapts file hooks onto the runtime hook layer, so you don't need both. If you're shipping a plugin, write it as runtime hooks.
|
||||
|
||||
## Message Builders
|
||||
|
||||
Message builders rewrite the provider-bound message list before the model call. They run after runtime messages are converted into SDK message blocks but before core's built-in safety builder.
|
||||
|
||||
Use them for:
|
||||
|
||||
- Custom compaction policies (replace middle history with a summary).
|
||||
- Redacting PII or secrets before they reach the provider.
|
||||
- Reshaping context for a specific model's strengths.
|
||||
|
||||
```typescript
|
||||
api.registerMessageBuilder({
|
||||
name: "summarize-middle-history",
|
||||
build(messages) {
|
||||
if (estimateTokens(messages) < THRESHOLD) return messages
|
||||
return [...prefix, summary, ...recent]
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
Multiple builders run in registration order; the output of one is the input of the next.
|
||||
|
||||
When to use `beforeModel` instead: reach for the `beforeModel` hook only if you need the runtime snapshot or want to mutate the request object itself. Pure message rewrites belong in a builder.
|
||||
|
||||
## Automation Events
|
||||
|
||||
Plugins can declare normalized event types and emit them into Cline automation. Hosts that don't have automation enabled simply ignore both -- feature-detect `ctx.automation`.
|
||||
|
||||
```typescript
|
||||
manifest: { capabilities: ["automationEvents"] },
|
||||
|
||||
setup(api, ctx) {
|
||||
api.registerAutomationEventType({
|
||||
eventType: "github.pull_request.opened",
|
||||
source: "github",
|
||||
description: "A new GitHub PR was opened",
|
||||
attributesSchema: { /* JSON Schema for envelope.attributes */ },
|
||||
})
|
||||
|
||||
if (!ctx.automation) return // host has no automation
|
||||
ctx.automation.ingestEvent({
|
||||
eventId: "pr-1234",
|
||||
eventType: "github.pull_request.opened",
|
||||
source: "github",
|
||||
subject: "owner/repo#1234",
|
||||
occurredAt: new Date().toISOString(),
|
||||
attributes: { /* ... */ },
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
## Loading a Plugin
|
||||
|
||||
There are three ways a plugin gets into a session:
|
||||
|
||||
### Auto-Discovery (CLI)
|
||||
|
||||
The CLI scans these directories on startup:
|
||||
|
||||
- `<workspace>/.cline/plugins/` -- project-scoped plugins.
|
||||
- `~/.cline/plugins/` -- user-scoped plugins.
|
||||
|
||||
Drop a `.ts` or `.js` file in, run `cline`, done:
|
||||
|
||||
```bash
|
||||
mkdir -p .cline/plugins
|
||||
cp my-plugin.ts .cline/plugins/
|
||||
cline -i "do the thing my plugin enables"
|
||||
```
|
||||
|
||||
### Explicit extensions in SDK Config
|
||||
|
||||
When you build your own host with `ClineCore`, pass the plugin object directly:
|
||||
|
||||
```typescript
|
||||
import plugin from "./my-plugin"
|
||||
import { ClineCore } from "@cline/core"
|
||||
|
||||
const host = await ClineCore.create({ backendMode: "local" })
|
||||
await host.start({
|
||||
config: {
|
||||
providerId: "anthropic",
|
||||
modelId: "claude-sonnet-4-6",
|
||||
apiKey: process.env.ANTHROPIC_API_KEY ?? "",
|
||||
cwd: process.cwd(),
|
||||
enableTools: true,
|
||||
systemPrompt: "You are a helpful assistant.",
|
||||
extensions: [plugin],
|
||||
extensionContext: {
|
||||
workspace: { rootPath: process.cwd(), cwd: process.cwd() },
|
||||
},
|
||||
},
|
||||
prompt: "...",
|
||||
interactive: false,
|
||||
})
|
||||
```
|
||||
|
||||
### pluginPaths for Directory-Based Plugins
|
||||
|
||||
When the plugin is a directory with `package.json`, point `pluginPaths` at the directory:
|
||||
|
||||
```typescript
|
||||
config: {
|
||||
pluginPaths: ["./path/to/my-plugin-package"],
|
||||
}
|
||||
```
|
||||
|
||||
Or install with the CLI:
|
||||
|
||||
```bash
|
||||
cline plugin install ./path/to/my-plugin-package
|
||||
cline plugin install @scope/my-cline-plugin # from npm
|
||||
cline plugin install --git github.com/owner/repo # from git
|
||||
```
|
||||
|
||||
## Single-File Plugin Template
|
||||
|
||||
Save as `my-plugin.ts`, drop in `.cline/plugins/`:
|
||||
|
||||
```typescript
|
||||
import { type AgentPlugin, ClineCore, createTool } from "@cline/core"
|
||||
|
||||
let sessionRoot: string | undefined
|
||||
|
||||
const plugin: AgentPlugin = {
|
||||
name: "my-plugin",
|
||||
manifest: {
|
||||
capabilities: ["tools", "hooks"],
|
||||
},
|
||||
|
||||
setup(api, ctx) {
|
||||
sessionRoot = ctx.workspaceInfo?.rootPath
|
||||
|
||||
api.registerTool(
|
||||
createTool({
|
||||
name: "do_thing",
|
||||
description: "Do the thing this plugin exists for.",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: { target: { type: "string" } },
|
||||
required: ["target"],
|
||||
},
|
||||
async execute(input) {
|
||||
const { target } = input as { target: string }
|
||||
return { ok: true, target, root: sessionRoot }
|
||||
},
|
||||
}),
|
||||
)
|
||||
},
|
||||
|
||||
hooks: {
|
||||
beforeRun() {
|
||||
console.log("[my-plugin] run started")
|
||||
},
|
||||
afterRun({ result }) {
|
||||
if (result.status !== "completed") return
|
||||
console.log(`[my-plugin] done in ${result.iterations} iteration(s)`)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
async function runDemo(): Promise<void> {
|
||||
const host = await ClineCore.create({ backendMode: "local" })
|
||||
try {
|
||||
const result = await host.start({
|
||||
config: {
|
||||
providerId: "anthropic",
|
||||
modelId: "claude-sonnet-4-6",
|
||||
apiKey: process.env.ANTHROPIC_API_KEY ?? "",
|
||||
cwd: process.cwd(),
|
||||
enableTools: true,
|
||||
systemPrompt: "You are a helpful assistant. Use tools when needed.",
|
||||
extensions: [plugin],
|
||||
extensionContext: {
|
||||
workspace: { rootPath: process.cwd(), cwd: process.cwd() },
|
||||
},
|
||||
},
|
||||
prompt: "Use do_thing on the target 'world'.",
|
||||
interactive: false,
|
||||
})
|
||||
console.log(result.result?.text ?? "")
|
||||
} finally {
|
||||
await host.dispose()
|
||||
}
|
||||
}
|
||||
|
||||
if (import.meta.main) {
|
||||
await runDemo()
|
||||
}
|
||||
|
||||
export { plugin, runDemo }
|
||||
export default plugin
|
||||
```
|
||||
|
||||
Copy it, rename the tool, swap in your logic. The `runDemo()` function lets you test with `ANTHROPIC_API_KEY=sk-... bun run my-plugin.ts`.
|
||||
|
||||
## Plugin Package
|
||||
|
||||
Use a plugin package when you need npm dependencies, multiple entry points, bundled assets, or npm/git distribution.
|
||||
|
||||
### Layout
|
||||
|
||||
```
|
||||
my-cline-plugin/
|
||||
+-- package.json
|
||||
+-- tsconfig.json (optional, for local typechecking)
|
||||
+-- index.ts (the plugin entry point)
|
||||
+-- README.md
|
||||
+-- assets/ (optional, bundled content)
|
||||
+-- templates/
|
||||
+-- schemas/
|
||||
```
|
||||
|
||||
### package.json -- The Discovery Contract
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "my-cline-plugin",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"description": "What this plugin does, in one sentence.",
|
||||
"type": "module",
|
||||
"exports": {
|
||||
".": "./index.ts"
|
||||
},
|
||||
"cline": {
|
||||
"plugins": [
|
||||
{
|
||||
"paths": ["./index.ts"],
|
||||
"capabilities": ["tools", "hooks"]
|
||||
}
|
||||
]
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@cline/core": "*"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@cline/core": { "optional": true }
|
||||
},
|
||||
"dependencies": {
|
||||
"zod": "^4.1.5"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Key fields:
|
||||
|
||||
- `type: "module"` -- required. Cline plugins are ES modules.
|
||||
- `cline.plugins` -- the discovery contract. Array of entries, each with `paths` (entry files) and `capabilities` (pre-declared, validated before importing).
|
||||
- `peerDependencies` for `@cline/core` -- the host already provides it. Marking it optional lets you typecheck in isolation.
|
||||
|
||||
### Bundling Assets
|
||||
|
||||
Resolve asset paths with `import.meta.url`, not `process.cwd()`:
|
||||
|
||||
```typescript
|
||||
import { dirname, join } from "node:path"
|
||||
import { fileURLToPath } from "node:url"
|
||||
import { readFileSync, existsSync } from "node:fs"
|
||||
|
||||
const MODULE_DIR = dirname(fileURLToPath(import.meta.url))
|
||||
const TEMPLATES_DIR = join(MODULE_DIR, "assets", "templates")
|
||||
|
||||
function loadTemplate(name: string): string | undefined {
|
||||
const path = join(TEMPLATES_DIR, `${name}.md`)
|
||||
return existsSync(path) ? readFileSync(path, "utf8") : undefined
|
||||
}
|
||||
```
|
||||
|
||||
This is the only place `import.meta.url` is appropriate in a plugin -- locating files inside the plugin package. For workspace paths, always use `ctx.workspaceInfo?.rootPath`.
|
||||
|
||||
### The Override Pattern (Bundled / Global / Project)
|
||||
|
||||
A package can ship default assets and let users override them. The convention is a three-tier lookup, last write wins by `name`:
|
||||
|
||||
1. bundled -- files inside the plugin package (defaults shipped with the plugin).
|
||||
2. global -- files under `~/.cline/data/settings/<kind>/` (user overrides).
|
||||
3. project -- files under `<workspace>/.cline/<kind>/` (project overrides).
|
||||
|
||||
### Multiple Plugin Entries
|
||||
|
||||
If your package exposes more than one plugin, list each in `cline.plugins`:
|
||||
|
||||
```json
|
||||
"cline": {
|
||||
"plugins": [
|
||||
{ "paths": ["./tools-plugin.ts"], "capabilities": ["tools"] },
|
||||
{ "paths": ["./hooks-plugin.ts"], "capabilities": ["hooks"] }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Each entry file should `export default` its own plugin object.
|
||||
|
||||
## Testing Your Plugin
|
||||
|
||||
### Unit Tests
|
||||
|
||||
The plugin object is plain data. Drive `setup()` against a minimal context and exercise tools directly:
|
||||
|
||||
```typescript
|
||||
import plugin from "../my-plugin"
|
||||
|
||||
const tools: unknown[] = []
|
||||
const api = {
|
||||
registerTool: (t: unknown) => tools.push(t),
|
||||
registerCommand: () => {},
|
||||
registerRule: () => {},
|
||||
registerMessageBuilder: () => {},
|
||||
registerProvider: () => {},
|
||||
registerAutomationEventType: () => {},
|
||||
}
|
||||
await plugin.setup?.(api as never, {
|
||||
workspaceInfo: { rootPath: "/tmp/fake-workspace" },
|
||||
})
|
||||
|
||||
// Now `tools` contains the registered tools -- call tool.execute(input, ctx)
|
||||
```
|
||||
|
||||
### End-to-End with runDemo()
|
||||
|
||||
Add a `runDemo()` in your plugin file (see the single-file template above) that boots a real `ClineCore` session:
|
||||
|
||||
```bash
|
||||
ANTHROPIC_API_KEY=sk-... bun run my-plugin.ts
|
||||
```
|
||||
|
||||
### CLI Smoke Test
|
||||
|
||||
```bash
|
||||
mkdir -p .cline/plugins
|
||||
cp my-plugin.ts .cline/plugins/
|
||||
cline -i "trigger something that exercises the plugin"
|
||||
```
|
||||
|
||||
For packages:
|
||||
|
||||
```bash
|
||||
cline plugin install ./my-cline-plugin
|
||||
cline -i "..."
|
||||
```
|
||||
|
||||
If the plugin fails validation or setup, the CLI prints a clear error and continues without it.
|
||||
|
||||
## Common Gotchas
|
||||
|
||||
- "capabilities must be a non-empty array" -- you forgot `manifest.capabilities`, or it's `[]`.
|
||||
- "registerRule requires the 'rules' capability" -- capability/handler drift. Add `"rules"` to capabilities, or stop calling `registerRule`.
|
||||
- Tool not visible to the model -- check `enableTools: true` on the session config, and that you're declaring `"tools"` in capabilities.
|
||||
- `ctx.workspaceInfo` is undefined in SDK tests -- the host didn't pass `extensionContext.workspace`. In SDK code, set it explicitly (see the ClineCore loading example above).
|
||||
- State leaking across sessions -- module-level variables are shared across sessions in the same process. Key by `ctx.session?.sessionId` if your host runs multiple sessions concurrently.
|
||||
- `afterRun` firing on aborts -- guard with `if (result.status !== "completed") return`.
|
||||
- Heavy work in `setup()` -- `setup()` blocks session start. Defer expensive work into the first tool call or `beforeRun`.
|
||||
- Importing host internals -- only import from `@cline/core`. Reaching into host-specific packages (e.g. CLI internals) will break in non-CLI hosts.
|
||||
- Sandboxed plugins and `telemetry` -- telemetry is process-local. Feature-detect `ctx.telemetry` and expect it to be undefined in sandboxed plugin processes.
|
||||
- Resolving bundled assets -- use `import.meta.url` + `fileURLToPath` to find files inside your package; never `process.cwd()`. For workspace paths, do the opposite: use `ctx.workspaceInfo?.rootPath`, never `import.meta.url`.
|
||||
- Plugin name collisions -- `name` must be unique within a session. If two plugins share a name, validation fails. Namespace by package (`my-org-redactor`, not `redactor`).
|
||||
|
||||
## Decision Guide -- Which Extension Point?
|
||||
|
||||
| You want to... | Use |
|
||||
|----------------|-----|
|
||||
| Give the model a new capability | `registerTool` |
|
||||
| Add a slash command in chat surfaces | `registerCommand` |
|
||||
| Inject text into the system prompt | `registerRule` |
|
||||
| Rewrite messages before they hit the provider | `registerMessageBuilder` |
|
||||
| Add a custom model provider | `registerProvider` |
|
||||
| Emit normalized cron/webhook events | `registerAutomationEventType` + `ctx.automation` |
|
||||
| Observe or steer the agent loop | `hooks.*` |
|
||||
| Block a dangerous tool call | `hooks.beforeTool` returning `{ stop: true }` |
|
||||
| Notify on completion | `hooks.afterRun` (gate on `status === "completed"`) |
|
||||
| Tweak each model request | `hooks.beforeModel` |
|
||||
| Stream events to a UI | `hooks.onEvent` |
|
||||
| Ship reusable templates with the plugin | Bundle assets next to `index.ts`, resolve via `import.meta.url` |
|
||||
| Let users override defaults globally or per-project | Three-tier lookup: bundled / global / project |
|
||||
|
||||
## Pre-Ship Checklist
|
||||
|
||||
- `manifest.capabilities` is a non-empty array.
|
||||
- Every `api.register*` call has a matching capability declared.
|
||||
- If `hooks` is present, `"hooks"` is in `capabilities`.
|
||||
- `ctx.workspaceInfo?.rootPath` is used for workspace paths (not `process.cwd()`).
|
||||
- Optional `ctx` fields are feature-detected.
|
||||
- Tool names are snake_case verbs; descriptions are written for the model.
|
||||
- Tool inputs have JSON Schema with `required` set.
|
||||
- `afterRun` handlers gate on `result.status === "completed"` if they only want successes.
|
||||
- State that must not leak between concurrent sessions is keyed by `ctx.session?.sessionId`.
|
||||
- (Package) `package.json` has `type: "module"`, `cline.plugins`, and `@cline/core` as an optional peer dep.
|
||||
- (Package) Bundled assets resolved via `import.meta.url`, not `process.cwd()`.
|
||||
- Smoke test: drop the plugin into `.cline/plugins/` (or `cline plugin install`), run `cline -i "..."`, watch it work.
|
||||
|
||||
## Plugin Examples from SDK
|
||||
|
||||
The SDK repo includes these example plugins:
|
||||
|
||||
| Plugin | Description |
|
||||
|--------|-------------|
|
||||
| `weather-metrics.ts` | Tool registration + lifecycle metrics |
|
||||
| `mac-notify.ts` | macOS Notification Center alerts |
|
||||
| `custom-compaction.ts` | Custom message compaction via message builders |
|
||||
| `background-terminal.ts` | Detached shell job management |
|
||||
| `automation-events.ts` | Plugin-emitted automation events |
|
||||
| `gitignore-read-files-guard.ts` | File access policy enforcement via beforeTool |
|
||||
| `web-search.ts` | Web search via Exa API |
|
||||
| `typescript-lsp/` | TypeScript Language Service tools (plugin package) |
|
||||
| `agents-squad/` | Multi-agent team orchestration (plugin package) |
|
||||
|
||||
## See Also
|
||||
|
||||
- `../tools/REFERENCE.md` - Tool creation
|
||||
- `../events/REFERENCE.md` - Event system
|
||||
- `../agent/REFERENCE.md` - Using plugins with Agent
|
||||
- `../clinecore/REFERENCE.md` - Using plugins with ClineCore
|
||||
@@ -0,0 +1,253 @@
|
||||
# Going to Production
|
||||
|
||||
Guidelines for deploying Cline SDK agents in production environments.
|
||||
|
||||
## Error Handling
|
||||
|
||||
Always check the result status:
|
||||
|
||||
```typescript
|
||||
const result = await agent.run(input)
|
||||
|
||||
switch (result.status) {
|
||||
case "completed":
|
||||
console.log("Success:", result.outputText)
|
||||
break
|
||||
case "aborted":
|
||||
console.log("Cancelled:", result.error?.message)
|
||||
break
|
||||
case "failed":
|
||||
console.error("Failed:", result.error)
|
||||
break
|
||||
}
|
||||
```
|
||||
|
||||
For ClineCore, check `finishReason`:
|
||||
|
||||
```typescript
|
||||
const session = await cline.start({ ... })
|
||||
|
||||
switch (session.result?.finishReason) {
|
||||
case "completed":
|
||||
// normal completion
|
||||
break
|
||||
case "max_iterations":
|
||||
// agent hit iteration limit
|
||||
break
|
||||
case "aborted":
|
||||
// manually cancelled
|
||||
break
|
||||
case "mistake_limit":
|
||||
// too many tool errors
|
||||
break
|
||||
case "error":
|
||||
// unrecoverable error
|
||||
break
|
||||
}
|
||||
```
|
||||
|
||||
## Cost Control
|
||||
|
||||
### Token Limits
|
||||
|
||||
Set maximum tokens per turn and iteration limits:
|
||||
|
||||
```typescript
|
||||
const agent = new Agent({
|
||||
providerId: "anthropic",
|
||||
modelId: "claude-sonnet-4-6",
|
||||
maxTokensPerTurn: 4096,
|
||||
maxIterations: 10,
|
||||
tools: [...],
|
||||
})
|
||||
```
|
||||
|
||||
### Model Selection
|
||||
|
||||
Use cheaper models for simple tasks:
|
||||
|
||||
```typescript
|
||||
// Simple classification or formatting
|
||||
{ providerId: "anthropic", modelId: "claude-haiku-4-5" }
|
||||
|
||||
// Complex reasoning and code generation
|
||||
{ providerId: "anthropic", modelId: "claude-sonnet-4-6" }
|
||||
|
||||
// Hardest tasks requiring deep reasoning
|
||||
{ providerId: "anthropic", modelId: "claude-opus-4-7" }
|
||||
```
|
||||
|
||||
### Usage Tracking
|
||||
|
||||
Monitor spending in real time:
|
||||
|
||||
```typescript
|
||||
agent.subscribe((event) => {
|
||||
if (event.type === "usage-updated" && event.usage.totalCost) {
|
||||
if (event.usage.totalCost > MAX_BUDGET) {
|
||||
agent.abort("Budget exceeded")
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
## Observability
|
||||
|
||||
### OpenTelemetry Integration
|
||||
|
||||
The SDK supports OpenTelemetry for traces, metrics, and logs:
|
||||
|
||||
```typescript
|
||||
import { ClineCore } from "@cline/sdk"
|
||||
|
||||
const cline = await ClineCore.create({
|
||||
clientName: "my-app",
|
||||
// OpenTelemetry config is picked up from environment
|
||||
// OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_SERVICE_NAME, etc.
|
||||
})
|
||||
```
|
||||
|
||||
### Structured Logging
|
||||
|
||||
Use the `BasicLogger` interface for injectable logging:
|
||||
|
||||
```typescript
|
||||
import type { BasicLogger } from "@cline/sdk"
|
||||
|
||||
const logger: BasicLogger = {
|
||||
debug: (msg, meta) => console.debug(msg, meta),
|
||||
log: (msg, meta) => console.log(msg, meta),
|
||||
error: (msg, meta) => console.error(msg, meta),
|
||||
}
|
||||
|
||||
await cline.start({
|
||||
config: {
|
||||
logger,
|
||||
// ...
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
### Custom Metrics via Plugins
|
||||
|
||||
```typescript
|
||||
const metricsPlugin: AgentPlugin = {
|
||||
name: "metrics",
|
||||
manifest: { capabilities: ["hooks"] },
|
||||
setup() {},
|
||||
hooks: {
|
||||
beforeRun() {
|
||||
metrics.increment("agent.runs.started")
|
||||
},
|
||||
afterRun({ result }) {
|
||||
metrics.increment("agent.runs.completed")
|
||||
metrics.histogram("agent.iterations", result.iterations)
|
||||
metrics.histogram("agent.tokens.output", result.usage.outputTokens)
|
||||
},
|
||||
beforeTool({ toolCall }) {
|
||||
metrics.increment(`agent.tools.${toolCall.toolName}`)
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
## Security
|
||||
|
||||
### Sandbox Tool Execution
|
||||
|
||||
Validate tool inputs to prevent path traversal and injection:
|
||||
|
||||
```typescript
|
||||
execute: async (input) => {
|
||||
const safePath = path.resolve(WORKSPACE_ROOT, input.path)
|
||||
if (!safePath.startsWith(WORKSPACE_ROOT)) {
|
||||
return { error: "Path traversal attempt blocked" }
|
||||
}
|
||||
return await readFile(safePath, "utf-8")
|
||||
}
|
||||
```
|
||||
|
||||
### API Key Management
|
||||
|
||||
- Use environment variables, never hardcode keys
|
||||
- Rotate keys regularly
|
||||
- Use different keys for development and production
|
||||
|
||||
```typescript
|
||||
{
|
||||
providerId: "anthropic",
|
||||
modelId: "claude-sonnet-4-6",
|
||||
apiKey: process.env.ANTHROPIC_API_KEY, // never a literal string
|
||||
}
|
||||
```
|
||||
|
||||
### Tool Policy Hardening
|
||||
|
||||
Disable tools you don't need and require approval for dangerous ones:
|
||||
|
||||
```typescript
|
||||
toolPolicies: {
|
||||
read_files: { autoApprove: true },
|
||||
search: { autoApprove: true },
|
||||
bash: { autoApprove: false }, // require approval
|
||||
editor: { autoApprove: false },
|
||||
apply_patch: { autoApprove: false },
|
||||
fetch_web: { enabled: false }, // disable entirely
|
||||
}
|
||||
```
|
||||
|
||||
## Deployment Patterns
|
||||
|
||||
### Stateless Worker
|
||||
|
||||
For request/response workloads (API endpoints, queue consumers):
|
||||
|
||||
```typescript
|
||||
const cline = await ClineCore.create({
|
||||
clientName: "worker",
|
||||
backendMode: "local",
|
||||
})
|
||||
|
||||
app.post("/agent", async (req, res) => {
|
||||
const session = await cline.start({
|
||||
prompt: req.body.prompt,
|
||||
config: { ... },
|
||||
})
|
||||
res.json({ text: session.result?.text, usage: session.result?.usage })
|
||||
})
|
||||
```
|
||||
|
||||
### Persistent Service
|
||||
|
||||
For long-running services with session management:
|
||||
|
||||
```typescript
|
||||
const cline = await ClineCore.create({
|
||||
clientName: "service",
|
||||
backendMode: "hub",
|
||||
})
|
||||
|
||||
process.on("SIGTERM", async () => {
|
||||
await cline.dispose("SIGTERM")
|
||||
process.exit(0)
|
||||
})
|
||||
```
|
||||
|
||||
### Scheduled Automation
|
||||
|
||||
See `../scheduling/REFERENCE.md` for recurring agent tasks.
|
||||
|
||||
## Retry and Resilience
|
||||
|
||||
- Tool `execute` functions support `retryable: true` (default) and `maxRetries: 3` (default)
|
||||
- Provider API calls are retried automatically on transient failures
|
||||
- Use `timeoutMs` on tools to prevent hanging
|
||||
- Monitor `mistake_limit` finish reason to detect systematic tool failures
|
||||
|
||||
## See Also
|
||||
|
||||
- `../agent/REFERENCE.md` - Agent overview
|
||||
- `../clinecore/REFERENCE.md` - ClineCore overview
|
||||
- `../tools/REFERENCE.md` - Tool configuration
|
||||
- `../plugins/REFERENCE.md` - Metrics plugins
|
||||
- `../scheduling/REFERENCE.md` - Scheduled agents
|
||||
@@ -0,0 +1,257 @@
|
||||
# Model Providers
|
||||
|
||||
The Cline SDK supports every major LLM provider out of the box via `@cline/llms`.
|
||||
|
||||
## Supported Providers
|
||||
|
||||
| Provider ID | Models |
|
||||
|-------------|--------|
|
||||
| `"anthropic"` | Claude Opus 4.7, Sonnet 4.6, Haiku 4.5 |
|
||||
| `"openai"` | GPT-5.5, GPT-5.3 Codex |
|
||||
| `"gemini"` | Gemini 3.1 Pro Preview, Gemini 3 Flash Preview |
|
||||
| `"vertex"` | Google models via Vertex AI |
|
||||
| `"bedrock"` | Claude, Llama via AWS Bedrock |
|
||||
| `"mistral"` | Mistral Large, Codestral |
|
||||
| `"openai-compatible"` | vLLM, Together, Fireworks, Groq, etc. |
|
||||
|
||||
## Basic Configuration
|
||||
|
||||
### With Agent
|
||||
|
||||
```typescript
|
||||
import { Agent } from "@cline/sdk"
|
||||
|
||||
const agent = new Agent({
|
||||
providerId: "anthropic",
|
||||
modelId: "claude-sonnet-4-6",
|
||||
apiKey: process.env.ANTHROPIC_API_KEY,
|
||||
systemPrompt: "You are a helpful assistant.",
|
||||
tools: [],
|
||||
})
|
||||
```
|
||||
|
||||
### With ClineCore
|
||||
|
||||
```typescript
|
||||
import { ClineCore } from "@cline/sdk"
|
||||
|
||||
const cline = await ClineCore.create({ clientName: "my-app" })
|
||||
|
||||
await cline.start({
|
||||
prompt: "Hello",
|
||||
config: {
|
||||
providerId: "anthropic",
|
||||
modelId: "claude-sonnet-4-6",
|
||||
apiKey: process.env.ANTHROPIC_API_KEY,
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
## Provider-Specific Configuration
|
||||
|
||||
### Anthropic
|
||||
|
||||
```typescript
|
||||
{
|
||||
providerId: "anthropic",
|
||||
modelId: "claude-opus-4-7", // or "claude-sonnet-4-6", "claude-haiku-4-5"
|
||||
apiKey: process.env.ANTHROPIC_API_KEY,
|
||||
}
|
||||
```
|
||||
|
||||
### OpenAI
|
||||
|
||||
```typescript
|
||||
{
|
||||
providerId: "openai",
|
||||
modelId: "gpt-5.5",
|
||||
apiKey: process.env.OPENAI_API_KEY,
|
||||
}
|
||||
```
|
||||
|
||||
### Google (Gemini)
|
||||
|
||||
```typescript
|
||||
{
|
||||
providerId: "gemini",
|
||||
modelId: "gemini-3.1-pro-preview",
|
||||
apiKey: process.env.GOOGLE_API_KEY,
|
||||
}
|
||||
```
|
||||
|
||||
### Google (Vertex AI)
|
||||
|
||||
```typescript
|
||||
{
|
||||
providerId: "vertex",
|
||||
modelId: "gemini-3.1-pro-preview",
|
||||
// Uses application default credentials or service account
|
||||
}
|
||||
```
|
||||
|
||||
### AWS Bedrock
|
||||
|
||||
```typescript
|
||||
{
|
||||
providerId: "bedrock",
|
||||
modelId: "anthropic.claude-sonnet-4-6",
|
||||
// Uses AWS credential chain (env vars, config file, IAM role)
|
||||
// Set AWS_REGION, AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY
|
||||
}
|
||||
```
|
||||
|
||||
### Mistral
|
||||
|
||||
```typescript
|
||||
{
|
||||
providerId: "mistral",
|
||||
modelId: "mistral-large-latest",
|
||||
apiKey: process.env.MISTRAL_API_KEY,
|
||||
}
|
||||
```
|
||||
|
||||
### OpenAI-Compatible
|
||||
|
||||
For any provider with an OpenAI-compatible API:
|
||||
|
||||
```typescript
|
||||
{
|
||||
providerId: "openai-compatible",
|
||||
modelId: "my-model",
|
||||
apiKey: process.env.API_KEY,
|
||||
baseUrl: "https://api.together.xyz/v1",
|
||||
}
|
||||
```
|
||||
|
||||
Works with: vLLM, Together AI, Fireworks, Groq, Ollama, LiteLLM, etc.
|
||||
|
||||
## Custom Base URL
|
||||
|
||||
Override the API endpoint for any provider:
|
||||
|
||||
```typescript
|
||||
{
|
||||
providerId: "anthropic",
|
||||
modelId: "claude-sonnet-4-6",
|
||||
apiKey: process.env.API_KEY,
|
||||
baseUrl: "https://my-proxy.example.com/v1",
|
||||
}
|
||||
```
|
||||
|
||||
## Custom Headers
|
||||
|
||||
Pass additional headers to API requests:
|
||||
|
||||
```typescript
|
||||
{
|
||||
providerId: "openai",
|
||||
modelId: "gpt-5.5",
|
||||
apiKey: process.env.API_KEY,
|
||||
headers: {
|
||||
"X-Custom-Header": "value",
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
## Gateway API
|
||||
|
||||
For advanced multi-provider setups, use the Gateway directly:
|
||||
|
||||
```typescript
|
||||
import { createGateway, DefaultGateway } from "@cline/llms"
|
||||
|
||||
const gateway = createGateway({
|
||||
providerConfigs: [
|
||||
{ providerId: "anthropic", apiKey: process.env.ANTHROPIC_API_KEY },
|
||||
{ providerId: "openai", apiKey: process.env.OPENAI_API_KEY },
|
||||
],
|
||||
})
|
||||
|
||||
// Create a model for a specific provider
|
||||
const model = gateway.createAgentModel({
|
||||
providerId: "anthropic",
|
||||
modelId: "claude-opus-4-7",
|
||||
})
|
||||
|
||||
// Use with Agent
|
||||
const agent = new Agent({ model, systemPrompt: "...", tools: [] })
|
||||
```
|
||||
|
||||
### Gateway Methods
|
||||
|
||||
```typescript
|
||||
gateway.registerProvider(registration) // add a custom provider
|
||||
gateway.configureProvider(config) // update provider settings
|
||||
gateway.listProviders() // list available providers
|
||||
gateway.listModels(providerId?) // list available models
|
||||
gateway.createAgentModel(selection) // create model for agent
|
||||
gateway.stream(request) // raw streaming (AsyncIterable)
|
||||
```
|
||||
|
||||
## Provider Registry
|
||||
|
||||
Query and register providers programmatically:
|
||||
|
||||
```typescript
|
||||
import {
|
||||
getAllProviders,
|
||||
getProviderIds,
|
||||
getProvider,
|
||||
getModelsForProvider,
|
||||
registerProvider,
|
||||
registerModel,
|
||||
createHandler,
|
||||
} from "@cline/llms"
|
||||
|
||||
// List all registered providers
|
||||
const providers = getAllProviders()
|
||||
|
||||
// Get models for a provider
|
||||
const models = getModelsForProvider("anthropic")
|
||||
|
||||
// Register a custom provider
|
||||
registerProvider({
|
||||
id: "my-provider",
|
||||
name: "My Custom Provider",
|
||||
handler: createHandler({ ... }),
|
||||
})
|
||||
```
|
||||
|
||||
## Model Metadata
|
||||
|
||||
Access model info (context window, pricing, capabilities):
|
||||
|
||||
```typescript
|
||||
import { getModelsForProvider } from "@cline/llms"
|
||||
|
||||
const models = getModelsForProvider("anthropic")
|
||||
for (const model of models) {
|
||||
console.log(`${model.id}: context=${model.contextWindow}, input=$${model.inputPrice}/MTok`)
|
||||
}
|
||||
```
|
||||
|
||||
## Cost Tracking
|
||||
|
||||
Track per-request and cumulative costs:
|
||||
|
||||
```typescript
|
||||
// Via events
|
||||
agent.subscribe((event) => {
|
||||
if (event.type === "usage-updated") {
|
||||
console.log(`Cost: $${event.usage.totalCost?.toFixed(4)}`)
|
||||
}
|
||||
})
|
||||
|
||||
// Via result
|
||||
const result = await agent.run("...")
|
||||
console.log(`Total cost: $${result.usage.totalCost?.toFixed(4)}`)
|
||||
|
||||
// Via ClineCore accumulated usage
|
||||
const usage = await cline.getAccumulatedUsage(sessionId)
|
||||
```
|
||||
|
||||
## See Also
|
||||
|
||||
- `../agent/REFERENCE.md` - Using providers with Agent
|
||||
- `../clinecore/REFERENCE.md` - Using providers with ClineCore
|
||||
- `../production/REFERENCE.md` - Cost control in production
|
||||
@@ -0,0 +1,227 @@
|
||||
# Scheduling and Automation
|
||||
|
||||
The Cline SDK supports scheduled, one-off, and event-driven agent execution through the automation subsystem in `@cline/core`.
|
||||
|
||||
## Overview
|
||||
|
||||
Three trigger types:
|
||||
|
||||
| Trigger | Description |
|
||||
|---------|-------------|
|
||||
| `schedule` | Recurring jobs via cron expressions |
|
||||
| `one_off` | Single execution tasks |
|
||||
| `event` | Triggered by external events (GitHub, Linear, custom) |
|
||||
|
||||
## CLI Schedule Management
|
||||
|
||||
```bash
|
||||
# Create a recurring schedule
|
||||
cline schedule create "Daily standup" \
|
||||
--cron "0 9 * * MON-FRI" \
|
||||
--prompt "Summarize open PRs and blockers" \
|
||||
--workspace /path/to/project \
|
||||
--model anthropic/claude-sonnet-4-6
|
||||
|
||||
# List schedules
|
||||
cline schedule list
|
||||
|
||||
# Trigger a schedule immediately
|
||||
cline schedule trigger <schedule-id>
|
||||
|
||||
# Pause/resume
|
||||
cline schedule pause <schedule-id>
|
||||
cline schedule resume <schedule-id>
|
||||
|
||||
# Delete
|
||||
cline schedule delete <schedule-id>
|
||||
|
||||
# View past executions
|
||||
cline schedule executions <schedule-id>
|
||||
```
|
||||
|
||||
## Cron Expressions
|
||||
|
||||
| Expression | Meaning |
|
||||
|-----------|---------|
|
||||
| `0 9 * * MON-FRI` | 9 AM weekdays |
|
||||
| `0 */6 * * *` | Every 6 hours |
|
||||
| `0 8 * * MON` | Mondays at 8 AM |
|
||||
| `*/30 * * * *` | Every 30 minutes |
|
||||
| `0 0 1 * *` | First of every month |
|
||||
|
||||
## File-Based Specs
|
||||
|
||||
Create Markdown files in `~/.cline/cron/` (global) or `.cline/cron/` (workspace):
|
||||
|
||||
### Recurring Schedule
|
||||
|
||||
```markdown
|
||||
---
|
||||
trigger: schedule
|
||||
schedule: "0 9 * * MON-FRI"
|
||||
timezone: America/New_York
|
||||
mode: exclusive
|
||||
prompt: "Check for dependency updates and create PRs for any outdated packages."
|
||||
modelSelection:
|
||||
providerId: anthropic
|
||||
modelId: claude-sonnet-4-6
|
||||
tools:
|
||||
enabled: true
|
||||
---
|
||||
|
||||
Additional context or instructions for the agent go in the body.
|
||||
```
|
||||
|
||||
### One-Off Task
|
||||
|
||||
```markdown
|
||||
---
|
||||
trigger: one_off
|
||||
prompt: "Generate a comprehensive test coverage report."
|
||||
modelSelection:
|
||||
providerId: anthropic
|
||||
modelId: claude-sonnet-4-6
|
||||
---
|
||||
```
|
||||
|
||||
### Event-Driven
|
||||
|
||||
```markdown
|
||||
---
|
||||
trigger: event
|
||||
eventType: github.pull_request.opened
|
||||
filters:
|
||||
repository: myorg/myrepo
|
||||
debounceMs: 5000
|
||||
cooldownMs: 60000
|
||||
prompt: "Review the PR for security issues and code quality."
|
||||
modelSelection:
|
||||
providerId: anthropic
|
||||
modelId: claude-sonnet-4-6
|
||||
---
|
||||
```
|
||||
|
||||
## CronSpec Types
|
||||
|
||||
```typescript
|
||||
interface CronScheduleSpec {
|
||||
trigger: "schedule"
|
||||
schedule: string // cron expression
|
||||
timezone?: string
|
||||
mode?: "exclusive" | "concurrent"
|
||||
prompt: string
|
||||
modelSelection?: { providerId: string; modelId?: string }
|
||||
extensionLoading?: "isolated" | "direct"
|
||||
configExtensions?: RuntimeConfigExtensionKind[]
|
||||
tools?: { enabled?: boolean; names?: string[] }
|
||||
}
|
||||
|
||||
interface CronOneOffSpec {
|
||||
trigger: "one_off"
|
||||
prompt: string
|
||||
modelSelection?: { providerId: string; modelId?: string }
|
||||
}
|
||||
|
||||
interface CronEventSpec {
|
||||
trigger: "event"
|
||||
eventType: string // e.g., "github.pull_request.opened"
|
||||
filters?: Record<string, unknown>
|
||||
debounceMs?: number
|
||||
cooldownMs?: number
|
||||
prompt: string
|
||||
modelSelection?: { providerId: string; modelId?: string }
|
||||
}
|
||||
```
|
||||
|
||||
## Programmatic Automation API
|
||||
|
||||
```typescript
|
||||
const cline = await ClineCore.create({
|
||||
clientName: "my-app",
|
||||
automation: true,
|
||||
})
|
||||
|
||||
// Start automation service
|
||||
cline.automation.start()
|
||||
|
||||
// Ingest an external event
|
||||
cline.automation.ingestEvent({
|
||||
eventId: "evt-123",
|
||||
eventType: "github.pull_request.opened",
|
||||
source: "github",
|
||||
timestamp: Date.now(),
|
||||
payload: { pr: { number: 42, title: "..." } },
|
||||
})
|
||||
|
||||
// List specs, runs, events
|
||||
const specs = await cline.automation.listSpecs()
|
||||
const runs = await cline.automation.listRuns()
|
||||
const events = await cline.automation.listEvents()
|
||||
|
||||
// Reconcile specs from directory
|
||||
await cline.automation.reconcile(specDirectory)
|
||||
|
||||
// Stop automation
|
||||
cline.automation.stop()
|
||||
```
|
||||
|
||||
## Event Ingestion from Plugins
|
||||
|
||||
Plugins can declare and emit automation events:
|
||||
|
||||
```typescript
|
||||
const webhookPlugin: AgentPlugin = {
|
||||
name: "webhook-events",
|
||||
manifest: { capabilities: ["automationEvents"] },
|
||||
setup(api) {
|
||||
api.registerAutomationEventType({
|
||||
type: "webhook.received",
|
||||
description: "External webhook received",
|
||||
})
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
Submit events via the plugin context:
|
||||
|
||||
```typescript
|
||||
ctx.automation.ingestEvent({
|
||||
eventId: "evt-456",
|
||||
eventType: "webhook.received",
|
||||
source: "custom",
|
||||
timestamp: Date.now(),
|
||||
payload: { ... },
|
||||
})
|
||||
```
|
||||
|
||||
## Concurrency Control
|
||||
|
||||
| Mode | Behavior |
|
||||
|------|----------|
|
||||
| `"exclusive"` | Skip if previous run still active |
|
||||
| `"concurrent"` | Allow overlapping runs |
|
||||
|
||||
## Run Reports
|
||||
|
||||
Each completed run writes a Markdown report to `.cline/cron/reports/<run-id>.md` with:
|
||||
- Run metadata (spec, trigger, timing)
|
||||
- Summary of agent output
|
||||
- Usage (tokens, cost)
|
||||
- Tool calls made
|
||||
- Trigger event context (for event-driven runs)
|
||||
|
||||
## Use Cases
|
||||
|
||||
- Daily standup summaries
|
||||
- Automated dependency update checks
|
||||
- PR review on open
|
||||
- Codebase health reports
|
||||
- Scheduled security scans
|
||||
- Event-driven CI/CD workflows
|
||||
|
||||
## See Also
|
||||
|
||||
- `../clinecore/REFERENCE.md` - ClineCore runtime
|
||||
- `../clinecore/api.md` - Automation API details
|
||||
- `../plugins/REFERENCE.md` - Plugin events
|
||||
- `../production/REFERENCE.md` - Production deployment
|
||||
@@ -0,0 +1,259 @@
|
||||
# Tools
|
||||
|
||||
Tools are how agents interact with the world. The Cline SDK supports both built-in tools (via ClineCore) and custom tools you define yourself.
|
||||
|
||||
## Creating Custom Tools
|
||||
|
||||
Use `createTool()` from `@cline/sdk` (or `@cline/shared`):
|
||||
|
||||
```typescript
|
||||
import { createTool } from "@cline/sdk"
|
||||
|
||||
const myTool = createTool({
|
||||
name: "search_issues",
|
||||
description: "Search GitHub issues by query. Returns up to 10 results.",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
query: { type: "string", description: "Search query" },
|
||||
state: { type: "string", enum: ["open", "closed", "all"] },
|
||||
},
|
||||
required: ["query"],
|
||||
},
|
||||
execute: async (input) => {
|
||||
const issues = await github.searchIssues(input.query, input.state)
|
||||
return { issues, count: issues.length }
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
### With Zod Schema
|
||||
|
||||
```typescript
|
||||
import { createTool } from "@cline/sdk"
|
||||
import { z } from "zod"
|
||||
|
||||
const deployTool = createTool({
|
||||
name: "deploy",
|
||||
description: "Deploy the app to the specified environment.",
|
||||
inputSchema: z.object({
|
||||
environment: z.enum(["staging", "production"]).describe("Target environment"),
|
||||
version: z.string().optional().describe("Version tag, defaults to latest"),
|
||||
}),
|
||||
execute: async (input) => {
|
||||
const result = await deploy(input.environment, input.version)
|
||||
return { url: result.url, status: "deployed" }
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
### Tool Config Options
|
||||
|
||||
```typescript
|
||||
createTool({
|
||||
name: string, // snake_case, unique per agent
|
||||
description: string, // what the tool does (model reads this)
|
||||
inputSchema: JSONSchema | ZodSchema, // input validation
|
||||
execute: async (input, context, onChange?) => output,
|
||||
timeoutMs?: number, // default: 30000
|
||||
retryable?: boolean, // default: true
|
||||
maxRetries?: number, // default: 3
|
||||
lifecycle?: {
|
||||
completesRun?: boolean // true = ends agent loop on success
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
### AgentToolContext
|
||||
|
||||
The second argument to `execute` provides runtime context:
|
||||
|
||||
```typescript
|
||||
interface AgentToolContext {
|
||||
agentId: string
|
||||
conversationId: string
|
||||
iteration: number
|
||||
abortSignal?: AbortSignal
|
||||
metadata?: Record<string, unknown>
|
||||
}
|
||||
```
|
||||
|
||||
## Tool Naming Rules
|
||||
|
||||
- Names must be `snake_case` (e.g., `search_issues`, `deploy_app`)
|
||||
- Names must be unique within a single agent's tool set
|
||||
- Choose descriptive names since the model uses them to decide which tool to call
|
||||
|
||||
## Tool Descriptions Matter
|
||||
|
||||
The model reads the tool description to decide when and how to use it. Write clear, specific descriptions:
|
||||
|
||||
```typescript
|
||||
// Bad: vague
|
||||
description: "Does deployment stuff"
|
||||
|
||||
// Good: specific with constraints
|
||||
description: "Deploy the application to staging or production. " +
|
||||
"Staging deployments are immediate. Production requires a passing CI build. " +
|
||||
"Returns the deployment URL and status."
|
||||
```
|
||||
|
||||
Include constraints, rate limits, and expected behavior in the description.
|
||||
|
||||
## Error Handling in Tools
|
||||
|
||||
Return errors as structured data instead of throwing:
|
||||
|
||||
```typescript
|
||||
// Good: return error data
|
||||
execute: async (input) => {
|
||||
const file = await readFile(input.path).catch(() => null)
|
||||
if (!file) {
|
||||
return { error: "File not found", path: input.path }
|
||||
}
|
||||
return { content: file }
|
||||
}
|
||||
```
|
||||
|
||||
Thrown exceptions count as "mistakes" against the agent's mistake limit. Returned error data lets the agent adjust its approach.
|
||||
|
||||
## Completion Tools
|
||||
|
||||
Tools with `lifecycle: { completesRun: true }` end the agent loop when they execute successfully:
|
||||
|
||||
```typescript
|
||||
const submitAnswer = createTool({
|
||||
name: "submit_answer",
|
||||
description: "Submit the final answer and end the task.",
|
||||
inputSchema: z.object({
|
||||
answer: z.string(),
|
||||
confidence: z.number().min(0).max(1),
|
||||
}),
|
||||
lifecycle: { completesRun: true },
|
||||
execute: async (input) => input,
|
||||
})
|
||||
```
|
||||
|
||||
The model sees the tool result and the run ends. Access the output via `result.toolCalls`.
|
||||
|
||||
## Built-in Tools (ClineCore Only)
|
||||
|
||||
When using `ClineCore` with `enableTools: true`, these tools are available automatically:
|
||||
|
||||
| Tool | Name | What It Does |
|
||||
|------|------|-------------|
|
||||
| Shell | `bash` | Execute shell commands in the session workspace |
|
||||
| Editor | `editor` | Create and edit files |
|
||||
| Read | `read_files` | Read file contents |
|
||||
| Patch | `apply_patch` | Apply unified diffs to files |
|
||||
| Search | `search` | Search file contents and directory structure |
|
||||
| Web | `fetch_web` | Fetch web content via HTTP |
|
||||
|
||||
Built-in tools respect the `cwd` setting in `CoreSessionConfig`.
|
||||
|
||||
## Tool Policies
|
||||
|
||||
Control which tools are available and whether they require approval:
|
||||
|
||||
```typescript
|
||||
// In Agent config
|
||||
const agent = new Agent({
|
||||
tools: [toolA, toolB, toolC],
|
||||
toolPolicies: {
|
||||
tool_a: { autoApprove: true }, // runs without asking
|
||||
tool_b: { autoApprove: false }, // requires approval
|
||||
tool_c: { enabled: false }, // hidden from model
|
||||
},
|
||||
})
|
||||
|
||||
// In ClineCore session
|
||||
await cline.start({
|
||||
prompt: "...",
|
||||
config: { ... },
|
||||
toolPolicies: {
|
||||
bash: { autoApprove: true },
|
||||
editor: { autoApprove: false },
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
### Policy Options
|
||||
|
||||
| Policy | Effect |
|
||||
|--------|--------|
|
||||
| `{ autoApprove: true }` | Tool runs without approval |
|
||||
| `{ autoApprove: false }` | Triggers approval callback before running |
|
||||
| `{ enabled: false }` | Tool is hidden from the model entirely |
|
||||
| No policy set | Defaults to enabled and auto-approved |
|
||||
|
||||
## Abort Signal in Long-Running Tools
|
||||
|
||||
Respect the abort signal for tools that take a long time:
|
||||
|
||||
```typescript
|
||||
execute: async (input, context) => {
|
||||
const results = []
|
||||
for (const item of input.items) {
|
||||
if (context.abortSignal?.aborted) {
|
||||
return { results, aborted: true, processed: results.length }
|
||||
}
|
||||
results.push(await processItem(item))
|
||||
}
|
||||
return { results, processed: results.length }
|
||||
}
|
||||
```
|
||||
|
||||
## Streaming Tool Output
|
||||
|
||||
Use the `onChange` callback (third argument) to stream partial results:
|
||||
|
||||
```typescript
|
||||
execute: async (input, context, onChange) => {
|
||||
let progress = 0
|
||||
for (const step of steps) {
|
||||
progress++
|
||||
onChange?.(`Processing step ${progress}/${steps.length}...`)
|
||||
await processStep(step)
|
||||
}
|
||||
return { completed: true }
|
||||
}
|
||||
```
|
||||
|
||||
## Testing Tools
|
||||
|
||||
Tools are plain async functions, so they're straightforward to test:
|
||||
|
||||
```typescript
|
||||
import { describe, it, expect } from "vitest"
|
||||
|
||||
describe("deploy tool", () => {
|
||||
it("deploys to staging", async () => {
|
||||
const context = { agentId: "test", conversationId: "test", iteration: 1 }
|
||||
const result = await deployTool.execute({ environment: "staging" }, context)
|
||||
expect(result.status).toBe("deployed")
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
## MCP Tool Integration
|
||||
|
||||
ClineCore can connect to MCP (Model Context Protocol) servers for additional tools. Configure in `.cline/mcp-servers.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"servers": {
|
||||
"my-server": {
|
||||
"command": "node",
|
||||
"args": ["./mcp-server.js"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
MCP tools appear alongside built-in and custom tools automatically.
|
||||
|
||||
## See Also
|
||||
|
||||
- `../agent/REFERENCE.md` - Using tools with Agent
|
||||
- `../clinecore/REFERENCE.md` - Using tools with ClineCore
|
||||
- `../plugins/REFERENCE.md` - Packaging tools as plugins
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"claude-dev": patch
|
||||
---
|
||||
|
||||
fix: use correct base URL for Vertex AI global endpoint with Claude models
|
||||
@@ -0,0 +1 @@
|
||||
../../.agents/skills/cline-sdk
|
||||
@@ -1,33 +0,0 @@
|
||||
# CLI Development
|
||||
|
||||
The CLI lives in `cli/` and uses React Ink for terminal UI.
|
||||
|
||||
- If needed, look at `cli/src/constants/colors.ts` for re-used terminal colors, e.g. `COLORS.primaryBlue` highlight color (selections, spinners, success states).
|
||||
- Never use `dimColor` with gray (e.g. `<Text color="gray" dimColor>`) - it's too hard to read. Use `color="gray"` for secondary text and normal foreground (no color) for primary text.
|
||||
- When thinking about how to handle state or messages from core, look at webview for how it communicates with the vs code extension.
|
||||
- When updating the webview, consider and suggest to the user to update the CLI TUI since we want to provide a similar experience to our terminal users as we do our vs code extension users.
|
||||
|
||||
## Adding New API Providers
|
||||
|
||||
When adding a new API provider to the extension, you must also update the CLI:
|
||||
|
||||
1. **Update `cli/src/components/ModelPicker.tsx`**: Add the provider to the `providerModels` map so `getDefaultModelId()` returns the correct default model. Import the models and default ID from `@shared/api`:
|
||||
```typescript
|
||||
import { newProviderDefaultModelId, newProviderModels } from "@/shared/api"
|
||||
|
||||
export const providerModels = {
|
||||
// ...existing providers
|
||||
"new-provider": { models: newProviderModels, defaultId: newProviderDefaultModelId },
|
||||
}
|
||||
```
|
||||
|
||||
2. **Use `applyProviderConfig()` for auth flows**: When implementing OAuth or other auth flows for the provider, use the shared utility at `cli/src/utils/provider-config.ts`:
|
||||
```typescript
|
||||
import { applyProviderConfig } from "../utils/provider-config"
|
||||
|
||||
// After successful auth:
|
||||
await applyProviderConfig({ providerId: "new-provider", controller })
|
||||
```
|
||||
This handles setting provider, default model, API key mapping, state persistence, and rebuilding the API handler.
|
||||
|
||||
3. **Provider-specific auth**: If the provider uses OAuth (like `openai-codex`), add handling in `SettingsPanelContent.tsx`'s `handleProviderSelect` callback. See the existing Codex OAuth flow as a reference.
|
||||
@@ -176,7 +176,7 @@ Present a final summary:
|
||||
- Slack message copied to clipboard: yes
|
||||
|
||||
Remind the user to:
|
||||
1. Manually trigger the publish release GitHub Action at: https://github.com/cline/cline/actions/workflows/publish.yml (paste `v{VERSION}` as the tag)
|
||||
1. Manually trigger the publish release GitHub Action at: https://github.com/cline/cline/actions/workflows/ext-vscode-publish-stable.yml (paste `v{VERSION}` as the tag)
|
||||
2. Post the Slack message to announce the hotfix
|
||||
|
||||
## Important Notes
|
||||
|
||||
@@ -43,7 +43,7 @@ git push origin v<version>
|
||||
### 4) Trigger publish workflow
|
||||
|
||||
Tell the maintainer to run:
|
||||
https://github.com/cline/cline/actions/workflows/publish.yml
|
||||
https://github.com/cline/cline/actions/workflows/ext-vscode-publish-stable.yml
|
||||
|
||||
Use `v<version>` as the release tag.
|
||||
|
||||
|
||||
@@ -20,8 +20,9 @@ command = "chmod +x ./scripts/run-extension-host.sh && ./scripts/run-extension-h
|
||||
name = "CLI"
|
||||
icon = "run"
|
||||
command = '''
|
||||
npm run cli:build
|
||||
npm run cli:run
|
||||
cd sdk
|
||||
bun install
|
||||
bun run cli
|
||||
'''
|
||||
|
||||
[[actions]]
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
demo.gif filter=lfs diff=lfs merge=lfs -text
|
||||
assets/docs/demo.gif filter=lfs diff=lfs merge=lfs -text
|
||||
webview-ui/src/assets/cline_kanban_demo.mp4 filter=lfs diff=lfs merge=lfs -text
|
||||
webview-ui/src/assets/cline_kanban_demo.webm filter=lfs diff=lfs merge=lfs -text
|
||||
|
||||
* text=auto eol=lf
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
/.github/ @saoudrizwan @arafatkatze @maxpaulus43 @candieduniverse
|
||||
/.github/ @saoudrizwan @arafatkatze @maxpaulus43 @dominiccooney
|
||||
/README.md @saoudrizwan @juanpflores
|
||||
|
||||
@@ -5,7 +5,6 @@ This is a VS Code extension. Read `.clinerules/general.md` for tribal knowledge
|
||||
## Architecture
|
||||
- **Core** (`src/`): `extension.ts` → `WebviewProvider` → `Controller` (single source of truth) → `Task` (agent loop).
|
||||
- **Webview** (`webview-ui/`): React/Vite app. State via `ExtensionStateContext.tsx`, synced through message passing.
|
||||
- **CLI** (`cli/`): React Ink terminal UI sharing core logic. Update CLI when changing webview features.
|
||||
- **Communication**: Protobuf-defined gRPC-like protocol over VS Code message passing. Schemas in `proto/`.
|
||||
- **MCP**: `src/services/mcp/McpHub.ts`.
|
||||
|
||||
@@ -28,7 +27,7 @@ Three proto conversion updates are **required** or the provider silently resets
|
||||
2. `convertApiProviderToProto()` in `src/shared/proto-conversions/models/api-configuration-conversion.ts`.
|
||||
3. `convertProtoToApiProvider()` in the same file.
|
||||
|
||||
Also update: `src/shared/api.ts`, `src/shared/providers/providers.json`, `src/core/api/index.ts`, `webview-ui/.../providerUtils.ts`, `webview-ui/.../validate.ts`, `webview-ui/.../ApiOptions.tsx`, and `cli/src/components/ModelPicker.tsx`.
|
||||
Also update: `src/shared/api.ts`, `src/shared/providers/providers.json`, `src/core/api/index.ts`, `webview-ui/.../providerUtils.ts`, `webview-ui/.../validate.ts`, `webview-ui/.../ApiOptions.tsx`.
|
||||
|
||||
For Responses API providers: add to `isNextGenModelProvider()` in `src/utils/model-utils.ts` and set `apiFormat: ApiFormat.OPENAI_RESPONSES` on models.
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ version: 2
|
||||
updates:
|
||||
# Main extension dependencies
|
||||
- package-ecosystem: "npm"
|
||||
directory: "/"
|
||||
directory: "/apps/vscode"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
# Group all updates into a single PR
|
||||
@@ -20,7 +20,7 @@ updates:
|
||||
|
||||
# Webview UI dependencies
|
||||
- package-ecosystem: "npm"
|
||||
directory: "/webview-ui"
|
||||
directory: "/apps/vscode/webview-ui"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
groups:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
name: Publish CLI to NPM
|
||||
name: cli-publish
|
||||
|
||||
on:
|
||||
schedule:
|
||||
@@ -33,7 +33,7 @@ permissions:
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: sdk
|
||||
working-directory: .
|
||||
|
||||
jobs:
|
||||
publish-main:
|
||||
@@ -88,9 +88,9 @@ jobs:
|
||||
|
||||
- name: Validate release tag
|
||||
id: version
|
||||
env:
|
||||
TAG: ${{ github.event.inputs.git_tag }}
|
||||
run: |
|
||||
TAG="${{ github.event.inputs.git_tag }}"
|
||||
|
||||
if [ -z "$TAG" ]; then
|
||||
echo "git_tag is required when publish_target=main"
|
||||
exit 1
|
||||
@@ -102,7 +102,7 @@ jobs:
|
||||
fi
|
||||
|
||||
VERSION="${TAG#cli-v}"
|
||||
PACKAGE_VERSION=$(node -p "require('./apps/cli/package.json').version")
|
||||
PACKAGE_VERSION=$(node -p "require('./sdk/apps/cli/package.json').version")
|
||||
|
||||
if [ "$PACKAGE_VERSION" != "$VERSION" ]; then
|
||||
echo "sdk/apps/cli/package.json version ${PACKAGE_VERSION} does not match ${TAG}"
|
||||
@@ -132,6 +132,15 @@ jobs:
|
||||
|
||||
- name: Build SDK packages
|
||||
run: bun run build:sdk
|
||||
env:
|
||||
TELEMETRY_SERVICE_API_KEY: ${{ secrets.TELEMETRY_SERVICE_API_KEY }}
|
||||
ERROR_SERVICE_API_KEY: ${{ secrets.ERROR_SERVICE_API_KEY }}
|
||||
OTEL_TELEMETRY_ENABLED: ${{ secrets.OTEL_TELEMETRY_ENABLED }}
|
||||
OTEL_LOGS_EXPORTER: otlp
|
||||
OTEL_METRICS_EXPORTER: otlp
|
||||
OTEL_EXPORTER_OTLP_PROTOCOL: ${{ secrets.OTEL_EXPORTER_OTLP_PROTOCOL }}
|
||||
OTEL_EXPORTER_OTLP_ENDPOINT: ${{ secrets.OTEL_EXPORTER_OTLP_ENDPOINT }}
|
||||
OTEL_EXPORTER_OTLP_HEADERS: ${{ secrets.OTEL_EXPORTER_OTLP_HEADERS }}
|
||||
|
||||
- name: Run tests
|
||||
run: bun run test
|
||||
@@ -139,10 +148,20 @@ jobs:
|
||||
- name: Build platform binaries
|
||||
run: bun script/build.ts --install-native-variants --skip-sdk-build
|
||||
working-directory: sdk/apps/cli
|
||||
env:
|
||||
TELEMETRY_SERVICE_API_KEY: ${{ secrets.TELEMETRY_SERVICE_API_KEY }}
|
||||
ERROR_SERVICE_API_KEY: ${{ secrets.ERROR_SERVICE_API_KEY }}
|
||||
OTEL_TELEMETRY_ENABLED: ${{ secrets.OTEL_TELEMETRY_ENABLED }}
|
||||
OTEL_LOGS_EXPORTER: otlp
|
||||
OTEL_METRICS_EXPORTER: otlp
|
||||
OTEL_EXPORTER_OTLP_PROTOCOL: ${{ secrets.OTEL_EXPORTER_OTLP_PROTOCOL }}
|
||||
OTEL_EXPORTER_OTLP_ENDPOINT: ${{ secrets.OTEL_EXPORTER_OTLP_ENDPOINT }}
|
||||
OTEL_EXPORTER_OTLP_HEADERS: ${{ secrets.OTEL_EXPORTER_OTLP_HEADERS }}
|
||||
|
||||
- name: Verify build output
|
||||
env:
|
||||
VERSION: ${{ steps.version.outputs.version }}
|
||||
run: |
|
||||
VERSION="${{ steps.version.outputs.version }}"
|
||||
EXPECTED=(
|
||||
"@cline/cli-darwin-arm64"
|
||||
"@cline/cli-darwin-x64"
|
||||
@@ -153,7 +172,7 @@ jobs:
|
||||
)
|
||||
|
||||
for package_name in "${EXPECTED[@]}"; do
|
||||
dir="apps/cli/dist/${package_name#@cline/}"
|
||||
dir="sdk/apps/cli/dist/${package_name#@cline/}"
|
||||
if [ ! -f "$dir/package.json" ]; then
|
||||
echo "Missing package manifest: $dir/package.json"
|
||||
exit 1
|
||||
@@ -179,16 +198,17 @@ jobs:
|
||||
|
||||
- name: Get Previous CLI Tag
|
||||
id: prev_tag
|
||||
env:
|
||||
CURRENT_TAG: ${{ steps.version.outputs.tag }}
|
||||
run: |
|
||||
CURRENT_TAG="${{ steps.version.outputs.tag }}"
|
||||
PREV_TAG=$(git describe --tags --abbrev=0 --match 'cli-v*' "$CURRENT_TAG^" 2>/dev/null || echo "")
|
||||
echo "prev_tag=$PREV_TAG" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Get Changelog Entry
|
||||
id: changelog
|
||||
run: |
|
||||
# Grab content between the first "## " header and the next one in apps/cli/CHANGELOG.md
|
||||
CONTENT=$(awk '/^## [0-9]/{if(found) exit; found=1; next} found{print}' apps/cli/CHANGELOG.md)
|
||||
# Grab content between the first "## " header and the next one in sdk/apps/cli/CHANGELOG.md
|
||||
CONTENT=$(awk '/^## [0-9]/{if(found) exit; found=1; next} found{print}' sdk/apps/cli/CHANGELOG.md)
|
||||
echo "content<<EOF" >> $GITHUB_OUTPUT
|
||||
echo "$CONTENT" >> $GITHUB_OUTPUT
|
||||
echo "EOF" >> $GITHUB_OUTPUT
|
||||
@@ -200,13 +220,15 @@ jobs:
|
||||
name: "CLI v${{ steps.version.outputs.version }}"
|
||||
body: |
|
||||
${{ steps.changelog.outputs.content }}
|
||||
${{ steps.prev_tag.outputs.prev_tag != '' && format('Full Changelog: https://github.com/{0}/compare/{1}...{2}', github.repository, steps.prev_tag.outputs.prev_tag, steps.version.outputs.tag) || '' }}
|
||||
|
||||
${{ steps.prev_tag.outputs.prev_tag != '' && format('**Full Changelog**: https://github.com/{0}/compare/{1}...{2}', github.repository, steps.prev_tag.outputs.prev_tag, steps.version.outputs.tag) || '' }}
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Summary
|
||||
env:
|
||||
VERSION: ${{ steps.version.outputs.version }}
|
||||
run: |
|
||||
VERSION="${{ steps.version.outputs.version }}"
|
||||
echo "Published cline@${VERSION} to npm with dist-tag 'latest'"
|
||||
echo "Install with: npm install -g cline"
|
||||
|
||||
@@ -309,6 +331,15 @@ jobs:
|
||||
- name: Build SDK packages
|
||||
if: steps.check_commits.outputs.skip != 'true'
|
||||
run: bun run build:sdk
|
||||
env:
|
||||
TELEMETRY_SERVICE_API_KEY: ${{ secrets.TELEMETRY_SERVICE_API_KEY }}
|
||||
ERROR_SERVICE_API_KEY: ${{ secrets.ERROR_SERVICE_API_KEY }}
|
||||
OTEL_TELEMETRY_ENABLED: ${{ secrets.OTEL_TELEMETRY_ENABLED }}
|
||||
OTEL_LOGS_EXPORTER: otlp
|
||||
OTEL_METRICS_EXPORTER: otlp
|
||||
OTEL_EXPORTER_OTLP_PROTOCOL: ${{ secrets.OTEL_EXPORTER_OTLP_PROTOCOL }}
|
||||
OTEL_EXPORTER_OTLP_ENDPOINT: ${{ secrets.OTEL_EXPORTER_OTLP_ENDPOINT }}
|
||||
OTEL_EXPORTER_OTLP_HEADERS: ${{ secrets.OTEL_EXPORTER_OTLP_HEADERS }}
|
||||
|
||||
- name: Run tests
|
||||
if: steps.check_commits.outputs.skip != 'true'
|
||||
@@ -318,7 +349,7 @@ jobs:
|
||||
if: steps.check_commits.outputs.skip != 'true'
|
||||
id: version
|
||||
run: |
|
||||
BASE_VERSION=$(node -p "require('./apps/cli/package.json').version")
|
||||
BASE_VERSION=$(node -p "require('./sdk/apps/cli/package.json').version")
|
||||
TIMESTAMP=$(date +%s)
|
||||
VERSION="${BASE_VERSION}-nightly.${TIMESTAMP}"
|
||||
|
||||
@@ -329,28 +360,37 @@ jobs:
|
||||
|
||||
- name: Update nightly package version
|
||||
if: steps.check_commits.outputs.skip != 'true'
|
||||
env:
|
||||
VERSION: ${{ steps.version.outputs.version }}
|
||||
run: |
|
||||
VERSION="${{ steps.version.outputs.version }}"
|
||||
node -e '
|
||||
const fs = require("node:fs");
|
||||
const path = "apps/cli/package.json";
|
||||
const path = "sdk/apps/cli/package.json";
|
||||
const pkg = JSON.parse(fs.readFileSync(path, "utf8"));
|
||||
pkg.version = process.env.VERSION;
|
||||
fs.writeFileSync(path, `${JSON.stringify(pkg, null, "\t")}\n`);
|
||||
'
|
||||
cat apps/cli/package.json | grep '"version"'
|
||||
env:
|
||||
VERSION: ${{ steps.version.outputs.version }}
|
||||
cat sdk/apps/cli/package.json | grep '"version"'
|
||||
|
||||
- name: Build platform binaries
|
||||
if: steps.check_commits.outputs.skip != 'true'
|
||||
run: bun script/build.ts --install-native-variants --skip-sdk-build
|
||||
working-directory: sdk/apps/cli
|
||||
env:
|
||||
TELEMETRY_SERVICE_API_KEY: ${{ secrets.TELEMETRY_SERVICE_API_KEY }}
|
||||
ERROR_SERVICE_API_KEY: ${{ secrets.ERROR_SERVICE_API_KEY }}
|
||||
OTEL_TELEMETRY_ENABLED: ${{ secrets.OTEL_TELEMETRY_ENABLED }}
|
||||
OTEL_LOGS_EXPORTER: otlp
|
||||
OTEL_METRICS_EXPORTER: otlp
|
||||
OTEL_EXPORTER_OTLP_PROTOCOL: ${{ secrets.OTEL_EXPORTER_OTLP_PROTOCOL }}
|
||||
OTEL_EXPORTER_OTLP_ENDPOINT: ${{ secrets.OTEL_EXPORTER_OTLP_ENDPOINT }}
|
||||
OTEL_EXPORTER_OTLP_HEADERS: ${{ secrets.OTEL_EXPORTER_OTLP_HEADERS }}
|
||||
|
||||
- name: Verify build output
|
||||
if: steps.check_commits.outputs.skip != 'true'
|
||||
env:
|
||||
VERSION: ${{ steps.version.outputs.version }}
|
||||
run: |
|
||||
VERSION="${{ steps.version.outputs.version }}"
|
||||
EXPECTED=(
|
||||
"@cline/cli-darwin-arm64"
|
||||
"@cline/cli-darwin-x64"
|
||||
@@ -361,7 +401,7 @@ jobs:
|
||||
)
|
||||
|
||||
for package_name in "${EXPECTED[@]}"; do
|
||||
dir="apps/cli/dist/${package_name#@cline/}"
|
||||
dir="sdk/apps/cli/dist/${package_name#@cline/}"
|
||||
if [ ! -f "$dir/package.json" ]; then
|
||||
echo "Missing package manifest: $dir/package.json"
|
||||
exit 1
|
||||
@@ -388,7 +428,8 @@ jobs:
|
||||
|
||||
- name: Summary
|
||||
if: steps.check_commits.outputs.skip != 'true'
|
||||
env:
|
||||
VERSION: ${{ steps.version.outputs.version }}
|
||||
run: |
|
||||
VERSION="${{ steps.version.outputs.version }}"
|
||||
echo "Published cline@${VERSION} to npm with dist-tag 'nightly'"
|
||||
echo "Install with: npm install -g cline@nightly"
|
||||
@@ -1,78 +0,0 @@
|
||||
name: Smoke Tests
|
||||
|
||||
# Temporarily disabled: this workflow built and linked the legacy CLI
|
||||
# (`cd cli && npm install && npm run build && npm link`) before running the
|
||||
# smoke-test scenarios. The legacy CLI publish chain has been retired in
|
||||
# favor of the SDK CLI at `sdk/apps/cli/`. The scenarios under
|
||||
# `evals/smoke-tests/scenarios/` are CLI-agnostic and should be re-enabled
|
||||
# once the build step is repointed at the new SDK CLI. Until then, only
|
||||
# manual `workflow_dispatch` runs are accepted (and will fail in their
|
||||
# current form).
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: smoke-tests-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
smoke-tests:
|
||||
name: Smoke Tests
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '22'
|
||||
cache: 'npm'
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Build and install CLI
|
||||
run: |
|
||||
npm run protos
|
||||
cd cli && npm install && npm run build && npm link
|
||||
echo "$(npm config get prefix)/bin" >> $GITHUB_PATH
|
||||
|
||||
- name: Verify CLI
|
||||
run: cline --version
|
||||
|
||||
- name: Run smoke tests
|
||||
env:
|
||||
CLINE_API_KEY: ${{ secrets.CLINE_API_KEY }}
|
||||
run: |
|
||||
cline auth -p cline -k "$CLINE_API_KEY" -m "anthropic/claude-sonnet-4.5"
|
||||
max_attempts=3
|
||||
for attempt in $(seq 1 $max_attempts); do
|
||||
echo "::group::Attempt $attempt of $max_attempts"
|
||||
if npx tsx evals/smoke-tests/run-smoke-tests.ts --trials 1 --parallel; then
|
||||
echo "::endgroup::"
|
||||
echo "Smoke tests passed on attempt $attempt"
|
||||
exit 0
|
||||
fi
|
||||
echo "::endgroup::"
|
||||
if [ $attempt -lt $max_attempts ]; then
|
||||
echo "::warning::Smoke tests failed on attempt $attempt, retrying..."
|
||||
sleep 10
|
||||
fi
|
||||
done
|
||||
echo "::error::Smoke tests failed after $max_attempts attempts"
|
||||
exit 1
|
||||
|
||||
- name: Generate summary
|
||||
if: always()
|
||||
run: cat evals/smoke-tests/results/latest/summary.md >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
- name: Upload results
|
||||
uses: actions/upload-artifact@v4
|
||||
if: always()
|
||||
with:
|
||||
name: smoke-test-results-${{ github.run_id }}
|
||||
path: evals/smoke-tests/results/latest/
|
||||
retention-days: 30
|
||||
@@ -1,4 +1,4 @@
|
||||
name: Trigger Jetbrains Plugin <-> Cline Tests
|
||||
name: ext-jb-test-integration
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [opened, reopened]
|
||||
@@ -15,9 +15,11 @@ jobs:
|
||||
trigger-integration-test:
|
||||
name: Run Tests
|
||||
runs-on: ubuntu-latest
|
||||
# Run on PR open/reopen, or when someone comments /test-jetbrains on a PR
|
||||
# Auto-run only for trusted PR authors. Anyone else needs a maintainer
|
||||
# to opt their PR in by commenting /test-jetbrains.
|
||||
if: |
|
||||
github.event_name == 'pull_request_target' ||
|
||||
(github.event_name == 'pull_request_target' &&
|
||||
contains(fromJSON('["MEMBER","OWNER","COLLABORATOR"]'), github.event.pull_request.author_association)) ||
|
||||
(github.event_name == 'issue_comment' &&
|
||||
github.event.issue.pull_request &&
|
||||
contains(github.event.comment.body, '/test-jetbrains') &&
|
||||
@@ -27,8 +29,8 @@ jobs:
|
||||
id: app-token
|
||||
uses: actions/create-github-app-token@v1
|
||||
with:
|
||||
app-id: 1998650
|
||||
private-key: ${{ secrets.CLINE_JETBRAINS_WORKFLOW_KEY }}
|
||||
app-id: ${{ vars.CLINE_JETBRAINS_APP_ID }}
|
||||
private-key: ${{ secrets.CLINE_JETBRAINS_APP_KEY }}
|
||||
owner: cline
|
||||
repositories: intellij-plugin
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
name: "Publish Nightly Release"
|
||||
name: ext-vscode-publish-nightly
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
@@ -10,7 +10,7 @@ run-name: "Publish Nightly from ${{ github.ref_name }} @ ${{ github.sha }}"
|
||||
# runs on the same ref can collide on the same version and cause publish failures
|
||||
# or inconsistent tagging. Runs on different branches proceed independently.
|
||||
concurrency:
|
||||
group: publish-nightly-${{ github.ref }}
|
||||
group: ext-vscode-publish-nightly-${{ github.ref }}
|
||||
cancel-in-progress: false
|
||||
|
||||
permissions: {}
|
||||
@@ -20,7 +20,8 @@ jobs:
|
||||
if: github.repository == 'cline/cline' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dpc/sdk-migration-simpler-login')
|
||||
permissions:
|
||||
contents: read
|
||||
uses: ./.github/workflows/test.yml
|
||||
pull-requests: read
|
||||
uses: ./.github/workflows/ext-vscode-test.yml
|
||||
|
||||
publish:
|
||||
needs: test
|
||||
@@ -40,6 +41,7 @@ jobs:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Show build source
|
||||
working-directory: ${{ github.workspace }}
|
||||
run: |
|
||||
echo "Building ref: $GITHUB_REF"
|
||||
echo "Building sha: $GITHUB_SHA"
|
||||
@@ -61,15 +63,6 @@ jobs:
|
||||
- name: Install Publishing Tools
|
||||
run: npm install -g @vscode/vsce ovsx
|
||||
|
||||
- name: Verify LFS media assets are resolved
|
||||
run: |
|
||||
for FILE in webview-ui/src/assets/cline_kanban_demo.mp4 webview-ui/src/assets/cline_kanban_demo.webm; do
|
||||
if grep -q "git-lfs.github.com/spec/v1" "$FILE"; then
|
||||
echo "Error: $FILE is still a Git LFS pointer in CI checkout"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
- name: Publish Nightly Extension
|
||||
env:
|
||||
VSCE_PAT: ${{ secrets.VSCE_PAT }}
|
||||
@@ -87,6 +80,7 @@ jobs:
|
||||
run: npm run publish:marketplace:nightly
|
||||
|
||||
- name: Tag published commit
|
||||
working-directory: ${{ github.workspace }}
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
@@ -1,4 +1,4 @@
|
||||
name: "Publish Release"
|
||||
name: ext-vscode-publish-stable
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
@@ -29,13 +29,16 @@ permissions:
|
||||
|
||||
jobs:
|
||||
test:
|
||||
uses: ./.github/workflows/test.yml
|
||||
uses: ./.github/workflows/ext-vscode-test.yml
|
||||
|
||||
publish:
|
||||
needs: test
|
||||
name: Publish Extension
|
||||
runs-on: ubuntu-latest
|
||||
environment: publish
|
||||
defaults:
|
||||
run:
|
||||
working-directory: apps/vscode
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@@ -47,6 +50,7 @@ jobs:
|
||||
|
||||
- name: Resolve Release Tag
|
||||
id: resolve_tag
|
||||
working-directory: ${{ github.workspace }}
|
||||
env:
|
||||
TAG: ${{ github.event.inputs.tag }}
|
||||
AUTO_CREATE: ${{ github.event.inputs.auto_create_tag_from_main }}
|
||||
@@ -135,15 +139,6 @@ 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 }}
|
||||
@@ -180,6 +175,7 @@ jobs:
|
||||
|
||||
- name: Get Previous Tag
|
||||
id: prev_tag
|
||||
working-directory: ${{ github.workspace }}
|
||||
run: |
|
||||
CURRENT_TAG="${{ steps.resolve_tag.outputs.tag }}"
|
||||
PREV_TAG=$(git describe --tags --abbrev=0 "$CURRENT_TAG^" 2>/dev/null || echo "")
|
||||
@@ -187,6 +183,7 @@ jobs:
|
||||
|
||||
- name: Get Changelog Entry
|
||||
id: changelog
|
||||
working-directory: ${{ github.workspace }}
|
||||
run: |
|
||||
# Get content between first ## [ and second ## [
|
||||
CONTENT=$(awk '/^## \[/{if(found) exit; found=1; next} found{print}' CHANGELOG.md)
|
||||
@@ -198,7 +195,7 @@ jobs:
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
tag_name: ${{ steps.resolve_tag.outputs.tag }}
|
||||
files: "*.vsix"
|
||||
files: "apps/vscode/*.vsix"
|
||||
body: |
|
||||
${{ steps.changelog.outputs.content }}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
name: E2E Tests
|
||||
name: ext-vscode-test-e2e
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -12,8 +12,53 @@ concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: read
|
||||
|
||||
jobs:
|
||||
detect-changes:
|
||||
runs-on: ubuntu-latest
|
||||
name: Detect Changes
|
||||
outputs:
|
||||
e2e: ${{ steps.force.outputs.run_all == 'true' || steps.filter.outputs.e2e == 'true' }}
|
||||
steps:
|
||||
- id: force
|
||||
if: github.event_name == 'push' || github.event_name == 'workflow_dispatch'
|
||||
run: echo "run_all=true" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
if: steps.force.outputs.run_all != 'true'
|
||||
|
||||
- uses: dorny/paths-filter@v3
|
||||
if: steps.force.outputs.run_all != 'true'
|
||||
id: filter
|
||||
with:
|
||||
filters: |
|
||||
e2e:
|
||||
- 'apps/vscode/src/**'
|
||||
- 'apps/vscode/webview-ui/**'
|
||||
- 'apps/vscode/proto/**'
|
||||
- 'apps/vscode/tests/**'
|
||||
- 'apps/vscode/scripts/**'
|
||||
- 'apps/vscode/standalone/**'
|
||||
- 'apps/vscode/assets/**'
|
||||
- 'apps/vscode/walkthrough/**'
|
||||
- 'apps/vscode/package.json'
|
||||
- 'apps/vscode/package-lock.json'
|
||||
- 'apps/vscode/buf.yaml'
|
||||
- 'apps/vscode/tsconfig*.json'
|
||||
- 'apps/vscode/biome.jsonc'
|
||||
- 'apps/vscode/esbuild.mjs'
|
||||
- 'apps/vscode/.mocharc.json'
|
||||
- 'apps/vscode/.vscode-test.mjs'
|
||||
- 'apps/vscode/.vscodeignore'
|
||||
- 'apps/vscode/playwright*.ts'
|
||||
- '.github/workflows/ext-vscode-test-e2e.yml'
|
||||
|
||||
matrix_prep:
|
||||
needs: detect-changes
|
||||
if: needs.detect-changes.outputs.e2e == 'true'
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
matrix: ${{ steps.set-matrix.outputs.matrix }}
|
||||
@@ -23,7 +68,8 @@ jobs:
|
||||
echo 'matrix=[{"runner":"ubuntu"},{"runner":"windows"},{"runner":"macos"}]' >> $GITHUB_OUTPUT
|
||||
|
||||
e2e:
|
||||
needs: matrix_prep
|
||||
needs: [detect-changes, matrix_prep]
|
||||
if: needs.detect-changes.outputs.e2e == 'true'
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@@ -33,6 +79,9 @@ jobs:
|
||||
permissions:
|
||||
id-token: write
|
||||
contents: read
|
||||
defaults:
|
||||
run:
|
||||
working-directory: apps/vscode
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup Node.js environment
|
||||
@@ -45,24 +94,24 @@ jobs:
|
||||
uses: actions/cache@v4
|
||||
id: root-cache
|
||||
with:
|
||||
path: node_modules
|
||||
key: ${{ runner.os }}-npm-${{ hashFiles('package-lock.json') }}
|
||||
path: apps/vscode/node_modules
|
||||
key: ${{ runner.os }}-npm-${{ hashFiles('apps/vscode/package-lock.json') }}
|
||||
|
||||
# Cache webview-ui dependencies - only reuse if package-lock.json exactly matches
|
||||
- name: Cache webview-ui dependencies
|
||||
uses: actions/cache@v4
|
||||
id: webview-cache
|
||||
with:
|
||||
path: webview-ui/node_modules
|
||||
key: ${{ runner.os }}-npm-webview-${{ hashFiles('webview-ui/package-lock.json') }}
|
||||
path: apps/vscode/webview-ui/node_modules
|
||||
key: ${{ runner.os }}-npm-webview-${{ hashFiles('apps/vscode/webview-ui/package-lock.json') }}
|
||||
|
||||
# Cache VS Code installation
|
||||
- name: Cache VS Code
|
||||
uses: actions/cache@v4
|
||||
id: vscode-cache
|
||||
with:
|
||||
path: .vscode-test
|
||||
key: vscode-${{ runner.os }}-stable-${{ hashFiles('.vscode-test.mjs', 'package.json') }}
|
||||
path: apps/vscode/.vscode-test
|
||||
key: vscode-${{ runner.os }}-stable-${{ hashFiles('apps/vscode/.vscode-test.mjs', 'apps/vscode/package.json') }}
|
||||
restore-keys: |
|
||||
vscode-${{ runner.os }}-stable-
|
||||
|
||||
@@ -75,7 +124,7 @@ jobs:
|
||||
~/.cache/ms-playwright
|
||||
~/Library/Caches/ms-playwright
|
||||
~/AppData/Local/ms-playwright
|
||||
key: playwright-browsers-${{ runner.os }}-${{ hashFiles('package-lock.json') }}
|
||||
key: playwright-browsers-${{ runner.os }}-${{ hashFiles('apps/vscode/package-lock.json') }}
|
||||
restore-keys: |
|
||||
playwright-browsers-${{ runner.os }}-
|
||||
|
||||
@@ -0,0 +1,357 @@
|
||||
name: ext-vscode-test
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
workflow_call:
|
||||
|
||||
# Set default permissions for all jobs
|
||||
permissions:
|
||||
contents: read # Needed to check out code
|
||||
pull-requests: read # Needed for changed-file detection on pull requests
|
||||
|
||||
jobs:
|
||||
detect-changes:
|
||||
runs-on: ubuntu-latest
|
||||
name: Detect Changes
|
||||
outputs:
|
||||
vscode: ${{ steps.force.outputs.run_all == 'true' || steps.filter.outputs.vscode == 'true' }}
|
||||
testing_platform: ${{ steps.force.outputs.run_all == 'true' || steps.filter.outputs.testing_platform == 'true' }}
|
||||
steps:
|
||||
- id: force
|
||||
if: github.event_name == 'push' || github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_call'
|
||||
run: echo "run_all=true" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
if: steps.force.outputs.run_all != 'true'
|
||||
|
||||
- uses: dorny/paths-filter@v3
|
||||
if: steps.force.outputs.run_all != 'true'
|
||||
id: filter
|
||||
with:
|
||||
filters: |
|
||||
vscode:
|
||||
- 'apps/vscode/src/**'
|
||||
- 'apps/vscode/webview-ui/**'
|
||||
- 'apps/vscode/proto/**'
|
||||
- 'apps/vscode/tests/**'
|
||||
- 'apps/vscode/scripts/**'
|
||||
- 'apps/vscode/standalone/**'
|
||||
- 'apps/vscode/assets/**'
|
||||
- 'apps/vscode/walkthrough/**'
|
||||
- 'apps/vscode/package.json'
|
||||
- 'apps/vscode/package-lock.json'
|
||||
- 'apps/vscode/buf.yaml'
|
||||
- 'apps/vscode/tsconfig*.json'
|
||||
- 'apps/vscode/biome.jsonc'
|
||||
- 'apps/vscode/esbuild.mjs'
|
||||
- 'apps/vscode/.mocharc.json'
|
||||
- 'apps/vscode/.nycrc*.json'
|
||||
- 'apps/vscode/.vscode-test.mjs'
|
||||
- 'apps/vscode/test-setup.js'
|
||||
- '.github/workflows/ext-vscode-test.yml'
|
||||
testing_platform:
|
||||
- 'apps/vscode/src/**'
|
||||
- 'apps/vscode/proto/**'
|
||||
- 'apps/vscode/standalone/**'
|
||||
- 'apps/vscode/testing-platform/**'
|
||||
- 'apps/vscode/tests/specs/**'
|
||||
- 'apps/vscode/package.json'
|
||||
- 'apps/vscode/package-lock.json'
|
||||
- 'apps/vscode/buf.yaml'
|
||||
- 'apps/vscode/tsconfig*.json'
|
||||
- 'apps/vscode/esbuild.mjs'
|
||||
- 'apps/vscode/.vscodeignore'
|
||||
- 'apps/vscode/scripts/**'
|
||||
- '.github/workflows/ext-vscode-test.yml'
|
||||
|
||||
quality-checks:
|
||||
needs: detect-changes
|
||||
if: needs.detect-changes.outputs.vscode == 'true' || needs.detect-changes.outputs.testing_platform == 'true'
|
||||
runs-on: ubuntu-latest
|
||||
name: Quality Checks
|
||||
defaults:
|
||||
run:
|
||||
working-directory: apps/vscode
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node.js environment
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22
|
||||
cache: 'npm'
|
||||
cache-dependency-path: |
|
||||
apps/vscode/package-lock.json
|
||||
apps/vscode/webview-ui/package-lock.json
|
||||
|
||||
- name: Install root dependencies
|
||||
run: npm ci
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Install webview-ui dependencies
|
||||
run: cd webview-ui && npm ci
|
||||
|
||||
- name: Run Quality Checks (Parallel)
|
||||
run: npm run ci:check-all
|
||||
|
||||
vscode-test:
|
||||
needs: [detect-changes, quality-checks]
|
||||
if: needs.detect-changes.outputs.vscode == 'true'
|
||||
env:
|
||||
VSCODE_TEST_VERSION: 1.103.0
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
name: ${{ matrix.os == 'ubuntu-latest' && 'vscode test' || format('vscode test ({0})', matrix.os) }}
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
working-directory: apps/vscode
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node.js environment
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22
|
||||
cache: 'npm'
|
||||
cache-dependency-path: |
|
||||
apps/vscode/package-lock.json
|
||||
apps/vscode/webview-ui/package-lock.json
|
||||
|
||||
- name: Install root dependencies
|
||||
run: npm ci
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Install webview-ui dependencies
|
||||
run: cd webview-ui && npm ci
|
||||
|
||||
- name: Set up NPM on Windows
|
||||
if: runner.os == 'Windows'
|
||||
run: |
|
||||
npm config set script-shell "C:\\Program Files\\Git\\bin\\bash.exe"
|
||||
|
||||
- name: Cache VS Code test runtime
|
||||
if: runner.os == 'Windows'
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: .vscode-test
|
||||
key: vscode-test-runtime-${{ runner.os }}-${{ env.VSCODE_TEST_VERSION }}
|
||||
|
||||
# Build the extension and tests (without redundant checks)
|
||||
- name: Build Tests and Extension
|
||||
id: build_step
|
||||
run: npm run ci:build
|
||||
|
||||
- name: Unit Tests with coverage - Linux
|
||||
id: unit_tests_linux
|
||||
if: ${{ !cancelled() && steps.build_step.outcome == 'success' && runner.os == 'Linux' }}
|
||||
run: |
|
||||
npx nyc --nycrc-path .nycrc.unit.json --reporter=lcov npm run test:unit
|
||||
|
||||
- name: Unit Tests - Non-Linux
|
||||
id: unit_tests_non_linux
|
||||
if: ${{ !cancelled() && steps.build_step.outcome == 'success' && runner.os != 'Linux' }}
|
||||
run: |
|
||||
npm run test:unit
|
||||
|
||||
- name: Extension Integration Tests - Linux
|
||||
id: integration_tests_linux
|
||||
if: ${{ !cancelled() && steps.build_step.outcome == 'success' && runner.os == 'Linux' }}
|
||||
run: xvfb-run -a npm run test:coverage
|
||||
|
||||
- name: Extension Integration Tests - Non-Linux
|
||||
id: integration_tests_non_linux
|
||||
if: ${{ !cancelled() && steps.build_step.outcome == 'success' && runner.os != 'Linux' }}
|
||||
run: |
|
||||
for attempt in 1 2 3; do
|
||||
echo "Running extension integration tests (attempt ${attempt}/3)"
|
||||
if npm run test:integration; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ "$attempt" -eq 3 ]; then
|
||||
echo "Extension integration tests failed after 3 attempts"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Extension integration tests failed; retrying after short delay"
|
||||
sleep 5
|
||||
done
|
||||
|
||||
- name: Webview Tests with Coverage
|
||||
id: webview_tests
|
||||
if: ${{ !cancelled() && steps.build_step.outcome == 'success' }}
|
||||
run: |
|
||||
cd webview-ui
|
||||
npm run test:coverage
|
||||
|
||||
- name: Save Coverage Reports
|
||||
uses: actions/upload-artifact@v4
|
||||
# Only upload artifacts on Linux - We only need coverage from one OS
|
||||
if: runner.os == 'Linux'
|
||||
with:
|
||||
name: pr-coverage-reports
|
||||
path: |
|
||||
apps/vscode/coverage-unit/lcov.info
|
||||
apps/vscode/webview-ui/coverage/lcov.info
|
||||
|
||||
test-platform-integration:
|
||||
needs: [detect-changes, quality-checks]
|
||||
if: needs.detect-changes.outputs.testing_platform == 'true'
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
working-directory: apps/vscode
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node.js environment
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22
|
||||
cache: 'npm'
|
||||
cache-dependency-path: |
|
||||
apps/vscode/package-lock.json
|
||||
apps/vscode/webview-ui/package-lock.json
|
||||
apps/vscode/testing-platform/package-lock.json
|
||||
|
||||
- name: Install root dependencies
|
||||
run: npm ci
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Install webview-ui dependencies
|
||||
run: cd webview-ui && npm ci
|
||||
|
||||
- name: Download ripgrep binaries
|
||||
run: npm run download-ripgrep
|
||||
|
||||
- name: Compile Standalone
|
||||
run: npm run compile-standalone
|
||||
|
||||
- name: Install testing platform dependencies
|
||||
run: cd testing-platform && npm ci
|
||||
|
||||
- name: Running testing platform integration spec tests
|
||||
timeout-minutes: 7
|
||||
run: npm run test:tp-orchestrator -- tests/specs/ --count=1 --coverage
|
||||
|
||||
- name: Save Coverage Reports
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: test-platform-integration-core-coverage
|
||||
path: apps/vscode/coverage/**/lcov.info
|
||||
|
||||
# Keep the required "test" check as a tiny aggregate gate instead of the conditional
|
||||
# VS Code matrix. GitHub treats conditionally skipped jobs as successful required
|
||||
# checks, so the gate below preserves the old required check name while making sure
|
||||
# whichever filtered test jobs were selected actually passed.
|
||||
test:
|
||||
needs: [detect-changes, quality-checks, vscode-test, test-platform-integration]
|
||||
if: ${{ !cancelled() }}
|
||||
runs-on: ubuntu-latest
|
||||
name: test
|
||||
steps:
|
||||
- name: Verify selected test jobs
|
||||
env:
|
||||
DETECT_CHANGES_RESULT: ${{ needs.detect-changes.result }}
|
||||
QUALITY_CHECKS_RESULT: ${{ needs.quality-checks.result }}
|
||||
VSCODE_CHANGED: ${{ needs.detect-changes.outputs.vscode }}
|
||||
TESTING_PLATFORM_CHANGED: ${{ needs.detect-changes.outputs.testing_platform }}
|
||||
VSCODE_TEST_RESULT: ${{ needs.vscode-test.result }}
|
||||
TEST_PLATFORM_RESULT: ${{ needs.test-platform-integration.result }}
|
||||
run: |
|
||||
if [ "$DETECT_CHANGES_RESULT" != "success" ]; then
|
||||
echo "detect-changes did not succeed: $DETECT_CHANGES_RESULT"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$VSCODE_CHANGED" != "true" ] && [ "$TESTING_PLATFORM_CHANGED" != "true" ]; then
|
||||
echo "No root test paths changed; skipping root test requirements."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ "$QUALITY_CHECKS_RESULT" != "success" ]; then
|
||||
echo "quality-checks did not succeed: $QUALITY_CHECKS_RESULT"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$VSCODE_CHANGED" = "true" ] && [ "$VSCODE_TEST_RESULT" != "success" ]; then
|
||||
echo "vscode-test did not succeed: $VSCODE_TEST_RESULT"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$TESTING_PLATFORM_CHANGED" = "true" ] && [ "$TEST_PLATFORM_RESULT" != "success" ]; then
|
||||
echo "test-platform-integration did not succeed: $TEST_PLATFORM_RESULT"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Selected root test jobs passed."
|
||||
|
||||
qlty:
|
||||
needs: [detect-changes, quality-checks, vscode-test, test-platform-integration]
|
||||
if: ${{ !cancelled() && needs.quality-checks.result == 'success' && (needs.vscode-test.result == 'success' || needs.vscode-test.result == 'skipped') && (needs.test-platform-integration.result == 'success' || needs.test-platform-integration.result == 'skipped') && (needs.detect-changes.outputs.vscode == 'true' || needs.detect-changes.outputs.testing_platform == 'true') }}
|
||||
runs-on: ubuntu-latest
|
||||
# Run on PRs to main, pushes to main, and manual dispatches
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Download unit tests coverage reports
|
||||
if: needs.detect-changes.outputs.vscode == 'true'
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: pr-coverage-reports
|
||||
path: apps/vscode
|
||||
|
||||
- name: Upload core unit tests coverage to Qlty
|
||||
if: needs.detect-changes.outputs.vscode == 'true'
|
||||
uses: qltysh/qlty-action/coverage@v2
|
||||
with:
|
||||
token: ${{ secrets.QLTY_COVERAGE_TOKEN }}
|
||||
# we can merge multiple files if necessary
|
||||
files: |
|
||||
apps/vscode/coverage-unit/lcov.info
|
||||
tag: unit:core
|
||||
|
||||
- name: Upload webview-ui unit tests coverage to Qlty
|
||||
if: needs.detect-changes.outputs.vscode == 'true'
|
||||
uses: qltysh/qlty-action/coverage@v2
|
||||
with:
|
||||
token: ${{ secrets.QLTY_COVERAGE_TOKEN }}
|
||||
# we can merge multiple files if necessary
|
||||
files: |
|
||||
apps/vscode/webview-ui/coverage/lcov.info
|
||||
tag: unit:webview-ui
|
||||
add-prefix: webview-ui/
|
||||
|
||||
- name: Download test platform integration core coverage artifact
|
||||
if: needs.detect-changes.outputs.testing_platform == 'true'
|
||||
uses: actions/download-artifact@v4
|
||||
continue-on-error: true
|
||||
id: download-integration-coverage
|
||||
with:
|
||||
name: test-platform-integration-core-coverage
|
||||
path: apps/vscode/integration-core-coverage-reports
|
||||
|
||||
- name: Upload core integration tests coverage to Qlty
|
||||
if: needs.detect-changes.outputs.testing_platform == 'true' && steps.download-integration-coverage.outcome == 'success'
|
||||
uses: qltysh/qlty-action/coverage@v2
|
||||
with:
|
||||
token: ${{ secrets.QLTY_COVERAGE_TOKEN }}
|
||||
files: apps/vscode/integration-core-coverage-reports/**/lcov.info
|
||||
tag: integration:core
|
||||
@@ -1,72 +0,0 @@
|
||||
name: "Publish New SDK Extension Nightly"
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 12 * * *' # 4 AM PST (UTC-8) = 12 UTC
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
checks: write
|
||||
pull-requests: write
|
||||
|
||||
env:
|
||||
# Keep the publish source pinned to one reviewed branch instead of accepting arbitrary refs.
|
||||
SDK_NIGHTLY_REF: dpc/sdk-migration-simpler-login
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
name: Publish Cline New SDK Extension Nightly
|
||||
if: github.repository == 'cline/cline' && github.ref == 'refs/heads/main'
|
||||
runs-on: ubuntu-latest
|
||||
environment: PublishNightly
|
||||
|
||||
steps:
|
||||
- name: Checkout trusted SDK nightly branch
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ env.SDK_NIGHTLY_REF }}
|
||||
lfs: true
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
# Keep publish environment aligned with test workflow/tooling lockfile expectations.
|
||||
# Newer LTS (Node 24 / npm 11) can make `npm list` fail with ELSPROBLEMS during vsce packaging.
|
||||
node-version: 22
|
||||
|
||||
- name: Install root dependencies
|
||||
run: npm ci --include=optional
|
||||
|
||||
- name: Install webview-ui dependencies
|
||||
run: cd webview-ui && npm ci --include=optional
|
||||
|
||||
- name: Install Publishing Tools
|
||||
run: npm install -g @vscode/vsce ovsx
|
||||
|
||||
- name: Verify LFS media assets are resolved
|
||||
run: |
|
||||
for FILE in webview-ui/src/assets/cline_kanban_demo.mp4 webview-ui/src/assets/cline_kanban_demo.webm; do
|
||||
if grep -q "git-lfs.github.com/spec/v1" "$FILE"; then
|
||||
echo "Error: $FILE is still a Git LFS pointer in CI checkout"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
- name: Publish SDK nightly extension
|
||||
env:
|
||||
VSCE_PAT: ${{ secrets.VSCE_PAT }}
|
||||
OVSX_PAT: ${{ secrets.OVSX_PAT }}
|
||||
TELEMETRY_SERVICE_API_KEY: ${{ secrets.TELEMETRY_SERVICE_API_KEY }}
|
||||
ERROR_SERVICE_API_KEY: ${{ secrets.ERROR_SERVICE_API_KEY }}
|
||||
CLINE_ENVIRONMENT: production
|
||||
# OpenTelemetry production defaults (can be overridden at runtime)
|
||||
OTEL_TELEMETRY_ENABLED: ${{ secrets.OTEL_TELEMETRY_ENABLED }}
|
||||
OTEL_LOGS_EXPORTER: otlp
|
||||
OTEL_METRICS_EXPORTER: otlp
|
||||
OTEL_EXPORTER_OTLP_PROTOCOL: ${{ secrets.OTEL_EXPORTER_OTLP_PROTOCOL }}
|
||||
OTEL_EXPORTER_OTLP_ENDPOINT: ${{ secrets.OTEL_EXPORTER_OTLP_ENDPOINT }}
|
||||
OTEL_EXPORTER_OTLP_HEADERS: ${{ secrets.OTEL_EXPORTER_OTLP_HEADERS }}
|
||||
run: npm run publish:marketplace:nightly
|
||||
@@ -1,4 +1,4 @@
|
||||
name: Auto-label Issues
|
||||
name: repo-label-issues
|
||||
|
||||
on:
|
||||
issues:
|
||||
@@ -15,7 +15,7 @@ jobs:
|
||||
script: |
|
||||
const body = context.payload.issue.body || '';
|
||||
const labels = context.payload.issue.labels.map(l => l.name);
|
||||
|
||||
|
||||
// Check if JetBrains Plugin is selected
|
||||
if (body.match(/###\s*Plugin Type\s*\n+JetBrains Plugin/i)) {
|
||||
if (!labels.includes('JetBrains')) {
|
||||
@@ -1,6 +1,6 @@
|
||||
# This workflow will only label and/or close 30 issues at a time in order to avoid exceeding a rate limit.
|
||||
# More info: https://docs.github.com/en/actions/use-cases-and-examples/project-management/closing-inactive-issues
|
||||
name: Close inactive issues
|
||||
name: repo-stale-issues
|
||||
on:
|
||||
schedule:
|
||||
- cron: "30 1 * * *"
|
||||
@@ -1,4 +1,4 @@
|
||||
name: Publish Main SDK Packages
|
||||
name: sdk-publish
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
@@ -26,7 +26,7 @@ on:
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: sdk
|
||||
working-directory: .
|
||||
|
||||
jobs:
|
||||
test:
|
||||
@@ -59,19 +59,23 @@ jobs:
|
||||
|
||||
- name: Determine publish channel
|
||||
id: channel
|
||||
env:
|
||||
EVENT_NAME: ${{ github.event_name }}
|
||||
INPUT_CHANNEL: ${{ inputs.channel }}
|
||||
run: |
|
||||
# Default to nightly for scheduled runs
|
||||
if [ "${{ github.event_name }}" = "schedule" ]; then
|
||||
if [ "$EVENT_NAME" = "schedule" ]; then
|
||||
echo "channel=nightly" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "channel=${{ inputs.channel }}" >> $GITHUB_OUTPUT
|
||||
echo "channel=$INPUT_CHANNEL" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Check for recent commits
|
||||
id: check_commits
|
||||
env:
|
||||
CHANNEL: ${{ steps.channel.outputs.channel }}
|
||||
FORCE_PUBLISH: ${{ inputs.force_publish }}
|
||||
run: |
|
||||
CHANNEL="${{ steps.channel.outputs.channel }}"
|
||||
|
||||
# Always publish for latest (production) releases
|
||||
if [ "$CHANNEL" = "latest" ]; then
|
||||
echo "Production release requested, proceeding with publish"
|
||||
@@ -79,7 +83,7 @@ jobs:
|
||||
exit 0
|
||||
fi
|
||||
|
||||
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
|
||||
@@ -141,9 +145,10 @@ jobs:
|
||||
- name: Generate shared version
|
||||
if: steps.check_commits.outputs.skip != 'true'
|
||||
id: version
|
||||
env:
|
||||
CHANNEL: ${{ steps.channel.outputs.channel }}
|
||||
run: |
|
||||
CHANNEL="${{ steps.channel.outputs.channel }}"
|
||||
BASE_VERSION=$(node -p "require('./packages/llms/package.json').version")
|
||||
BASE_VERSION=$(node -p "require('./sdk/packages/llms/package.json').version")
|
||||
|
||||
if [ "$CHANNEL" = "nightly" ]; then
|
||||
TIMESTAMP=$(date +%s)
|
||||
@@ -159,11 +164,13 @@ jobs:
|
||||
|
||||
- name: Update all package versions and lockfile
|
||||
if: steps.check_commits.outputs.skip != 'true'
|
||||
run: bun scripts/version.ts "${{ steps.version.outputs.version }}"
|
||||
env:
|
||||
VERSION: ${{ steps.version.outputs.version }}
|
||||
run: bun sdk/scripts/version.ts "$VERSION"
|
||||
|
||||
- name: Verify publishability
|
||||
if: steps.check_commits.outputs.skip != 'true'
|
||||
run: bun scripts/check-publish.ts
|
||||
run: bun sdk/scripts/check-publish.ts
|
||||
|
||||
- name: Prepare package tarball directory
|
||||
if: steps.check_commits.outputs.skip != 'true'
|
||||
@@ -176,10 +183,11 @@ jobs:
|
||||
if: steps.check_commits.outputs.skip != 'true'
|
||||
env:
|
||||
NPM_CONFIG_PROVENANCE: "true"
|
||||
CHANNEL: ${{ steps.channel.outputs.channel }}
|
||||
VERSION: ${{ steps.version.outputs.version }}
|
||||
run: |
|
||||
CHANNEL="${{ steps.channel.outputs.channel }}"
|
||||
echo "Publishing @cline/shared@${{ steps.version.outputs.version }} with tag '${CHANNEL}'..."
|
||||
cd packages/shared
|
||||
echo "Publishing @cline/shared@${VERSION} with tag '${CHANNEL}'..."
|
||||
cd sdk/packages/shared
|
||||
TARBALL=$(bun pm pack --destination "$RUNNER_TEMP/sdk-npm-packs" --quiet)
|
||||
npm publish "$RUNNER_TEMP/sdk-npm-packs/$(basename "$TARBALL")" --tag "$CHANNEL" --access public
|
||||
|
||||
@@ -187,10 +195,11 @@ jobs:
|
||||
if: steps.check_commits.outputs.skip != 'true'
|
||||
env:
|
||||
NPM_CONFIG_PROVENANCE: "true"
|
||||
CHANNEL: ${{ steps.channel.outputs.channel }}
|
||||
VERSION: ${{ steps.version.outputs.version }}
|
||||
run: |
|
||||
CHANNEL="${{ steps.channel.outputs.channel }}"
|
||||
echo "Publishing @cline/llms@${{ steps.version.outputs.version }} with tag '${CHANNEL}'..."
|
||||
cd packages/llms
|
||||
echo "Publishing @cline/llms@${VERSION} with tag '${CHANNEL}'..."
|
||||
cd sdk/packages/llms
|
||||
TARBALL=$(bun pm pack --destination "$RUNNER_TEMP/sdk-npm-packs" --quiet)
|
||||
npm publish "$RUNNER_TEMP/sdk-npm-packs/$(basename "$TARBALL")" --tag "$CHANNEL" --access public
|
||||
|
||||
@@ -198,10 +207,11 @@ jobs:
|
||||
if: steps.check_commits.outputs.skip != 'true'
|
||||
env:
|
||||
NPM_CONFIG_PROVENANCE: "true"
|
||||
CHANNEL: ${{ steps.channel.outputs.channel }}
|
||||
VERSION: ${{ steps.version.outputs.version }}
|
||||
run: |
|
||||
CHANNEL="${{ steps.channel.outputs.channel }}"
|
||||
echo "Publishing @cline/agents@${{ steps.version.outputs.version }} with tag '${CHANNEL}'..."
|
||||
cd packages/agents
|
||||
echo "Publishing @cline/agents@${VERSION} with tag '${CHANNEL}'..."
|
||||
cd sdk/packages/agents
|
||||
TARBALL=$(bun pm pack --destination "$RUNNER_TEMP/sdk-npm-packs" --quiet)
|
||||
npm publish "$RUNNER_TEMP/sdk-npm-packs/$(basename "$TARBALL")" --tag "$CHANNEL" --access public
|
||||
|
||||
@@ -209,10 +219,11 @@ jobs:
|
||||
if: steps.check_commits.outputs.skip != 'true'
|
||||
env:
|
||||
NPM_CONFIG_PROVENANCE: "true"
|
||||
CHANNEL: ${{ steps.channel.outputs.channel }}
|
||||
VERSION: ${{ steps.version.outputs.version }}
|
||||
run: |
|
||||
CHANNEL="${{ steps.channel.outputs.channel }}"
|
||||
echo "Publishing @cline/core@${{ steps.version.outputs.version }} with tag '${CHANNEL}'..."
|
||||
cd packages/core
|
||||
echo "Publishing @cline/core@${VERSION} with tag '${CHANNEL}'..."
|
||||
cd sdk/packages/core
|
||||
TARBALL=$(bun pm pack --destination "$RUNNER_TEMP/sdk-npm-packs" --quiet)
|
||||
npm publish "$RUNNER_TEMP/sdk-npm-packs/$(basename "$TARBALL")" --tag "$CHANNEL" --access public
|
||||
|
||||
@@ -220,18 +231,19 @@ jobs:
|
||||
if: steps.check_commits.outputs.skip != 'true'
|
||||
env:
|
||||
NPM_CONFIG_PROVENANCE: "true"
|
||||
CHANNEL: ${{ steps.channel.outputs.channel }}
|
||||
VERSION: ${{ steps.version.outputs.version }}
|
||||
run: |
|
||||
CHANNEL="${{ steps.channel.outputs.channel }}"
|
||||
echo "Publishing @cline/sdk@${{ steps.version.outputs.version }} with tag '${CHANNEL}'..."
|
||||
cd packages/sdk
|
||||
echo "Publishing @cline/sdk@${VERSION} with tag '${CHANNEL}'..."
|
||||
cd sdk/packages/sdk
|
||||
TARBALL=$(bun pm pack --destination "$RUNNER_TEMP/sdk-npm-packs" --quiet)
|
||||
npm publish "$RUNNER_TEMP/sdk-npm-packs/$(basename "$TARBALL")" --tag "$CHANNEL" --access public
|
||||
|
||||
- name: Create package tags for production publish
|
||||
if: steps.check_commits.outputs.skip != 'true' && steps.channel.outputs.channel == 'latest'
|
||||
env:
|
||||
VERSION: ${{ steps.version.outputs.version }}
|
||||
run: |
|
||||
VERSION="${{ steps.version.outputs.version }}"
|
||||
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||
|
||||
@@ -250,9 +262,10 @@ jobs:
|
||||
|
||||
- name: Summary
|
||||
if: steps.check_commits.outputs.skip != 'true'
|
||||
env:
|
||||
VERSION: ${{ steps.version.outputs.version }}
|
||||
CHANNEL: ${{ steps.channel.outputs.channel }}
|
||||
run: |
|
||||
VERSION="${{ steps.version.outputs.version }}"
|
||||
CHANNEL="${{ steps.channel.outputs.channel }}"
|
||||
echo "Published SDK packages with tag '${CHANNEL}':"
|
||||
echo " - @cline/shared@${VERSION}"
|
||||
echo " - @cline/llms@${VERSION}"
|
||||
@@ -1,4 +1,4 @@
|
||||
name: SDK Tests
|
||||
name: sdk-test
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -21,7 +21,7 @@ permissions:
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: sdk
|
||||
working-directory: .
|
||||
|
||||
jobs:
|
||||
quality-checks:
|
||||
@@ -96,12 +96,12 @@ jobs:
|
||||
|
||||
- name: Run SDK Tests (Windows)
|
||||
if: ${{ !cancelled() && steps.build_sdk_step.outcome == 'success' && steps.build_cli_step.outcome == 'success' && matrix.os == 'windows-latest' }}
|
||||
run: bun -F './packages/**' test
|
||||
run: bun -F './sdk/packages/**' test
|
||||
|
||||
- name: Smoke test SQLite under Node
|
||||
if: ${{ !cancelled() && steps.build_sdk_step.outcome == 'success' && matrix.os != 'windows-latest' }}
|
||||
timeout-minutes: 10
|
||||
run: bun scripts/ci-node-smoke.ts
|
||||
run: bun sdk/scripts/ci-node-smoke.ts
|
||||
|
||||
- name: Run TUI e2e tests
|
||||
if: ${{ !cancelled() && steps.build_sdk_step.outcome == 'success' && steps.build_cli_step.outcome == 'success' && matrix.os == 'ubuntu-latest' && matrix.node-version == '24.x' }}
|
||||
@@ -109,4 +109,4 @@ jobs:
|
||||
|
||||
- name: Verify packages are publishable
|
||||
if: ${{ !cancelled() && steps.build_sdk_step.outcome == 'success' && steps.build_cli_step.outcome == 'success' && matrix.os == 'ubuntu-latest' && matrix.node-version == '24.x' }}
|
||||
run: bun scripts/check-publish.ts
|
||||
run: bun sdk/scripts/check-publish.ts
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
name: Test Stale Issues Workflow
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
days-before-stale:
|
||||
description: "Days before an issue becomes stale"
|
||||
required: true
|
||||
default: "1"
|
||||
days-before-close:
|
||||
description: "Days before a stale issue is closed"
|
||||
required: true
|
||||
default: "1"
|
||||
|
||||
jobs:
|
||||
test-stale:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- uses: actions/stale@28ca103
|
||||
with:
|
||||
days-before-issue-stale: ${{ github.event.inputs.days-before-stale }}
|
||||
days-before-issue-close: ${{ github.event.inputs.days-before-close }}
|
||||
stale-issue-label: "stale"
|
||||
stale-issue-message: "This issue is stale because it has been open for ${{ github.event.inputs.days-before-stale }} days with no activity."
|
||||
close-issue-message: "This issue was closed because it has been inactive for ${{ github.event.inputs.days-before-close }} days since being marked as stale."
|
||||
days-before-pr-stale: -1
|
||||
days-before-pr-close: -1
|
||||
exempt-issue-labels: "pinned,security"
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
debug-only: true
|
||||
@@ -1,245 +0,0 @@
|
||||
name: Tests
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
workflow_call:
|
||||
|
||||
# Set default permissions for all jobs
|
||||
permissions:
|
||||
contents: read # Needed to check out code
|
||||
|
||||
jobs:
|
||||
quality-checks:
|
||||
runs-on: ubuntu-latest
|
||||
name: Quality Checks
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node.js environment
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22
|
||||
cache: 'npm'
|
||||
cache-dependency-path: |
|
||||
package-lock.json
|
||||
webview-ui/package-lock.json
|
||||
|
||||
- name: Install root dependencies
|
||||
run: npm ci
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Install webview-ui dependencies
|
||||
run: cd webview-ui && npm ci
|
||||
|
||||
- name: Run Quality Checks (Parallel)
|
||||
run: npm run ci:check-all
|
||||
|
||||
test:
|
||||
needs: quality-checks
|
||||
env:
|
||||
VSCODE_TEST_VERSION: 1.103.0
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
name: ${{ matrix.os == 'ubuntu-latest' && 'test' || format('test ({0})', matrix.os) }}
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node.js environment
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22
|
||||
cache: 'npm'
|
||||
cache-dependency-path: |
|
||||
package-lock.json
|
||||
webview-ui/package-lock.json
|
||||
|
||||
- name: Install root dependencies
|
||||
run: npm ci
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Install webview-ui dependencies
|
||||
run: cd webview-ui && npm ci
|
||||
|
||||
- name: Set up NPM on Windows
|
||||
if: runner.os == 'Windows'
|
||||
run: |
|
||||
npm config set script-shell "C:\\Program Files\\Git\\bin\\bash.exe"
|
||||
|
||||
- name: Cache VS Code test runtime
|
||||
if: runner.os == 'Windows'
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: .vscode-test
|
||||
key: vscode-test-runtime-${{ runner.os }}-${{ env.VSCODE_TEST_VERSION }}
|
||||
|
||||
# Build the extension and tests (without redundant checks)
|
||||
- name: Build Tests and Extension
|
||||
id: build_step
|
||||
run: npm run ci:build
|
||||
|
||||
- name: Unit Tests with coverage - Linux
|
||||
id: unit_tests_linux
|
||||
if: ${{ !cancelled() && steps.build_step.outcome == 'success' && runner.os == 'Linux' }}
|
||||
run: |
|
||||
npx nyc --nycrc-path .nycrc.unit.json --reporter=lcov npm run test:unit
|
||||
|
||||
- name: Unit Tests - Non-Linux
|
||||
id: unit_tests_non_linux
|
||||
if: ${{ !cancelled() && steps.build_step.outcome == 'success' && runner.os != 'Linux' }}
|
||||
run: |
|
||||
npm run test:unit
|
||||
|
||||
- name: Extension Integration Tests - Linux
|
||||
id: integration_tests_linux
|
||||
if: ${{ !cancelled() && steps.build_step.outcome == 'success' && runner.os == 'Linux' }}
|
||||
run: xvfb-run -a npm run test:coverage
|
||||
|
||||
- name: Extension Integration Tests - Non-Linux
|
||||
id: integration_tests_non_linux
|
||||
if: ${{ !cancelled() && steps.build_step.outcome == 'success' && runner.os != 'Linux' }}
|
||||
run: |
|
||||
for attempt in 1 2 3; do
|
||||
echo "Running extension integration tests (attempt ${attempt}/3)"
|
||||
if npm run test:integration; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ "$attempt" -eq 3 ]; then
|
||||
echo "Extension integration tests failed after 3 attempts"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Extension integration tests failed; retrying after short delay"
|
||||
sleep 5
|
||||
done
|
||||
|
||||
- name: Webview Tests with Coverage
|
||||
id: webview_tests
|
||||
if: ${{ !cancelled() && steps.build_step.outcome == 'success' }}
|
||||
run: |
|
||||
cd webview-ui
|
||||
npm run test:coverage
|
||||
|
||||
- name: CLI Tests
|
||||
id: cli_tests
|
||||
if: ${{ !cancelled() && steps.build_step.outcome == 'success' }}
|
||||
run: cd cli && npm run test:run
|
||||
|
||||
- name: Save Coverage Reports
|
||||
uses: actions/upload-artifact@v4
|
||||
# Only upload artifacts on Linux - We only need coverage from one OS
|
||||
if: runner.os == 'Linux'
|
||||
with:
|
||||
name: pr-coverage-reports
|
||||
path: |
|
||||
coverage-unit/lcov.info
|
||||
webview-ui/coverage/lcov.info
|
||||
|
||||
test-platform-integration:
|
||||
needs: quality-checks
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node.js environment
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22
|
||||
cache: 'npm'
|
||||
cache-dependency-path: |
|
||||
package-lock.json
|
||||
webview-ui/package-lock.json
|
||||
testing-platform/package-lock.json
|
||||
|
||||
- name: Install root dependencies
|
||||
run: npm ci
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Install webview-ui dependencies
|
||||
run: cd webview-ui && npm ci
|
||||
|
||||
- name: Download ripgrep binaries
|
||||
run: npm run download-ripgrep
|
||||
|
||||
- name: Compile Standalone
|
||||
run: npm run compile-standalone
|
||||
|
||||
- name: Install testing platform dependencies
|
||||
run: cd testing-platform && npm ci
|
||||
|
||||
- name: Running testing platform integration spec tests
|
||||
timeout-minutes: 7
|
||||
run: npm run test:tp-orchestrator -- tests/specs/ --count=1 --coverage
|
||||
|
||||
- name: Save Coverage Reports
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: test-platform-integration-core-coverage
|
||||
path: coverage/**/lcov.info
|
||||
|
||||
qlty:
|
||||
needs: [test, test-platform-integration]
|
||||
runs-on: ubuntu-latest
|
||||
# Run on PRs to main, pushes to main, and manual dispatches
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Download unit tests coverage reports
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: pr-coverage-reports
|
||||
path: .
|
||||
|
||||
- name: Upload core unit tests coverage to Qlty
|
||||
uses: qltysh/qlty-action/coverage@v2
|
||||
with:
|
||||
token: ${{ secrets.QLTY_COVERAGE_TOKEN }}
|
||||
# we can merge multiple files if necessary
|
||||
files: |
|
||||
coverage-unit/lcov.info
|
||||
tag: unit:core
|
||||
|
||||
- name: Upload webview-ui unit tests coverage to Qlty
|
||||
uses: qltysh/qlty-action/coverage@v2
|
||||
with:
|
||||
token: ${{ secrets.QLTY_COVERAGE_TOKEN }}
|
||||
# we can merge multiple files if necessary
|
||||
files: |
|
||||
webview-ui/coverage/lcov.info
|
||||
tag: unit:webview-ui
|
||||
add-prefix: webview-ui/
|
||||
|
||||
- name: Download test platform integration core coverage artifact
|
||||
uses: actions/download-artifact@v4
|
||||
continue-on-error: true
|
||||
id: download-integration-coverage
|
||||
with:
|
||||
name: test-platform-integration-core-coverage
|
||||
path: integration-core-coverage-reports
|
||||
|
||||
- name: Upload core integration tests coverage to Qlty
|
||||
if: steps.download-integration-coverage.outcome == 'success'
|
||||
uses: qltysh/qlty-action/coverage@v2
|
||||
with:
|
||||
token: ${{ secrets.QLTY_COVERAGE_TOKEN }}
|
||||
files: integration-core-coverage-reports/**/lcov.info
|
||||
tag: integration:core
|
||||
@@ -17,8 +17,8 @@ pnpm-lock.yaml
|
||||
.actrc
|
||||
CLAUDE.local.md
|
||||
|
||||
webview-ui/src/**/*.js
|
||||
webview-ui/src/**/*.js.map
|
||||
apps/vscode/webview-ui/src/**/*.js
|
||||
apps/vscode/webview-ui/src/**/*.js.map
|
||||
|
||||
# Ignore coverage directories and files
|
||||
coverage
|
||||
@@ -35,9 +35,9 @@ coverage-unit
|
||||
.worktrees
|
||||
|
||||
## Generated files ##
|
||||
src/generated/
|
||||
src/shared/proto/
|
||||
webview-ui/src/services/grpc-client.ts
|
||||
apps/vscode/src/generated/
|
||||
apps/vscode/src/shared/proto/
|
||||
apps/vscode/webview-ui/src/services/grpc-client.ts
|
||||
*.tsbuildinfo
|
||||
|
||||
# E2E Tests
|
||||
@@ -60,3 +60,13 @@ tests/**/cache
|
||||
# Backup created by scripts/marketplace-readme.mjs while publishing.
|
||||
# Should never be committed: only exists if a publish aborts mid-swap.
|
||||
.README.github.bak
|
||||
|
||||
|
||||
# SDK Session files / User data
|
||||
.cline/data
|
||||
.cline/tmp
|
||||
*.db
|
||||
*.db-shm
|
||||
*.db-wal
|
||||
.cline/**/managed.json
|
||||
.cline/**/bundle.json
|
||||
|
||||
@@ -1 +1,10 @@
|
||||
if ! command -v gitleaks >/dev/null 2>&1; then
|
||||
echo "gitleaks is required for the pre-commit secret scan."
|
||||
echo "Install it with: brew install gitleaks"
|
||||
echo "Other install options: https://github.com/gitleaks/gitleaks#installing"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
gitleaks git --pre-commit --redact --staged --verbose || exit 1
|
||||
|
||||
lint-staged
|
||||
@@ -1,2 +1,3 @@
|
||||
node 22
|
||||
|
||||
bun 1.3.13
|
||||
node 22
|
||||
@@ -5,6 +5,7 @@
|
||||
"connor4312.esbuild-problem-matchers",
|
||||
"ms-vscode.extension-test-runner",
|
||||
"bradlc.vscode-tailwindcss",
|
||||
"biomejs.biome"
|
||||
"biomejs.biome",
|
||||
"oven.bun-vscode"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -10,23 +10,23 @@
|
||||
"type": "extensionHost",
|
||||
"request": "launch",
|
||||
"args": [
|
||||
"--extensionDevelopmentPath=${workspaceFolder}",
|
||||
"--extensionDevelopmentPath=${workspaceFolder}/apps/vscode",
|
||||
"--disable-workspace-trust",
|
||||
"--disable-extension",
|
||||
"saoudrizwan.claude-dev", // Avoid conflicts with installed Cline
|
||||
"--disable-extension",
|
||||
"saoudrizwan.cline-nightly", // Avoid conflicts with installed Cline Nightly
|
||||
"${workspaceFolder}",
|
||||
"${workspaceFolder}/apps/vscode",
|
||||
"--disable-extensions"
|
||||
],
|
||||
"outFiles": [
|
||||
"${workspaceFolder}/dist/**/*.js"
|
||||
"${workspaceFolder}/apps/vscode/dist/**/*.js"
|
||||
],
|
||||
"preLaunchTask": "${defaultBuildTask}",
|
||||
"envFile": "${workspaceFolder}/.env",
|
||||
"envFile": "${workspaceFolder}/apps/vscode/.env",
|
||||
"env": {
|
||||
"IS_DEV": "true",
|
||||
"DEV_WORKSPACE_FOLDER": "${workspaceFolder}",
|
||||
"DEV_WORKSPACE_FOLDER": "${workspaceFolder}/apps/vscode",
|
||||
"CLINE_ENVIRONMENT": "production"
|
||||
}
|
||||
},
|
||||
@@ -35,22 +35,22 @@
|
||||
"type": "extensionHost",
|
||||
"request": "launch",
|
||||
"args": [
|
||||
"--extensionDevelopmentPath=${workspaceFolder}",
|
||||
"--extensionDevelopmentPath=${workspaceFolder}/apps/vscode",
|
||||
"--disable-workspace-trust",
|
||||
"--disable-extension",
|
||||
"saoudrizwan.claude-dev", // Avoid conflicts with installed Cline
|
||||
"--disable-extension",
|
||||
"saoudrizwan.cline-nightly", // Avoid conflicts with installed Cline Nightly
|
||||
"${workspaceFolder}"
|
||||
"${workspaceFolder}/apps/vscode"
|
||||
],
|
||||
"outFiles": [
|
||||
"${workspaceFolder}/dist/**/*.js"
|
||||
"${workspaceFolder}/apps/vscode/dist/**/*.js"
|
||||
],
|
||||
"preLaunchTask": "${defaultBuildTask}",
|
||||
"envFile": "${workspaceFolder}/.env",
|
||||
"envFile": "${workspaceFolder}/apps/vscode/.env",
|
||||
"env": {
|
||||
"IS_DEV": "true",
|
||||
"DEV_WORKSPACE_FOLDER": "${workspaceFolder}",
|
||||
"DEV_WORKSPACE_FOLDER": "${workspaceFolder}/apps/vscode",
|
||||
"CLINE_ENVIRONMENT": "staging"
|
||||
}
|
||||
},
|
||||
@@ -59,22 +59,22 @@
|
||||
"type": "extensionHost",
|
||||
"request": "launch",
|
||||
"args": [
|
||||
"--extensionDevelopmentPath=${workspaceFolder}",
|
||||
"--extensionDevelopmentPath=${workspaceFolder}/apps/vscode",
|
||||
"--disable-workspace-trust",
|
||||
"--disable-extension",
|
||||
"saoudrizwan.claude-dev", // Avoid conflicts with installed Cline
|
||||
"--disable-extension",
|
||||
"saoudrizwan.cline-nightly", // Avoid conflicts with installed Cline Nightly
|
||||
"${workspaceFolder}"
|
||||
"${workspaceFolder}/apps/vscode"
|
||||
],
|
||||
"outFiles": [
|
||||
"${workspaceFolder}/dist/**/*.js"
|
||||
"${workspaceFolder}/apps/vscode/dist/**/*.js"
|
||||
],
|
||||
"preLaunchTask": "${defaultBuildTask}",
|
||||
"envFile": "${workspaceFolder}/.env",
|
||||
"envFile": "${workspaceFolder}/apps/vscode/.env",
|
||||
"env": {
|
||||
"IS_DEV": "true",
|
||||
"DEV_WORKSPACE_FOLDER": "${workspaceFolder}",
|
||||
"DEV_WORKSPACE_FOLDER": "${workspaceFolder}/apps/vscode",
|
||||
"CLINE_ENVIRONMENT": "local"
|
||||
}
|
||||
},
|
||||
@@ -84,27 +84,27 @@
|
||||
"request": "launch",
|
||||
"runtimeExecutable": "${execPath}",
|
||||
"args": [
|
||||
"--user-data-dir=${workspaceFolder}/dist/tmp/user",
|
||||
"--user-data-dir=${workspaceFolder}/apps/vscode/dist/tmp/user",
|
||||
"--profile-temp",
|
||||
"--sync=off",
|
||||
"--disable-extension",
|
||||
"saoudrizwan.claude-dev", // Avoid conflicts with installed Cline
|
||||
"--disable-extension",
|
||||
"saoudrizwan.cline-nightly", // Avoid conflicts with installed Cline Nightly
|
||||
"--extensionDevelopmentPath=${workspaceFolder}",
|
||||
"${workspaceFolder}"
|
||||
"--extensionDevelopmentPath=${workspaceFolder}/apps/vscode",
|
||||
"${workspaceFolder}/apps/vscode"
|
||||
],
|
||||
"outFiles": [
|
||||
"${workspaceFolder}/dist/**/*.js"
|
||||
"${workspaceFolder}/apps/vscode/dist/**/*.js"
|
||||
],
|
||||
"preLaunchTask": "clean-tmp-user",
|
||||
"internalConsoleOptions": "openOnSessionStart",
|
||||
"postDebugTask": "stop",
|
||||
"envFile": "${workspaceFolder}/.env",
|
||||
"envFile": "${workspaceFolder}/apps/vscode/.env",
|
||||
"env": {
|
||||
"IS_DEV": "true",
|
||||
"TEMP_PROFILE": "true",
|
||||
"DEV_WORKSPACE_FOLDER": "${workspaceFolder}",
|
||||
"DEV_WORKSPACE_FOLDER": "${workspaceFolder}/apps/vscode",
|
||||
"CLINE_ENVIRONMENT": "production"
|
||||
}
|
||||
},
|
||||
@@ -117,13 +117,13 @@
|
||||
],
|
||||
"sourceMaps": true,
|
||||
"resolveSourceMapLocations": [
|
||||
"${workspaceFolder}/**",
|
||||
"${workspaceFolder}/apps/vscode/**",
|
||||
"!**/node_modules/**"
|
||||
],
|
||||
"cwd": "${workspaceFolder}",
|
||||
"cwd": "${workspaceFolder}/apps/vscode",
|
||||
"outFiles": [
|
||||
"${workspaceFolder}/dist/**/*.js",
|
||||
"${workspaceFolder}/dist-standalone/**/*.js"
|
||||
"${workspaceFolder}/apps/vscode/dist/**/*.js",
|
||||
"${workspaceFolder}/apps/vscode/dist-standalone/**/*.js"
|
||||
],
|
||||
"preLaunchTask": "compile-standalone",
|
||||
"runtimeExecutable": "npx",
|
||||
@@ -131,11 +131,11 @@
|
||||
"tsx"
|
||||
],
|
||||
"program": "scripts/test-standalone-core-api-server.ts",
|
||||
"envFile": "${workspaceFolder}/.env",
|
||||
"envFile": "${workspaceFolder}/apps/vscode/.env",
|
||||
"env": {
|
||||
"PROTOBUS_PORT": "26040",
|
||||
"HOSTBRIDGE_PORT": "26041",
|
||||
"WORKSPACE_DIR": "${workspaceFolder}",
|
||||
"WORKSPACE_DIR": "${workspaceFolder}/apps/vscode",
|
||||
"E2E_TEST": "true",
|
||||
"CLINE_ENVIRONMENT": "local"
|
||||
},
|
||||
@@ -151,10 +151,10 @@
|
||||
],
|
||||
"sourceMaps": true,
|
||||
"resolveSourceMapLocations": [
|
||||
"${workspaceFolder}/**",
|
||||
"${workspaceFolder}/apps/vscode/**",
|
||||
"!**/node_modules/**"
|
||||
],
|
||||
"cwd": "${workspaceFolder}",
|
||||
"cwd": "${workspaceFolder}/apps/vscode",
|
||||
"runtimeExecutable": "npx",
|
||||
"runtimeArgs": [
|
||||
"mocha"
|
||||
@@ -169,7 +169,7 @@
|
||||
"--exit",
|
||||
"${file}"
|
||||
],
|
||||
"envFile": "${workspaceFolder}/.env",
|
||||
"envFile": "${workspaceFolder}/apps/vscode/.env",
|
||||
"env": {
|
||||
"TS_NODE_PROJECT": "./tsconfig.unit-test.json",
|
||||
"NODE_ENV": "test",
|
||||
@@ -188,7 +188,7 @@
|
||||
"run",
|
||||
"storybook"
|
||||
],
|
||||
"cwd": "${workspaceFolder}/webview-ui",
|
||||
"cwd": "${workspaceFolder}/apps/vscode/webview-ui",
|
||||
"console": "integratedTerminal",
|
||||
"internalConsoleOptions": "neverOpen",
|
||||
"serverReadyAction": {
|
||||
@@ -199,6 +199,119 @@
|
||||
"env": {
|
||||
"IS_DEV": "true"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Launch Bun CLI (Prompt)",
|
||||
"type": "bun",
|
||||
"request": "launch",
|
||||
"cwd": "${workspaceFolder}/sdk/apps/cli",
|
||||
"runtime": "bun",
|
||||
"runtimeArgs": [
|
||||
"--conditions=development"
|
||||
],
|
||||
"program": "${workspaceFolder}/sdk/apps/cli/src/index.ts",
|
||||
"args": [
|
||||
"${input:cliPrompt}"
|
||||
],
|
||||
"env": {
|
||||
"CLINE_BUILD_ENV": "development"
|
||||
},
|
||||
"presentation": {
|
||||
"hidden": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Launch RPC Server",
|
||||
"type": "bun",
|
||||
"request": "launch",
|
||||
"cwd": "${workspaceFolder}/sdk/apps/cli",
|
||||
"runtime": "bun",
|
||||
"runtimeArgs": [
|
||||
"--conditions=development"
|
||||
],
|
||||
"program": "${workspaceFolder}/sdk/apps/cli/src/index.ts",
|
||||
"args": [
|
||||
"rpc",
|
||||
"start"
|
||||
],
|
||||
"env": {
|
||||
"CLINE_BUILD_ENV": "development",
|
||||
"CLINE_DEBUG_PORT_BASE": "9230"
|
||||
},
|
||||
"presentation": {
|
||||
"hidden": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Attach RPC Runtime (9230)",
|
||||
"type": "bun",
|
||||
"request": "attach",
|
||||
"url": "ws://127.0.0.1:9230",
|
||||
"localRoot": "${workspaceFolder}/sdk",
|
||||
"remoteRoot": "${workspaceFolder}/sdk",
|
||||
"presentation": {
|
||||
"hidden": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Attach Hook Worker (9231)",
|
||||
"type": "bun",
|
||||
"request": "attach",
|
||||
"url": "ws://127.0.0.1:9231",
|
||||
"localRoot": "${workspaceFolder}/sdk",
|
||||
"remoteRoot": "${workspaceFolder}/sdk",
|
||||
"presentation": {
|
||||
"hidden": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Attach Plugin Sandbox (9232)",
|
||||
"type": "bun",
|
||||
"request": "attach",
|
||||
"url": "ws://127.0.0.1:9232",
|
||||
"localRoot": "${workspaceFolder}/sdk",
|
||||
"remoteRoot": "${workspaceFolder}/sdk",
|
||||
"presentation": {
|
||||
"hidden": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Attach Connector Child (9233)",
|
||||
"type": "bun",
|
||||
"request": "attach",
|
||||
"url": "ws://127.0.0.1:9233",
|
||||
"localRoot": "${workspaceFolder}/sdk",
|
||||
"remoteRoot": "${workspaceFolder}/sdk",
|
||||
"presentation": {
|
||||
"hidden": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"compounds": [
|
||||
{
|
||||
"name": "Launch RPC Server Debugger",
|
||||
"configurations": [
|
||||
"Launch RPC Server",
|
||||
"Attach RPC Runtime (9230)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Launch CLI Debugger",
|
||||
"configurations": [
|
||||
"Launch Bun CLI (Prompt)",
|
||||
"Attach RPC Runtime (9230)",
|
||||
"Attach Hook Worker (9231)",
|
||||
"Attach Plugin Sandbox (9232)",
|
||||
"Attach Connector Child (9233)"
|
||||
]
|
||||
}
|
||||
],
|
||||
"inputs": [
|
||||
{
|
||||
"id": "cliPrompt",
|
||||
"type": "promptString",
|
||||
"description": "Prompt to send to the CLI",
|
||||
"default": "hey"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
// Place your settings in this file to overwrite default and user settings.
|
||||
{
|
||||
"files.insertFinalNewline": true,
|
||||
"files.exclude": {
|
||||
"out": false, // set this to true to hide the "out" folder with the compiled JS files
|
||||
"dist": false // set this to true to hide the "dist" folder with the compiled JS files
|
||||
@@ -16,7 +17,7 @@
|
||||
// Protobuf settings
|
||||
"protoc": {
|
||||
"options": [
|
||||
"--proto_path=proto"
|
||||
"--proto_path=apps/vscode/proto"
|
||||
]
|
||||
},
|
||||
// Enable Lint and format using Biome
|
||||
|
||||
@@ -5,24 +5,28 @@
|
||||
"tasks": [
|
||||
{
|
||||
"label": "compile-standalone",
|
||||
"type": "npm",
|
||||
"script": "compile-standalone",
|
||||
"type": "shell",
|
||||
"command": "npm run compile-standalone",
|
||||
"group": "build",
|
||||
"problemMatcher": [],
|
||||
"presentation": {
|
||||
"reveal": "always"
|
||||
},
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/apps/vscode"
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "npm: protos",
|
||||
"type": "npm",
|
||||
"script": "protos",
|
||||
"type": "shell",
|
||||
"command": "npm run protos",
|
||||
"problemMatcher": [],
|
||||
"isBackground": false,
|
||||
"presentation": {
|
||||
"reveal": "always"
|
||||
},
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/apps/vscode",
|
||||
"env": {
|
||||
"IS_DEV": "true"
|
||||
}
|
||||
@@ -60,8 +64,8 @@
|
||||
"group": "build"
|
||||
},
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "build:webview",
|
||||
"type": "shell",
|
||||
"command": "npm run build:webview",
|
||||
"group": "build",
|
||||
"problemMatcher": [],
|
||||
"isBackground": true,
|
||||
@@ -74,14 +78,15 @@
|
||||
"reveal": "always"
|
||||
},
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/apps/vscode",
|
||||
"env": {
|
||||
"IS_DEV": "true"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "build:webview:test",
|
||||
"type": "shell",
|
||||
"command": "npm run build:webview:test",
|
||||
"group": "build",
|
||||
"problemMatcher": [],
|
||||
"isBackground": true,
|
||||
@@ -94,6 +99,7 @@
|
||||
"reveal": "always"
|
||||
},
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/apps/vscode",
|
||||
"env": {
|
||||
"IS_DEV": "true",
|
||||
"IS_TEST": "true"
|
||||
@@ -101,8 +107,8 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "dev:webview",
|
||||
"type": "shell",
|
||||
"command": "npm run dev:webview",
|
||||
"group": "build",
|
||||
"problemMatcher": [
|
||||
{
|
||||
@@ -131,14 +137,15 @@
|
||||
"reveal": "always"
|
||||
},
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/apps/vscode",
|
||||
"env": {
|
||||
"IS_DEV": "true"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "watch:esbuild",
|
||||
"type": "shell",
|
||||
"command": "npm run watch:esbuild",
|
||||
"group": "build",
|
||||
"problemMatcher": {
|
||||
"pattern": [
|
||||
@@ -169,14 +176,15 @@
|
||||
"reveal": "always"
|
||||
},
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/apps/vscode",
|
||||
"env": {
|
||||
"IS_DEV": "true"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "watch:esbuild:test",
|
||||
"type": "shell",
|
||||
"command": "npm run watch:esbuild:test",
|
||||
"group": "build",
|
||||
"problemMatcher": {
|
||||
"pattern": [
|
||||
@@ -207,6 +215,7 @@
|
||||
"reveal": "always"
|
||||
},
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/apps/vscode",
|
||||
"env": {
|
||||
"IS_DEV": "true",
|
||||
"IS_TEST": "true"
|
||||
@@ -214,8 +223,8 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "watch:tsc",
|
||||
"type": "shell",
|
||||
"command": "npm run watch:tsc",
|
||||
"group": "build",
|
||||
"problemMatcher": "$tsc-watch",
|
||||
"isBackground": true,
|
||||
@@ -226,11 +235,15 @@
|
||||
"presentation": {
|
||||
"group": "watch",
|
||||
"reveal": "always"
|
||||
},
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/apps/vscode"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "watch-tests",
|
||||
"type": "shell",
|
||||
"command": "npm run watch-tests",
|
||||
"label": "npm: watch-tests",
|
||||
"problemMatcher": "$tsc-watch",
|
||||
"isBackground": true,
|
||||
"dependsOn": [
|
||||
@@ -240,7 +253,10 @@
|
||||
"reveal": "always",
|
||||
"group": "watchers"
|
||||
},
|
||||
"group": "build"
|
||||
"group": "build",
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/apps/vscode"
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "tasks: watch-tests",
|
||||
@@ -262,11 +278,11 @@
|
||||
"dependsOn": [
|
||||
"watch"
|
||||
],
|
||||
"command": "rm -rf ${workspaceFolder}/dist/tmp/user && mkdir -p ${workspaceFolder}/dist/tmp/user"
|
||||
"command": "rm -rf ${workspaceFolder}/apps/vscode/dist/tmp/user && mkdir -p ${workspaceFolder}/apps/vscode/dist/tmp/user"
|
||||
},
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "storybook",
|
||||
"type": "shell",
|
||||
"command": "npm run storybook",
|
||||
"group": "build",
|
||||
"problemMatcher": [],
|
||||
"isBackground": false,
|
||||
@@ -279,10 +295,22 @@
|
||||
"reveal": "always"
|
||||
},
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/apps/vscode",
|
||||
"env": {
|
||||
"IS_DEV": "true"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "build-sdk",
|
||||
"type": "shell",
|
||||
"command": "bun run build:sdk",
|
||||
"problemMatcher": [
|
||||
"$tsc"
|
||||
],
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}"
|
||||
}
|
||||
}
|
||||
],
|
||||
"inputs": [
|
||||
|
||||
@@ -1,5 +1,81 @@
|
||||
# Changelog
|
||||
|
||||
## [3.86.2]
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix `@` file mentions and workspace file search on VS Code 1.122+ by resolving the new bundled `@vscode/ripgrep-universal` per-platform binary layout before falling back to legacy ripgrep paths.
|
||||
|
||||
## [3.86.1]
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix `@` file mentions failing to find files in some environments (notably VS Code Remote SSH, and after certain VS Code updates) by keeping the file-search fallback alive when the workspace index or bundled ripgrep binary is unavailable.
|
||||
|
||||
## [3.86.0]
|
||||
|
||||
### Added
|
||||
|
||||
- Add Claude Opus 4.8 provider support, including 1M-context variants where available.
|
||||
- Add Moonshot Kimi K2.6 model support.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Show prompt-cache support for Qwen 3.7 Max in the Cline provider.
|
||||
- Fix the VS Code nightly publish workflow startup permissions.
|
||||
|
||||
### Changed
|
||||
|
||||
- Move the VS Code extension project into `apps/vscode`.
|
||||
|
||||
## [3.85.0]
|
||||
|
||||
### Added
|
||||
|
||||
- Add GPT-5.5 support to SAP AI Core.
|
||||
- Add DeepSeek V4 Flash and Pro models.
|
||||
- Add Gemini 3.5 Flash to Gemini and Vertex providers.
|
||||
- Add `/lg-task` URI webhook integration for LG dashboard flows.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix Vertex AI global endpoint handling for Claude models.
|
||||
- Route Poolside Laguna models through next-gen prompts and native tool calling.
|
||||
|
||||
### Changed
|
||||
|
||||
- Update `diff` and `protobufjs` dependencies.
|
||||
|
||||
## [3.84.0]
|
||||
|
||||
### Added
|
||||
|
||||
- Add SAP AI Core support for additional hosted models
|
||||
|
||||
### Fixed
|
||||
|
||||
- Disable the MCP "Restart Server" button when a server is toggled off.
|
||||
|
||||
### Changed
|
||||
|
||||
- Remove the Cline Kanban launch modal and bundled demo media from the VS Code extension startup flow.
|
||||
|
||||
## [3.83.0]
|
||||
|
||||
### Fixed
|
||||
|
||||
- Show a clear "Searching..." state in the @-mention file picker
|
||||
- Improve @-mention file search performance
|
||||
- Allow `write_to_file` to create or overwrite files with empty content.
|
||||
- Fix validation failures for MCP servers that require an object.
|
||||
- Enable OpenRouter prompt cache control for Qwen models.
|
||||
- Update Axios and SAP Connectivity dependencies
|
||||
|
||||
### Changed
|
||||
|
||||
- Use the VS Code-specific `README.marketplace.md` when packaging and publishing the VS Code extension
|
||||
- Add telemetry to @-mention search to help diagnose local, remote, and multi-root workspace search behavior.
|
||||
|
||||
## [3.82.0]
|
||||
|
||||
### Added
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
@.clinerules/general.md
|
||||
@.clinerules/network.md
|
||||
@.clinerules/cli.md
|
||||
|
||||
@@ -45,7 +45,7 @@ We also welcome contributions to our [documentation](https://github.com/cline/cl
|
||||
3. Install [bun](https://bun.com)
|
||||
4. Install the necessary dependencies for the extension and webview-gui:
|
||||
```bash
|
||||
npm run install:all
|
||||
cd apps/vscode && npm run install:all && cd ../..
|
||||
cd sdk && bun run build && cd ..
|
||||
```
|
||||
5. Generate Protocol Buffer files (required before first build):
|
||||
@@ -61,7 +61,7 @@ We also welcome contributions to our [documentation](https://github.com/cline/cl
|
||||
2. Push your branch and create a PR on GitHub. Our CI will:
|
||||
- Run tests and checks
|
||||
3. Testing
|
||||
- Run `npm run test` to run tests locally.
|
||||
- Run `cd apps/vscode && npm run test` to run tests locally.
|
||||
- Before submitting PR, run `npm run format:fix` to format your code
|
||||
|
||||
### Extension
|
||||
@@ -73,6 +73,7 @@ We also welcome contributions to our [documentation](https://github.com/cline/cl
|
||||
- If you dismissed the prompts, you can install them manually from the Extensions panel
|
||||
|
||||
2. **Local Development**
|
||||
- cd into the vscode extension, `cd apps/vscode`
|
||||
- Run `npm run install:all` to install dependencies
|
||||
- Run `npm run protos` to generate Protocol Buffer files (required before first build)
|
||||
- Run `npm run test` to run tests locally
|
||||
|
||||
@@ -5,12 +5,32 @@
|
||||
<h1 align="center">Cline</h1>
|
||||
|
||||
<p align="center">
|
||||
Autonomous AI coding agents for your IDE, terminal, and applications.
|
||||
The open source coding agent in your IDE and terminal.
|
||||
</p>
|
||||
|
||||
<div align="center">
|
||||
|
||||
[Discord](https://discord.gg/cline) | [Documentation](https://docs.cline.bot) | [Reddit](https://www.reddit.com/r/cline/) | [Feature Requests](https://github.com/cline/cline/discussions/categories/feature-requests?discussions_q=is%3Aopen+category%3A%22Feature+Requests%22+sort%3Atop) | [Careers](https://cline.bot/join-us)
|
||||
<div align="center">
|
||||
<table>
|
||||
<tbody>
|
||||
<td align="center">
|
||||
<a href="https://docs.cline.bot" target="_blank"><strong>Docs</strong></a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://discord.gg/cline" target="_blank"><strong>Discord</strong></a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://www.reddit.com/r/cline/" target="_blank"><strong>r/cline</strong></a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/cline/cline/discussions/categories/feature-requests?discussions_q=is%3Aopen+category%3A%22Feature+Requests%22+sort%3Atop" target="_blank"><strong>Feature Requests</strong></a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://cline.bot/join-us" target="_blank"><strong>Join us!</strong></a>
|
||||
</td>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -44,7 +64,7 @@ web-based task board. Each card gets its own
|
||||
worktree, auto-commit, and dependency chains.
|
||||
|
||||
```
|
||||
npx kanban
|
||||
npm i -g kanban
|
||||
```
|
||||
|
||||
<a href="https://github.com/cline/kanban">Learn more</a>
|
||||
@@ -104,24 +124,22 @@ npm install @cline/sdk
|
||||
|
||||
---
|
||||
|
||||
## Repository Map
|
||||
## Index
|
||||
|
||||
Cline ships across multiple surfaces. When you are reading about a feature below, use the applicability notes to know where it is available and these paths to find the implementation.
|
||||
| Product | Description | Location | CHANGELOG |
|
||||
|---------|------------|--------------|--------------|
|
||||
| **SDK** | Node.js programmatic agent API and extension exports. | [`sdk/`](https://github.com/cline/cline/tree/main/sdk) | [CHANGELOG.md](https://github.com/cline/cline/blob/main/sdk/CHANGELOG.md) |
|
||||
| **CLI** | Terminal UI, headless mode, shell commands, and CLI-specific flows. | [`sdk/apps/cli/`](https://github.com/cline/cline/tree/main/sdk/apps/cli) | [CHANGELOG.md](https://github.com/cline/cline/blob/main/sdk/apps/cli/CHANGELOG.md) |
|
||||
| **VS Code Extension** | The Marketplace extension and extension host integration. | [`/`](https://github.com/cline/cline/tree/main) (WIP migrating) | [CHANGELOG.md](https://github.com/cline/cline/blob/main/CHANGELOG.md) |
|
||||
| **JetBrains Plugin** | JetBrains-hosted client that talks to the shared agent core. | Currently we are not open-sourcing JetBrains plugins | - |
|
||||
| **Kanban** | Web-based multi-agent task board. | [`cline/kanban`](https://github.com/cline/kanban) | [CHANGELOG.md](https://github.com/cline/kanban/blob/main/CHANGELOG.md) |
|
||||
| **Docs site** | Public documentation pages. | [`docs/`](https://docs.cline.bot/) | - |
|
||||
|
||||
| Surface | What it is | Pointers |
|
||||
|---------|------------|--------------|
|
||||
| **SDK** | Node.js programmatic agent API and extension exports. | [`./sdk/`](https://github.com/cline/cline/tree/main/sdk) |
|
||||
| **CLI** | Terminal UI, headless mode, shell commands, and CLI-specific flows. | [`./sdk/apps/cli/`](https://github.com/cline/cline/tree/main/sdk/apps/cli) |
|
||||
| **VS Code Extension** | The Marketplace extension and extension host integration. | [`./`](https://github.com/cline/cline/tree/main) (WIP migrating) |
|
||||
| **JetBrains Plugin** | JetBrains-hosted client that talks to the shared agent core. | Currently we are not open-sourcing JetBrains plugins |
|
||||
| **Kanban** | Web-based multi-agent task board. | Kanban app code lives in [`cline/kanban`](https://github.com/cline/kanban). |
|
||||
| **Docs site** | Public documentation pages. | `docs/` |
|
||||
## Edits Code Across Your Project
|
||||
|
||||
## Edit Code Across All Your Codebases
|
||||
Cline reads your project structure, understands the relationships between files, and makes coordinated changes across your codebase. It monitors linter and compiler errors as it works, fixing issues like missing imports, type mismatches, and syntax errors before you even see them. In VS Code and JetBrains, every edit shows up as a diff you can review, modify, or revert. All changes are tracked with checkpoints, so you can easily undo the agent's work.
|
||||
|
||||
Cline reads your project structure, understands the relationships between files, and makes coordinated changes across your codebase. It monitors linter and compiler errors as it works, fixing issues like missing imports, type mismatches, and syntax errors before you even see them. In VS Code and JetBrains, every edit shows up as a diff you can review, modify, or revert. All changes are tracked in your file timeline.
|
||||
|
||||
## Run Commands and Act to Output
|
||||
## Runs Bash Commands
|
||||
|
||||
Cline executes commands directly in your terminal and watches the output in real time. Install packages, run build scripts, execute tests, deploy applications, manage databases. For long-running processes like dev servers, Cline continues working in the background and reacts to new output as it appears, catching compile errors, test failures, and server crashes as they happen.
|
||||
|
||||
@@ -129,11 +147,11 @@ Cline executes commands directly in your terminal and watches the output in real
|
||||
|
||||
Toggle between Plan mode and Act mode. In Plan mode, Cline explores your codebase, asks clarifying questions, and lays out a strategy. Once you're aligned, switch to Act mode and Cline executes the plan. Every file edit and terminal command requires your approval, so you stay in control of what actually changes. Or toggle auto-approve and let Cline run autonomously.
|
||||
|
||||
## Rules and Configuration
|
||||
## Rules and Skills
|
||||
|
||||
Define project-specific rules in `.clinerules` files that guide how Cline works in your codebase: coding standards, architecture conventions, deployment procedures, testing requirements. Rules are picked up automatically by the CLI, VS Code extension, and JetBrains plugin. Import rules from Cursor or Windsurf formats.
|
||||
Define project-specific rules in `.clinerules` files that guide how Cline works in your codebase: coding standards, architecture conventions, deployment procedures, testing requirements. Rules are picked up automatically by the CLI, VS Code extension, and JetBrains plugin. Use skills to let the model load specific rules when needed.
|
||||
|
||||
## Works With Every Major Model
|
||||
## Works With Every Model
|
||||
|
||||
Cline is not locked to a single AI provider. Use whichever model fits your workflow:
|
||||
|
||||
@@ -150,11 +168,9 @@ Cline is not locked to a single AI provider. Use whichever model fits your workf
|
||||
| Ollama / LM Studio | Run local models on your machine |
|
||||
| Any OpenAI-compatible API | Self-hosted or third-party endpoints |
|
||||
|
||||
## Extend With MCP Servers and Plugins
|
||||
## Extend With Plugins or MCP Servers
|
||||
|
||||
Cline's capabilities are extensible.
|
||||
1. MCP: Use [MCP servers](https://github.com/modelcontextprotocol) to connect to databases, query APIs, manage cloud infrastructure, and interact with external systems. Use [community-built servers](https://github.com/modelcontextprotocol/servers) or ask Cline to create custom tools on the fly. In the CLI, manage servers with `cline mcp`.
|
||||
2. Plugins: With the SDK, register tools and lifecycle hooks programmatically through the plugin system for logging, auditing, policy enforcement, or adding domain-specific capabilities. Simple plugin example below.
|
||||
Extend Cline's capabilities with plugins. Using the SDK, register tools and lifecycle hooks programmatically through the plugin system for logging, auditing, policy enforcement, or adding domain-specific capabilities. Simple plugin example below.
|
||||
|
||||
```typescript
|
||||
import { Agent, createTool } from "@cline/sdk"
|
||||
@@ -170,15 +186,17 @@ const deployTool = createTool({
|
||||
|
||||
const agent = new Agent({ tools: [deployTool], /* ... */ })
|
||||
```
|
||||
...or use [MCP servers](https://github.com/modelcontextprotocol) to connect to databases, query APIs, manage cloud infrastructure, and interact with external systems. Use [community-built servers](https://github.com/modelcontextprotocol/servers) or ask Cline to create custom tools on the fly. In the CLI, manage servers with `cline mcp`.
|
||||
|
||||
## Multi-Agent Teams for Cline SDK and Cline CLI
|
||||
## Multi-Agent Teams
|
||||
|
||||
Coordinate multiple agents working together on complex tasks. A coordinator agent breaks the work into subtasks and delegates to specialist agents, each with their own tools and context. Team state persists across sessions so you can pick up where you left off.
|
||||
|
||||
```bash
|
||||
cline --team-name auth-sprint "Plan and implement user authentication with tests"
|
||||
```
|
||||
## Scheduled Agents for Cline SDK and Cline CLI
|
||||
|
||||
## Scheduled Agents
|
||||
|
||||
Run agents on cron schedules for recurring automations. Daily PR summaries, weekly dependency checks, codebase health reports. Schedules persist across restarts and run independently of any terminal session.
|
||||
|
||||
@@ -189,18 +207,16 @@ cline schedule create "PR summary" \
|
||||
--workspace /path/to/repo
|
||||
```
|
||||
|
||||
## Connect to Slack, Telegram, Discord, and More with Cline CLI
|
||||
## Connect to Slack, Telegram, Discord, and More
|
||||
|
||||
Chat with your agent from any messaging platform. Each conversation thread maps to an agent session with full context. Set up access control to restrict who can interact with your agent.
|
||||
Chat with your agent from any messaging platform: Telegram, Slack, Discord, Google Chat, WhatsApp, and Linear. Each conversation thread maps to an agent session with full context. Set up access control to restrict who can interact with your agent.
|
||||
|
||||
```bash
|
||||
cline connect telegram -m my_bot -k $BOT_TOKEN
|
||||
cline connect telegram -k $BOT_TOKEN
|
||||
cline connect slack --token $SLACK_TOKEN --signing-secret $SECRET --base-url $URL
|
||||
```
|
||||
|
||||
Supported platforms: Telegram, Slack, Discord, Google Chat, WhatsApp, and Linear.
|
||||
|
||||
## Headless Mode for CI/CD with Cline CLI
|
||||
## Headless CLI for CI/CD
|
||||
|
||||
Run Cline with zero interaction for scripting and automation. Pipe input, get JSON output, chain commands, integrate into CI/CD pipelines.
|
||||
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
.vscode/**
|
||||
.vscode-test/**
|
||||
.worktrees/**
|
||||
# Agent tooling, never shipped in the VSIX
|
||||
.agents/**
|
||||
.claude/**
|
||||
.codex/**
|
||||
CLAUDE.local.md
|
||||
out/
|
||||
dist-standalone/
|
||||
@@ -22,9 +26,6 @@ eslint-rules/**
|
||||
.husky/**
|
||||
.env
|
||||
|
||||
# cli
|
||||
cli/**
|
||||
|
||||
# sdk (separate monorepo with its own build/release pipeline)
|
||||
sdk/**
|
||||
|
||||
@@ -19,7 +19,7 @@ English | <a href="https://github.com/cline/cline/blob/main/locales/es/README.md
|
||||
<a href="https://github.com/cline/cline/discussions/categories/feature-requests?discussions_q=is%3Aopen+category%3A%22Feature+Requests%22+sort%3Atop" target="_blank"><strong>Feature Requests</strong></a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://docs.cline.bot/getting-started/for-new-coders" target="_blank"><strong>Getting Started</strong></a>
|
||||
<a href="https://docs.cline.bot/getting-started/installing-cline" target="_blank"><strong>Getting Started</strong></a>
|
||||
</td>
|
||||
</tbody>
|
||||
</table>
|
||||
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 4.9 KiB |
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 902 B After Width: | Height: | Size: 902 B |
|
Before Width: | Height: | Size: 666 B After Width: | Height: | Size: 666 B |
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 902 B After Width: | Height: | Size: 902 B |
|
Before Width: | Height: | Size: 989 B After Width: | Height: | Size: 989 B |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 358 B After Width: | Height: | Size: 358 B |
@@ -1,11 +1,6 @@
|
||||
{
|
||||
"root": false,
|
||||
"$schema": "./node_modules/@biomejs/biome/configuration_schema.json",
|
||||
"vcs": {
|
||||
"enabled": true,
|
||||
"clientKind": "git",
|
||||
"useIgnoreFile": true,
|
||||
"defaultBranch": "main"
|
||||
},
|
||||
"assist": {
|
||||
"enabled": true,
|
||||
"actions": {
|
||||
@@ -179,9 +174,7 @@
|
||||
"!!**/*.js",
|
||||
"!!**/scripts/**",
|
||||
"!!**/*.tsx",
|
||||
"!!**/testing-platform/**",
|
||||
// ACP mode must redirect console to stderr - this is intentional
|
||||
"!!cli/src/acp/index.ts"
|
||||
"!!**/testing-platform/**"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -0,0 +1,630 @@
|
||||
{
|
||||
"name": "claude-dev",
|
||||
"displayName": "Cline",
|
||||
"description": "Autonomous coding agent right in your IDE, capable of creating/editing files, running commands, using the browser, and more with your permission every step of the way.",
|
||||
"version": "3.86.2",
|
||||
"icon": "assets/icons/icon.png",
|
||||
"workspaces": [
|
||||
"."
|
||||
],
|
||||
"engines": {
|
||||
"vscode": "^1.84.0"
|
||||
},
|
||||
"author": {
|
||||
"name": "Cline Bot Inc."
|
||||
},
|
||||
"license": "Apache-2.0",
|
||||
"publisher": "saoudrizwan",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/cline/cline"
|
||||
},
|
||||
"homepage": "https://cline.bot",
|
||||
"categories": [
|
||||
"AI",
|
||||
"Chat",
|
||||
"Programming Languages",
|
||||
"Education",
|
||||
"Snippets",
|
||||
"Testing"
|
||||
],
|
||||
"keywords": [
|
||||
"cline",
|
||||
"claude",
|
||||
"dev",
|
||||
"mcp",
|
||||
"openrouter",
|
||||
"coding",
|
||||
"agent",
|
||||
"autonomous",
|
||||
"chatgpt",
|
||||
"sonnet",
|
||||
"ai",
|
||||
"llama"
|
||||
],
|
||||
"activationEvents": [
|
||||
"onLanguage",
|
||||
"onUri",
|
||||
"onStartupFinished",
|
||||
"workspaceContains:evals.env"
|
||||
],
|
||||
"main": "./dist/extension.js",
|
||||
"contributes": {
|
||||
"icons": {
|
||||
"cline-icon": {
|
||||
"description": "cline",
|
||||
"default": {
|
||||
"fontPath": "assets/icons/cline-bot.woff",
|
||||
"fontCharacter": "\\e900"
|
||||
}
|
||||
}
|
||||
},
|
||||
"walkthroughs": [
|
||||
{
|
||||
"id": "ClineWalkthrough",
|
||||
"title": "Meet Cline, your new coding partner",
|
||||
"description": "Cline codes like a developer because it thinks like one. Here are 5 ways to put it to work:",
|
||||
"steps": [
|
||||
{
|
||||
"id": "welcome",
|
||||
"title": "Start with a Goal, Not Just a Prompt",
|
||||
"description": "Tell Cline what you want to achieve. It plans, asks, and then codes, like a true partner.",
|
||||
"media": {
|
||||
"markdown": "walkthrough/step1.md"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "learn",
|
||||
"title": "Let Cline Learn Your Codebase",
|
||||
"description": "Point Cline to your project. It builds understanding to make smart, context-aware changes.",
|
||||
"media": {
|
||||
"markdown": "walkthrough/step2.md"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "advanced-features",
|
||||
"title": "Always Use the Best AI Models",
|
||||
"description": "Cline empowers you with State-of-the-Art AI, connecting to top models (Anthropic, Gemini, OpenAI & more).",
|
||||
"media": {
|
||||
"markdown": "walkthrough/step3.md"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "mcp",
|
||||
"title": "Extend with Powerful Tools (MCP)",
|
||||
"description": "Connect to databases, APIs, or discover new capabilities in the MCP Marketplace.",
|
||||
"media": {
|
||||
"markdown": "walkthrough/step4.md"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "getting-started",
|
||||
"title": "You're Always in Control",
|
||||
"description": "Review Cline's plans and diffs. Approve changes before they happen. No surprises.",
|
||||
"media": {
|
||||
"markdown": "walkthrough/step5.md"
|
||||
},
|
||||
"content": {
|
||||
"path": "walkthrough/step5.md"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"viewsContainers": {
|
||||
"activitybar": [
|
||||
{
|
||||
"id": "claude-dev-ActivityBar",
|
||||
"title": "Cline",
|
||||
"icon": "assets/icons/icon.svg"
|
||||
}
|
||||
]
|
||||
},
|
||||
"views": {
|
||||
"claude-dev-ActivityBar": [
|
||||
{
|
||||
"type": "webview",
|
||||
"id": "claude-dev.SidebarProvider",
|
||||
"name": "",
|
||||
"icon": "assets/icons/icon.svg"
|
||||
}
|
||||
]
|
||||
},
|
||||
"commands": [
|
||||
{
|
||||
"command": "cline.plusButtonClicked",
|
||||
"title": "New Task",
|
||||
"icon": "$(add)"
|
||||
},
|
||||
{
|
||||
"command": "cline.mcpButtonClicked",
|
||||
"title": "MCP Servers",
|
||||
"icon": "$(server)"
|
||||
},
|
||||
{
|
||||
"command": "cline.historyButtonClicked",
|
||||
"title": "History",
|
||||
"icon": "$(history)"
|
||||
},
|
||||
{
|
||||
"command": "cline.accountButtonClicked",
|
||||
"title": "Account",
|
||||
"icon": "$(account)"
|
||||
},
|
||||
{
|
||||
"command": "cline.settingsButtonClicked",
|
||||
"title": "Settings",
|
||||
"icon": "$(settings-gear)"
|
||||
},
|
||||
{
|
||||
"command": "cline.dev.createTestTasks",
|
||||
"title": "Create Test Tasks",
|
||||
"category": "Cline",
|
||||
"when": "cline.isDevMode"
|
||||
},
|
||||
{
|
||||
"command": "cline.dev.expireMcpOAuthTokens",
|
||||
"title": "Expire MCP OAuth Tokens (for testing)",
|
||||
"category": "Cline",
|
||||
"when": "cline.isDevMode"
|
||||
},
|
||||
{
|
||||
"command": "cline.addToChat",
|
||||
"title": "Add to Cline",
|
||||
"category": "Cline"
|
||||
},
|
||||
{
|
||||
"command": "cline.addTerminalOutputToChat",
|
||||
"title": "Add to Cline",
|
||||
"category": "Cline"
|
||||
},
|
||||
{
|
||||
"command": "cline.focusChatInput",
|
||||
"title": "Jump to Chat Input",
|
||||
"category": "Cline"
|
||||
},
|
||||
{
|
||||
"command": "cline.generateGitCommitMessage",
|
||||
"title": "Generate Commit Message with Cline",
|
||||
"category": "Cline",
|
||||
"icon": "$(cline-icon)"
|
||||
},
|
||||
{
|
||||
"command": "cline.abortGitCommitMessage",
|
||||
"title": "Generate Commit Message with Cline - Stop",
|
||||
"category": "Cline",
|
||||
"icon": "$(debug-stop)"
|
||||
},
|
||||
{
|
||||
"command": "cline.explainCode",
|
||||
"title": "Explain with Cline",
|
||||
"category": "Cline"
|
||||
},
|
||||
{
|
||||
"command": "cline.improveCode",
|
||||
"title": "Improve with Cline",
|
||||
"category": "Cline"
|
||||
},
|
||||
{
|
||||
"command": "cline.jupyterGenerateCell",
|
||||
"title": "Generate Jupyter Cell with Cline",
|
||||
"category": "Cline",
|
||||
"icon": "$(sparkle)"
|
||||
},
|
||||
{
|
||||
"command": "cline.jupyterExplainCell",
|
||||
"title": "Explain Jupyter Cell with Cline",
|
||||
"category": "Cline",
|
||||
"icon": "$(question)"
|
||||
},
|
||||
{
|
||||
"command": "cline.jupyterImproveCell",
|
||||
"title": "Improve Jupyter Cell with Cline",
|
||||
"category": "Cline",
|
||||
"icon": "$(lightbulb)"
|
||||
},
|
||||
{
|
||||
"command": "cline.openWalkthrough",
|
||||
"title": "Open Walkthrough",
|
||||
"category": "Cline"
|
||||
},
|
||||
{
|
||||
"command": "cline.reconstructTaskHistory",
|
||||
"title": "Reconstruct Task History",
|
||||
"category": "Cline"
|
||||
},
|
||||
{
|
||||
"command": "cline.reviewComment.reply",
|
||||
"title": "Reply",
|
||||
"category": "Cline",
|
||||
"enablement": "!commentIsEmpty"
|
||||
},
|
||||
{
|
||||
"command": "cline.reviewComment.addToChat",
|
||||
"title": "Add to Cline Chat",
|
||||
"category": "Cline",
|
||||
"icon": "$(link-external)"
|
||||
}
|
||||
],
|
||||
"keybindings": [
|
||||
{
|
||||
"command": "editor.action.submitComment",
|
||||
"key": "enter",
|
||||
"when": "commentEditorFocused && commentController == cline-ai-review && !commentIsEmpty"
|
||||
},
|
||||
{
|
||||
"command": "cline.addToChat",
|
||||
"key": "cmd+'",
|
||||
"mac": "cmd+'",
|
||||
"win": "ctrl+'",
|
||||
"linux": "ctrl+'",
|
||||
"when": "editorHasSelection"
|
||||
},
|
||||
{
|
||||
"command": "cline.generateGitCommitMessage",
|
||||
"when": "config.git.enabled && scmProvider == git"
|
||||
},
|
||||
{
|
||||
"command": "cline.focusChatInput",
|
||||
"key": "cmd+'",
|
||||
"mac": "cmd+'",
|
||||
"win": "ctrl+'",
|
||||
"linux": "ctrl+'",
|
||||
"when": "!editorHasSelection"
|
||||
}
|
||||
],
|
||||
"menus": {
|
||||
"view/title": [
|
||||
{
|
||||
"command": "cline.plusButtonClicked",
|
||||
"group": "navigation@1",
|
||||
"when": "view == claude-dev.SidebarProvider"
|
||||
},
|
||||
{
|
||||
"command": "cline.mcpButtonClicked",
|
||||
"group": "navigation@2",
|
||||
"when": "view == claude-dev.SidebarProvider"
|
||||
},
|
||||
{
|
||||
"command": "cline.historyButtonClicked",
|
||||
"group": "navigation@3",
|
||||
"when": "view == claude-dev.SidebarProvider"
|
||||
},
|
||||
{
|
||||
"command": "cline.accountButtonClicked",
|
||||
"group": "navigation@5",
|
||||
"when": "view == claude-dev.SidebarProvider"
|
||||
},
|
||||
{
|
||||
"command": "cline.settingsButtonClicked",
|
||||
"group": "navigation@6",
|
||||
"when": "view == claude-dev.SidebarProvider"
|
||||
}
|
||||
],
|
||||
"editor/context": [
|
||||
{
|
||||
"command": "cline.addToChat",
|
||||
"group": "navigation",
|
||||
"when": "editorHasSelection"
|
||||
}
|
||||
],
|
||||
"terminal/context": [
|
||||
{
|
||||
"command": "cline.addTerminalOutputToChat",
|
||||
"group": "navigation"
|
||||
}
|
||||
],
|
||||
"scm/title": [
|
||||
{
|
||||
"command": "cline.generateGitCommitMessage",
|
||||
"group": "navigation",
|
||||
"when": "config.git.enabled && scmProvider == git && !cline.isGeneratingCommit"
|
||||
},
|
||||
{
|
||||
"command": "cline.abortGitCommitMessage",
|
||||
"group": "navigation",
|
||||
"when": "config.git.enabled && scmProvider == git && cline.isGeneratingCommit"
|
||||
}
|
||||
],
|
||||
"notebook/toolbar": [
|
||||
{
|
||||
"command": "cline.jupyterGenerateCell",
|
||||
"group": "navigation/add@1",
|
||||
"when": "notebookType == 'jupyter-notebook'"
|
||||
}
|
||||
],
|
||||
"notebook/cell/title": [
|
||||
{
|
||||
"command": "cline.jupyterExplainCell",
|
||||
"group": "inline@1",
|
||||
"when": "notebookType == 'jupyter-notebook'"
|
||||
},
|
||||
{
|
||||
"command": "cline.jupyterImproveCell",
|
||||
"group": "inline@2",
|
||||
"when": "notebookType == 'jupyter-notebook'"
|
||||
}
|
||||
],
|
||||
"commandPalette": [
|
||||
{
|
||||
"command": "cline.generateGitCommitMessage",
|
||||
"when": "config.git.enabled && !cline.isGeneratingCommit"
|
||||
},
|
||||
{
|
||||
"command": "cline.abortGitCommitMessage",
|
||||
"when": "config.git.enabled && cline.isGeneratingCommit"
|
||||
},
|
||||
{
|
||||
"command": "cline.reviewComment.reply",
|
||||
"when": "false"
|
||||
}
|
||||
],
|
||||
"comments/commentThread/context": [
|
||||
{
|
||||
"command": "cline.reviewComment.reply",
|
||||
"group": "inline",
|
||||
"when": "commentController == cline-ai-review"
|
||||
}
|
||||
],
|
||||
"comments/commentThread/title": [
|
||||
{
|
||||
"command": "cline.reviewComment.addToChat",
|
||||
"group": "inline",
|
||||
"when": "commentController == cline-ai-review"
|
||||
}
|
||||
]
|
||||
},
|
||||
"configuration": {
|
||||
"title": "Cline",
|
||||
"properties": {}
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"vscode:prepublish": "npm run package",
|
||||
"compile": "npm run check-types && npm run lint && node esbuild.mjs",
|
||||
"compile-standalone": "npm run check-types && npm run lint && node esbuild.mjs --standalone",
|
||||
"postcompile-standalone": "node scripts/package-standalone.mjs",
|
||||
"dev": "npm run protos && npm run watch",
|
||||
"watch": "npx npm-run-all -p watch:*",
|
||||
"watch:esbuild": "node esbuild.mjs --watch",
|
||||
"watch:tsc": "tsc --noEmit --watch --project tsconfig.json",
|
||||
"package": "npm run check-types && npm run build:webview && npm run lint && node esbuild.mjs --production",
|
||||
"protos": "node scripts/build-proto.mjs",
|
||||
"protos-python": "node scripts/build-python-proto.mjs",
|
||||
"download-ripgrep": "node scripts/download-ripgrep.mjs",
|
||||
"postprotos": "biome format src/shared/proto src/core/controller src/hosts/ webview-ui/src/services src/generated --write --no-errors-on-unmatched",
|
||||
"clean:build": "rimraf dist dist-standalone webview-ui/build src/generated out/",
|
||||
"clean:deps": "rimraf node_modules webview-ui/node_modules",
|
||||
"clean:all": "npm run clean:build && npm run clean:deps",
|
||||
"compile-tests": "node ./scripts/build-tests.js",
|
||||
"watch-tests": "tsc -p . -w --outDir out",
|
||||
"check-types": "npm run protos && npx tsc --noEmit && cd webview-ui && npx tsc --noEmit",
|
||||
"lint": "biome lint --no-errors-on-unmatched --files-ignore-unknown=true --diagnostic-level=error && npm run lint:proto",
|
||||
"lint:proto": "bash ./scripts/proto-lint.sh",
|
||||
"format": "biome format --changed --no-errors-on-unmatched --files-ignore-unknown=true --diagnostic-level=error",
|
||||
"format:fix": "biome check --changed --no-errors-on-unmatched --files-ignore-unknown=true --write",
|
||||
"fix:all": "biome check --no-errors-on-unmatched --files-ignore-unknown=true --write --diagnostic-level=error --unsafe",
|
||||
"ci:check-all": "npx npm-run-all -p check-types lint format",
|
||||
"ci:build": "npm run protos && npm run build:webview && node esbuild.mjs && npm run compile-tests",
|
||||
"pretest": "npm run compile && npm run compile-tests && npm run compile-standalone && npm run lint",
|
||||
"test": "npx npm-run-all test:unit test:integration",
|
||||
"test:integration": "vscode-test",
|
||||
"test:unit": "cross-env TS_NODE_PROJECT=./tsconfig.unit-test.json mocha",
|
||||
"test:coverage": "vscode-test --coverage",
|
||||
"test:sca-server": "npx tsx watch scripts/test-standalone-core-api-server.ts",
|
||||
"test:tp-orchestrator": "npx tsx scripts/testing-platform-orchestrator.ts",
|
||||
"e2e": "playwright test -c playwright.config.ts",
|
||||
"test:e2e:build": "vsce package --allow-package-secrets sendgrid --out dist/e2e.vsix",
|
||||
"test:e2e": "playwright install && npm run test:e2e:build && node src/test/e2e/utils/build.mjs && playwright test",
|
||||
"test:e2e:optimal": "npm run test:e2e:build && node src/test/e2e/utils/build.mjs && playwright test",
|
||||
"test:e2e:ui": "npx tsx scripts/interactive-playwright.ts",
|
||||
"install:all": "npm install && cd webview-ui && npm install",
|
||||
"dev:webview": "cd webview-ui && npm run dev",
|
||||
"build:webview": "cd webview-ui && npm run build",
|
||||
"test:webview": "cd webview-ui && npm run test",
|
||||
"publish:marketplace": "node scripts/publish-marketplace.mjs",
|
||||
"publish:marketplace:prerelease": "node scripts/publish-marketplace.mjs --pre-release",
|
||||
"publish:marketplace:nightly": "node ./scripts/publish-nightly.mjs",
|
||||
"prepare": "npx husky",
|
||||
"docs": "cd docs && npm run dev",
|
||||
"docs:check-links": "cd docs && npm run check",
|
||||
"docs:rename-file": "cd docs && npm run rename",
|
||||
"report-issue": "node scripts/report-issue.js",
|
||||
"storybook": "cd webview-ui && npm run storybook",
|
||||
"eval:smoke:run": "npx tsx evals/smoke-tests/run-smoke-tests.ts"
|
||||
},
|
||||
"lint-staged": {
|
||||
"src/shared/storage/state-keys.ts": [
|
||||
"node scripts/generate-state-proto.mjs",
|
||||
"git add proto/cline/state.proto"
|
||||
],
|
||||
"*": [
|
||||
"biome check --write --staged --no-errors-on-unmatched --files-ignore-unknown=true"
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "^2.3.14",
|
||||
"@bufbuild/buf": "^1.54.0",
|
||||
"@microsoft/tui-test": "0.0.1",
|
||||
"@types/better-sqlite3": "^7.6.13",
|
||||
"@types/chai": "^5.0.1",
|
||||
"@types/clone-deep": "^4.0.4",
|
||||
"@types/get-folder-size": "^3.0.4",
|
||||
"@types/js-yaml": "^4.0.9",
|
||||
"@types/mocha": "^10.0.7",
|
||||
"@types/node": "20.x",
|
||||
"@types/pdf-parse": "^1.1.4",
|
||||
"@types/picomatch": "^4.0.2",
|
||||
"@types/proxyquire": "^1.3.31",
|
||||
"@types/shell-quote": "^1.7.5",
|
||||
"@types/should": "^11.2.0",
|
||||
"@types/sinon": "^21.0.0",
|
||||
"@types/turndown": "^5.0.5",
|
||||
"@types/vscode": "1.84.0",
|
||||
"@vscode/test-cli": "^0.0.12",
|
||||
"@vscode/test-electron": "^2.5.2",
|
||||
"@vscode/vsce": "^3.6.0",
|
||||
"c8": "^10.1.3",
|
||||
"chai": "^4.3.10",
|
||||
"chalk": "5.6.2",
|
||||
"cross-env": "^10.1.0",
|
||||
"dotenv": "^17.2.3",
|
||||
"esbuild": "^0.25.0",
|
||||
"grpc-tools": "^1.13.0",
|
||||
"husky": "^9.1.7",
|
||||
"lint-staged": "^16.1.0",
|
||||
"minimatch": "^3.0.3",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"nyc": "^17.1.0",
|
||||
"prebuild-install": "^7.1.3",
|
||||
"protoc-gen-ts": "^0.8.7",
|
||||
"proxyquire": "^2.1.3",
|
||||
"rimraf": "^6.0.1",
|
||||
"should": "^13.2.3",
|
||||
"sinon": "^21.0.3",
|
||||
"tree-kill": "^1.2.2",
|
||||
"ts-node": "^10.9.2",
|
||||
"ts-proto": "^2.6.1",
|
||||
"tsconfig-paths": "^4.2.0",
|
||||
"typescript": "^5.4.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@anthropic-ai/sdk": "^0.37.0",
|
||||
"@anthropic-ai/vertex-sdk": "^0.6.4",
|
||||
"@aws-sdk/client-bedrock-runtime": "^3.922.0",
|
||||
"@aws-sdk/credential-providers": "^3.922.0",
|
||||
"@azure/identity": "^4.13.0",
|
||||
"@bufbuild/protobuf": "^2.2.5",
|
||||
"@cerebras/cerebras_cloud_sdk": "^1.35.0",
|
||||
"@google-cloud/vertexai": "^1.9.3",
|
||||
"@google/genai": "^1.30.0",
|
||||
"@grpc/grpc-js": "^1.9.15",
|
||||
"@grpc/reflection": "^1.0.4",
|
||||
"@mistralai/mistralai": "^1.5.0",
|
||||
"@modelcontextprotocol/sdk": "^1.25.1",
|
||||
"@opentelemetry/api": "^1.9.0",
|
||||
"@opentelemetry/core": "^2.1.0",
|
||||
"@opentelemetry/exporter-logs-otlp-grpc": "^0.56.0",
|
||||
"@opentelemetry/exporter-logs-otlp-http": "^0.56.0",
|
||||
"@opentelemetry/exporter-logs-otlp-proto": "^0.56.0",
|
||||
"@opentelemetry/exporter-metrics-otlp-grpc": "^0.56.0",
|
||||
"@opentelemetry/exporter-metrics-otlp-http": "^0.56.0",
|
||||
"@opentelemetry/exporter-metrics-otlp-proto": "^0.56.0",
|
||||
"@opentelemetry/exporter-prometheus": "^0.56.0",
|
||||
"@opentelemetry/exporter-trace-otlp-http": "^0.56.0",
|
||||
"@opentelemetry/instrumentation": "^0.205.0",
|
||||
"@opentelemetry/instrumentation-http": "^0.205.0",
|
||||
"@opentelemetry/resources": "^1.30.1",
|
||||
"@opentelemetry/sdk-logs": "^0.56.0",
|
||||
"@opentelemetry/sdk-metrics": "^1.30.1",
|
||||
"@opentelemetry/sdk-node": "^0.56.0",
|
||||
"@opentelemetry/sdk-trace-base": "^2.1.0",
|
||||
"@opentelemetry/sdk-trace-node": "^1.30.1",
|
||||
"@opentelemetry/semantic-conventions": "^1.37.0",
|
||||
"@playwright/test": "^1.55.1",
|
||||
"@sap-ai-sdk/ai-api": "^2.7.0",
|
||||
"@sap-ai-sdk/orchestration": "^2.7.0",
|
||||
"@sap-cloud-sdk/connectivity": "^4.6.0",
|
||||
"@streamparser/json": "^0.0.22",
|
||||
"@tailwindcss/vite": "^4.1.14",
|
||||
"@types/uuid": "^10.0.0",
|
||||
"@vscode/codicons": "^0.0.36",
|
||||
"archiver": "^7.0.1",
|
||||
"aws4fetch": "^1.0.20",
|
||||
"axios": "1.16.1",
|
||||
"better-sqlite3": "^12.4.1",
|
||||
"cheerio": "^1.0.0",
|
||||
"chokidar": "^4.0.1",
|
||||
"chrome-launcher": "^1.1.2",
|
||||
"clone-deep": "^4.0.1",
|
||||
"default-shell": "^2.2.0",
|
||||
"diff": "8.0.4",
|
||||
"exceljs": "^4.4.0",
|
||||
"execa": "^9.5.2",
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"fzf": "^0.5.2",
|
||||
"get-folder-size": "^5.0.0",
|
||||
"globby": "^14.0.2",
|
||||
"grpc-health-check": "^2.0.2",
|
||||
"iconv-lite": "^0.6.3",
|
||||
"ignore": "^7.0.3",
|
||||
"image-size": "^2.0.2",
|
||||
"isbinaryfile": "^5.0.2",
|
||||
"jschardet": "^3.1.4",
|
||||
"json5": "^2.2.3",
|
||||
"mammoth": "^1.11.0",
|
||||
"nanoid": "^5.1.6",
|
||||
"nice-grpc": "^2.1.12",
|
||||
"node-machine-id": "^1.1.12",
|
||||
"ollama": "^0.5.13",
|
||||
"open": "^10.1.2",
|
||||
"open-graph-scraper": "^6.9.0",
|
||||
"openai": "^6.21.0",
|
||||
"os-name": "^6.0.0",
|
||||
"p-mutex": "^1.0.0",
|
||||
"p-timeout": "^6.1.4",
|
||||
"p-wait-for": "^5.0.2",
|
||||
"pdf-parse": "^1.1.1",
|
||||
"picomatch": "^4.0.3",
|
||||
"posthog-node": "^5.8.0",
|
||||
"puppeteer-chromium-resolver": "^23.0.0",
|
||||
"puppeteer-core": "^23.4.0",
|
||||
"reconnecting-eventsource": "^1.6.4",
|
||||
"serialize-error": "^11.0.3",
|
||||
"shell-quote": "^1.8.3",
|
||||
"simple-git": "3.36.0",
|
||||
"strip-ansi": "^7.1.2",
|
||||
"tailwindcss": "^4.1.14",
|
||||
"tree-sitter-wasms": "^0.1.11",
|
||||
"ts-morph": "^25.0.1",
|
||||
"turndown": "^7.2.0",
|
||||
"ulid": "^2.4.0",
|
||||
"undici": "^7.26.0",
|
||||
"uuid": "^11.1.0",
|
||||
"vscode-uri": "^3.1.0",
|
||||
"web-tree-sitter": "^0.22.6",
|
||||
"zod": "^3.24.2"
|
||||
},
|
||||
"overrides": {
|
||||
"tar-fs": ">=3.1.1",
|
||||
"tar": "^7.5.2",
|
||||
"vite": "^7.1.11",
|
||||
"js-yaml": "^4.1.1",
|
||||
"serialize-javascript": ">=7.0.3",
|
||||
"protobufjs": "7.5.8",
|
||||
"diff": "8.0.4"
|
||||
},
|
||||
"c8": {
|
||||
"reporter": [
|
||||
"lcov",
|
||||
"html"
|
||||
],
|
||||
"exclude": [
|
||||
"**/testing-platform/**",
|
||||
"**/webview-ui/**",
|
||||
"**/.vscode-test/**",
|
||||
"**/node_modules/**",
|
||||
"node_modules",
|
||||
"**/dist-standalone/src/**",
|
||||
"**/dist-standalone/vsce-extension/https:/**",
|
||||
"**/dist-standalone/vsce-extension/**",
|
||||
"**/dist-standalone/https:/**",
|
||||
"**/dist-standalone/LIB/src/**",
|
||||
"**/dist-standalone/pdfjs-dist/**",
|
||||
"**/*.d.ts",
|
||||
"**/*.{test,spec}.{js,jsx,ts,tsx,mjs,cjs}",
|
||||
"**/__tests__/**",
|
||||
"**/test/**",
|
||||
"**/tests/**",
|
||||
"**/.nyc_output/**",
|
||||
"**/tests-results/**",
|
||||
"src/test/**",
|
||||
"**/src/xml/**",
|
||||
"**/standalone/**",
|
||||
"**/src/generated/**",
|
||||
"dist"
|
||||
],
|
||||
"all": true,
|
||||
"exclude-after-remap": true
|
||||
}
|
||||
}
|
||||