Documentation Index
Fetch the complete documentation index at: https://docs.cline.bot/llms.txt
Use this file to discover all available pages before exploring further.
Tool policies control whether tools are enabled and whether they run without approval. Tool names not listed in toolPolicies default to enabled and auto-approved, so set policies explicitly for tools that need review.
The simplest way to manage permissions is through tool policies. Set them per-tool when creating an agent:
const agent = new Agent({
// ...config
tools: [readFileTool, writeFileTool, bashTool, searchTool],
toolPolicies: {
read_files: { autoApprove: true }, // Always run without asking
search_codebase: { autoApprove: true }, // Always run without asking
write_file: { autoApprove: false }, // Always ask before running
run_commands: { autoApprove: false }, // Always ask before running
},
})
Policy Options
| Policy | Effect |
|---|
{ autoApprove: true } | Tool executes immediately without approval |
{ autoApprove: false } | Tool waits for approval before executing |
{ enabled: false } | Tool is completely disabled (model won’t see it) |
| No policy set | Defaults to enabled and auto-approved |
Auto-Approve Everything
For trusted environments (CI pipelines, sandboxed containers, development scripts):
const agent = new Agent({
// ...config
tools: allTools,
toolPolicies: Object.fromEntries(
allTools.map((t) => [t.name, { autoApprove: true }])
),
})
Or with ClineCore:
const session = await cline.start({
config: {
enableTools: true,
},
toolPolicies: {
run_commands: { autoApprove: true },
editor: { autoApprove: true },
read_files: { autoApprove: true },
apply_patch: { autoApprove: true },
search_codebase: { autoApprove: true },
fetch_web_content: { autoApprove: true },
},
capabilities: {
requestToolApproval: async () => ({ approved: true }),
},
// ...
})
Auto-approving all tools means the agent can execute any shell command, modify any file, and make network requests without your review. Only use this in environments where the agent’s actions are either sandboxed or fully trusted.
Interactive Approval
For applications that need human oversight, implement a custom approval handler:
import { ClineCore } from "@cline/sdk"
import * as readline from "readline"
const rl = readline.createInterface({ input: process.stdin, output: process.stdout })
const ask = (q: string) => new Promise<string>((res) => rl.question(q, res))
const cline = await ClineCore.create({
clientName: "interactive-app",
capabilities: {
requestToolApproval: async (request) => {
console.log(`\nTool: ${request.toolName}`)
console.log(`Input: ${JSON.stringify(request.input, null, 2)}`)
const answer = await ask("Approve? (y/n): ")
return { approved: answer.toLowerCase() === "y" }
},
},
})
Tiered Permissions
A practical middle ground: auto-approve read-only operations, require approval for writes:
const READ_TOOLS = new Set(["read_files", "search_codebase", "fetch_web_content"])
const WRITE_TOOLS = new Set(["run_commands", "editor", "apply_patch"])
const toolPolicies: Record<string, { autoApprove: boolean }> = {}
for (const tool of READ_TOOLS) {
toolPolicies[tool] = { autoApprove: true }
}
for (const tool of WRITE_TOOLS) {
toolPolicies[tool] = { autoApprove: false }
}
Conditional Approval Logic
Approve based on what the tool is actually doing, not just which tool it is:
const cline = await ClineCore.create({
clientName: "smart-approval",
capabilities: {
requestToolApproval: async (request) => {
// Auto-approve non-destructive shell commands
if (request.toolName === "run_commands") {
const cmd = JSON.stringify(request.input)
const safeCommands = ["ls", "cat", "grep", "find", "git status", "git log", "git diff"]
if (safeCommands.some((safe) => cmd.startsWith(safe))) {
return { approved: true }
}
}
// Auto-approve reads to specific directories
if (request.toolName === "read_files") {
const path = request.input.path as string
if (path.startsWith("/src/") || path.startsWith("/tests/")) {
return { approved: true }
}
}
// Everything else requires manual approval
console.log(`Approval needed: ${request.toolName}`)
console.log(`Input: ${JSON.stringify(request.input)}`)
return { approved: false }
},
},
})
When approval is denied, the agent receives a rejection message and can adjust its approach. It might:
- Ask the user for clarification
- Try a different tool to accomplish the same goal
- Modify its approach and try again with different parameters
- Give up on that subtask and move on
The agent does not get stuck in a loop. The rejection counts as a response, and the agent proceeds with its next iteration.