Back to Cline

System Prompt Architecture

src/core/prompts/system-prompt/README.md

3.82.036.2 KB
Original Source

System Prompt Architecture

Overview

The system prompt architecture provides a modular, composable system for building AI assistant prompts. It supports multiple model variants, dynamic component composition, flexible tool configuration, and template-based prompt generation.

Developer

To generate snapshots for each variants added to the unit test in src/core/prompts/system-prompt/tests/integration.test.ts:

sh
npm run test:unit

Directory Structure

src/core/prompts/system-prompt/
├── registry/
│   ├── ClineToolSet.ts            # Tool set management & registry
│   ├── PromptRegistry.ts          # Singleton registry for loading/managing prompts
│   ├── PromptBuilder.ts           # Builds final prompts with template resolution
│   └── utils.ts                   # Model family detection utilities
├── components/                    # Reusable prompt components
│   ├── agent_role.ts             # Agent role and identity section
│   ├── system_info.ts            # System information section
│   ├── mcp.ts                    # MCP servers section  
│   ├── todo.ts                   # Todo management section
│   ├── user_instructions.ts      # User custom instructions
│   ├── tool_use.ts               # Tool usage instructions
│   ├── editing_files.ts          # File editing guidelines
│   ├── capabilities.ts           # Agent capabilities section
│   ├── rules.ts                  # Behavioral rules section
│   ├── objective.ts              # Task objective section
│   ├── act_vs_plan.ts            # Action vs planning mode
│   ├── feedback.ts               # Feedback and improvement section
│   └── index.ts                  # Component registry
├── templates/                    # Template engine and placeholders
│   ├── TemplateEngine.ts         # {{placeholder}} resolution engine
│   └── placeholders.ts           # Standard placeholder definitions
├── tools/                        # Individual tool definitions
│   ├── spec.ts                   # Tool specification interface
│   ├── register.ts               # Tool registration system
│   ├── index.ts                  # Tool exports
│   └── [tool-name].ts            # Individual tool implementations
├── variants/                     # Model-specific prompt variants
│   ├── generic/
│   │   ├── config.ts             # Generic fallback configuration
│   │   └── template.ts           # Base prompt template
│   ├── next-gen/
│   │   ├── config.ts             # Next-gen model configuration
│   │   └── template.ts           # Advanced model template
│   ├── xs/
│   │   ├── config.ts             # Small model configuration
│   │   └── template.ts           # Optimized template
│   └── index.ts                  # Variant registry exports
├── types.ts                      # Core type definitions
└── README.md                     # This documentation

Core Components

1. PromptRegistry (Singleton)

The PromptRegistry is the central manager for all prompt variants and components. It provides a singleton interface for loading and accessing prompts.

typescript
class PromptRegistry {
  private static instance: PromptRegistry;
  private variants: Map<string, PromptVariant> = new Map();
  private components: ComponentRegistry = {};
  private loaded: boolean = false;

  static getInstance(): PromptRegistry {
    if (!this.instance) {
      this.instance = new PromptRegistry();
    }
    return this.instance;
  }

  // Load all prompts and components on initialization
  async load(): Promise<void> {
    if (this.loaded) return;
    
    await Promise.all([
      this.loadVariants(),    // Load from variants/ directory
      this.loadComponents()   // Load from components/ directory
    ]);
    
    this.loaded = true;
  }

	/**
	 * Get prompt by model ID with fallback to generic
	 */
	async get(context: SystemPromptContext): Promise<string> {
		await this.load()

		// Try model family fallback (e.g., "claude-4" -> "claude")
		const modelFamily = this.getModelFamily(context.providerInfo)
		const variant = this.variants.get(modelFamily ?? ModelFamily.GENERIC)

		if (!variant) {
			throw new Error(
				`No prompt variant found for model '${context.providerInfo.model.id}' and no generic fallback available`,
			)
		}

		const builder = new PromptBuilder(variant, context, this.components)
		return await builder.build()
	}

  // Get specific version of a prompt
  async getVersion(modelId: string, version: number, context: SystemPromptContext, isNextGenModelFamily?: boolean): Promise<string> {
    // Supports next-gen model family prioritization
  }

  // Get prompt by tag/label
  async getByTag(modelId: string, tag?: string, label?: string, context?: SystemPromptContext, isNextGenModelFamily?: boolean): Promise<string> {
    // Supports tag and label-based retrieval with next-gen prioritization
  }
}

2. PromptVariant Structure

The PromptVariant interface defines the configuration for each model-specific prompt variant:

typescript
interface PromptVariant {
  id: string;                                    // Model family ID (e.g., "next-gen", "generic")
  version: number;                               // Version number
  family: ModelFamily;                           // Model family enum
  tags: string[];                                // ["production", "beta", "experimental"]
  labels: { [key: string]: number };             // {"staging": 2, "prod": 1}
  description: string;                           // Brief description of the variant

  // Prompt configuration
  config: PromptConfig;                          // Model-specific config
  baseTemplate: string;                          // Main prompt template with placeholders
  componentOrder: SystemPromptSection[];        // Ordered list of components to include
  componentOverrides: { [K in SystemPromptSection]?: ConfigOverride }; // Component customizations
  placeholders: { [key: string]: string };      // Default placeholder values

  // Tool configuration
  tools?: ClineDefaultTool[];                    // Ordered list of tools to include
  toolOverrides?: { [K in ClineDefaultTool]?: ConfigOverride }; // Tool-specific customizations
}

interface PromptConfig {
  modelName?: string;
  temperature?: number;
  maxTokens?: number;
  tools?: ClineToolSpec[];
  [key: string]: any;                            // Additional arbitrary config
}

interface ConfigOverride {
  template?: string;                             // Custom template for the component/tool
  enabled?: boolean;                             // Whether the component/tool is enabled
  order?: number;                                // Override the order
}

3. PromptBuilder

The PromptBuilder orchestrates the construction of the final prompt by combining templates, components, and placeholders:

typescript
class PromptBuilder {
  private templateEngine: TemplateEngine;

  constructor(
    private variant: PromptVariant,
    private context: SystemPromptContext,
    private components: ComponentRegistry
  ) {
    this.templateEngine = new TemplateEngine();
  }

  async build(): Promise<string> {
    // 1. Build all components in specified order
    const componentSections = await this.buildComponents();
    
    // 2. Prepare all placeholder values
    const placeholderValues = this.preparePlaceholders(componentSections);
    
    // 3. Resolve template placeholders
    const prompt = this.templateEngine.resolve(this.variant.baseTemplate, placeholderValues);
    
    // 4. Apply final post-processing
    return this.postProcess(prompt);
  }

  private async buildComponents(): Promise<Record<string, string>> {
    const sections: Record<string, string> = {};
    
    // Process components sequentially to maintain order
    for (const componentId of this.variant.componentOrder) {
      const componentFn = this.components[componentId];
      if (!componentFn) {
        console.warn(`Warning: Component '${componentId}' not found`);
        continue;
      }

      try {
        const result = await componentFn(this.variant, this.context);
        if (result?.trim()) {
          sections[componentId] = result;
        }
      } catch (error) {
        console.warn(`Warning: Failed to build component '${componentId}':`, error);
      }
    }

    return sections;
  }

  private preparePlaceholders(componentSections: Record<string, string>): Record<string, unknown> {
    const placeholders: Record<string, unknown> = {};

    // Add variant placeholders
    Object.assign(placeholders, this.variant.placeholders);

    // Add standard system placeholders
    placeholders[STANDARD_PLACEHOLDERS.CWD] = this.context.cwd || process.cwd();
    placeholders[STANDARD_PLACEHOLDERS.SUPPORTS_BROWSER] = this.context.supportsBrowserUse || false;
    placeholders[STANDARD_PLACEHOLDERS.MODEL_FAMILY] = this.variant.family;
    placeholders[STANDARD_PLACEHOLDERS.CURRENT_DATE] = new Date().toISOString().split("T")[0];

    // Add all component sections
    Object.assign(placeholders, componentSections);

    // Add runtime placeholders with highest priority
    const runtimePlaceholders = (this.context as any).runtimePlaceholders;
    if (runtimePlaceholders) {
      Object.assign(placeholders, runtimePlaceholders);
    }

    return placeholders;
  }

  private postProcess(prompt: string): string {
    if (!prompt) return "";

    // Combine multiple regex operations for better performance
    return prompt
      .replace(/\n\s*\n\s*\n/g, "\n\n")     // Remove multiple consecutive empty lines
      .trim()                                // Remove leading/trailing whitespace
      .replace(/====+\s*$/, "")             // Remove trailing ==== after trim
      .replace(/\n====+\s*\n+\s*====+\n/g, "\n====\n") // Remove empty sections between separators
      .replace(/====\n([^\n])/g, "====\n\n$1")          // Ensure proper section separation
      .replace(/([^\n])\n====/g, "$1\n\n====");
  }
}

4. Template System

The template system uses {{PLACEHOLDER}} syntax for dynamic content injection:

typescript
class TemplateEngine {
  resolve(template: string, placeholders: Record<string, unknown>): string {
    return template.replace(/\{\{([^}]+)\}\}/g, (match, key) => {
      const trimmedKey = key.trim();
      
      // Support nested object access using dot notation
      const value = this.getNestedValue(placeholders, trimmedKey);
      
      if (value !== undefined && value !== null) {
        return typeof value === "string" ? value : JSON.stringify(value);
      }
      
      // Keep placeholder if not found (allows for partial resolution)
      return match;
    });
  }

  extractPlaceholders(template: string): string[] {
    const placeholders: string[] = [];
    const regex = /\{\{([^}]+)\}\}/g;
    let match: RegExpExecArray | null = null;

    match = regex.exec(template);
    while (match !== null) {
      const placeholder = match[1].trim();
      if (!placeholders.includes(placeholder)) {
        placeholders.push(placeholder);
      }
      match = regex.exec(template);
    }

    return placeholders;
  }
}

Base Template Example:

markdown
You are Cline, a highly skilled software engineer...

====

{{TOOL_USE_SECTION}}

====

{{MCP_SECTION}}

====

{{USER_INSTRUCTIONS_SECTION}}

====

{{SYSTEM_INFO_SECTION}}

====

{{TODO_SECTION}}

5. Component System

Components are reusable functions that generate specific sections of the prompt:

typescript
type ComponentFunction = (
  variant: PromptVariant, 
  context: SystemPromptContext
) => Promise<string | undefined>;

// Example component
export async function getSystemInfo(
  variant: PromptVariant,
  context: SystemPromptContext,
): Promise<string> {
  const info = await getSystemEnv();

  // Support component overrides
  const template = variant.componentOverrides?.SYSTEM_INFO_SECTION?.template || `
Operating System: {{os}}
Default Shell: {{shell}}
Home Directory: {{homeDir}}
Current Working Directory: {{workingDir}}
  `;

  return new TemplateEngine().resolve(template, {
    os: info.os,
    shell: info.shell,
    homeDir: info.homeDir,
    workingDir: info.workingDir
  });
}

6. Tool System

Tools are managed through the ClineToolSet and can be configured per variant:

typescript
class ClineToolSet {
  private static variants: Map<ModelFamily, Set<ClineToolSet>> = new Map();

  static register(config: ClineToolSpec): ClineToolSet {
    return new ClineToolSet(config.id, config);
  }

  static getTools(variant: ModelFamily): ClineToolSet[] {
    const toolsSet = ClineToolSet.variants.get(variant) || new Set();
    const defaultSet = ClineToolSet.variants.get(ModelFamily.GENERIC) || new Set();
    return toolsSet ? Array.from(toolsSet) : Array.from(defaultSet);
  }
}

// Tool generation in PromptBuilder
public static async getToolsPrompts(variant: PromptVariant, context: SystemPromptContext) {
  const tools = ClineToolSet.getTools(variant.family);
  
  // Filter and sort tools based on variant configuration
  const enabledTools = tools.filter((tool) => 
    !tool.config.contextRequirements || tool.config.contextRequirements(context)
  );

  let sortedEnabledTools = enabledTools;
  if (variant?.tools?.length) {
    const toolOrderMap = new Map(variant.tools.map((id, index) => [id, index]));
    sortedEnabledTools = enabledTools.sort((a, b) => {
      const orderA = toolOrderMap.get(a.config.id);
      const orderB = toolOrderMap.get(b.config.id);
      
      if (orderA !== undefined && orderB !== undefined) {
        return orderA - orderB;
      }
      if (orderA !== undefined) return -1;
      if (orderB !== undefined) return 1;
      return a.config.id.localeCompare(b.config.id);
    });
  }

  const ids = sortedEnabledTools.map((tool) => tool.config.id);
  return Promise.all(sortedEnabledTools.map((tool) => PromptBuilder.tool(tool.config, ids)));
}

Configuration Examples

Basic Variant Configuration (Using Builder Pattern)

typescript
// variants/generic/config.ts
import { ModelFamily } from "@/shared/prompts";
import { ClineDefaultTool } from "@/shared/tools";
import { SystemPromptSection } from "../../templates/placeholders";
import { validateVariant } from "../../validation/VariantValidator";
import { createVariant } from "../builder";
import { baseTemplate } from "./template";

// Type-safe variant configuration using the builder pattern
export const config = createVariant(ModelFamily.GENERIC)
  .description("The fallback prompt for generic use cases and models.")
  .version(1)
  .tags("fallback", "stable")
  .labels({
    stable: 1,
    fallback: 1,
  })
  .template(baseTemplate)
  .components(
    SystemPromptSection.AGENT_ROLE,
    SystemPromptSection.TOOL_USE,
    SystemPromptSection.MCP,
    SystemPromptSection.EDITING_FILES,
    SystemPromptSection.ACT_VS_PLAN,
    SystemPromptSection.TODO,
    SystemPromptSection.CAPABILITIES,
    SystemPromptSection.RULES,
    SystemPromptSection.SYSTEM_INFO,
    SystemPromptSection.OBJECTIVE,
    SystemPromptSection.USER_INSTRUCTIONS,
  )
  .tools(
    ClineDefaultTool.BASH,
    ClineDefaultTool.FILE_READ,
    ClineDefaultTool.FILE_NEW,
    ClineDefaultTool.FILE_EDIT,
    ClineDefaultTool.SEARCH,
    ClineDefaultTool.LIST_FILES,
    ClineDefaultTool.LIST_CODE_DEF,
    ClineDefaultTool.BROWSER,
    ClineDefaultTool.MCP_USE,
    ClineDefaultTool.MCP_ACCESS,
    ClineDefaultTool.ASK,
    ClineDefaultTool.ATTEMPT,
    ClineDefaultTool.NEW_TASK,
    ClineDefaultTool.PLAN_MODE,
    ClineDefaultTool.MCP_DOCS,
    ClineDefaultTool.TODO,
  )
  .placeholders({
    MODEL_FAMILY: "generic",
  })
  .config({})
  .build();

// Compile-time validation
const validationResult = validateVariant({ ...config, id: "generic" }, { strict: true });
if (!validationResult.isValid) {
  console.error("Generic variant configuration validation failed:", validationResult.errors);
  throw new Error(`Invalid generic variant configuration: ${validationResult.errors.join(", ")}`);
}

// Export type information for better IDE support
export type GenericVariantConfig = typeof config;

Advanced Variant with Overrides (Using Builder Pattern)

typescript
// variants/next-gen/config.ts
import { ModelFamily } from "@/shared/prompts";
import { ClineDefaultTool } from "@/shared/tools";
import { SystemPromptSection } from "../../templates/placeholders";
import { validateVariant } from "../../validation/VariantValidator";
import { createVariant } from "../builder";
import { baseTemplate, rules_template } from "./template";

// Type-safe variant configuration using the builder pattern
export const config = createVariant(ModelFamily.NEXT_GEN)
  .description("Prompt tailored to newer frontier models with smarter agentic capabilities.")
  .version(1)
  .tags("next-gen", "advanced", "production")
  .labels({
    stable: 1,
    production: 1,
    advanced: 1,
  })
  .template(baseTemplate)
  .components(
    SystemPromptSection.AGENT_ROLE,
    SystemPromptSection.TOOL_USE,
    SystemPromptSection.MCP,
    SystemPromptSection.EDITING_FILES,
    SystemPromptSection.ACT_VS_PLAN,
    SystemPromptSection.TODO,
    SystemPromptSection.CAPABILITIES,
    SystemPromptSection.FEEDBACK,  // Additional component for next-gen
    SystemPromptSection.RULES,
    SystemPromptSection.SYSTEM_INFO,
    SystemPromptSection.OBJECTIVE,
    SystemPromptSection.USER_INSTRUCTIONS,
  )
  .tools(
    ClineDefaultTool.BASH,
    ClineDefaultTool.FILE_READ,
    ClineDefaultTool.FILE_NEW,
    ClineDefaultTool.FILE_EDIT,
    ClineDefaultTool.SEARCH,
    ClineDefaultTool.LIST_FILES,
    ClineDefaultTool.LIST_CODE_DEF,
    ClineDefaultTool.BROWSER,
    ClineDefaultTool.WEB_FETCH,  // Additional tool for next-gen
    ClineDefaultTool.MCP_USE,
    ClineDefaultTool.MCP_ACCESS,
    ClineDefaultTool.ASK,
    ClineDefaultTool.ATTEMPT,
    ClineDefaultTool.NEW_TASK,
    ClineDefaultTool.PLAN_MODE,
    ClineDefaultTool.MCP_DOCS,
    ClineDefaultTool.TODO,
  )
  .placeholders({
    MODEL_FAMILY: ModelFamily.NEXT_GEN,
  })
  .config({})
  // Override the RULES component with custom template
  .overrideComponent(SystemPromptSection.RULES, {
    template: rules_template,
  })
  .build();

// Compile-time validation
const validationResult = validateVariant({ ...config, id: "next-gen" }, { strict: true });
if (!validationResult.isValid) {
  console.error("Next-gen variant configuration validation failed:", validationResult.errors);
  throw new Error(`Invalid next-gen variant configuration: ${validationResult.errors.join(", ")}`);
}

// Export type information for better IDE support
export type NextGenVariantConfig = typeof config;

Compact Variant with Component Overrides

typescript
// variants/xs/config.ts
import { ModelFamily } from "@/shared/prompts";
import { ClineDefaultTool } from "@/shared/tools";
import { SystemPromptSection } from "../../templates/placeholders";
import { validateVariant } from "../../validation/VariantValidator";
import { createVariant } from "../builder";
import { xsComponentOverrides } from "./overrides";
import { baseTemplate } from "./template";

// Type-safe variant configuration using the builder pattern
export const config = createVariant(ModelFamily.XS)
  .description("Prompt for models with a small context window.")
  .version(1)
  .tags("local", "xs", "compact")
  .labels({
    stable: 1,
    production: 1,
    advanced: 1,
  })
  .template(baseTemplate)
  .components(
    SystemPromptSection.AGENT_ROLE,
    SystemPromptSection.RULES,
    SystemPromptSection.ACT_VS_PLAN,
    SystemPromptSection.CAPABILITIES,
    SystemPromptSection.EDITING_FILES,
    SystemPromptSection.OBJECTIVE,
    SystemPromptSection.SYSTEM_INFO,
    SystemPromptSection.USER_INSTRUCTIONS,
  )
  .tools(
    ClineDefaultTool.BASH,
    ClineDefaultTool.FILE_READ,
    ClineDefaultTool.FILE_NEW,
    ClineDefaultTool.FILE_EDIT,
    ClineDefaultTool.SEARCH,
    ClineDefaultTool.LIST_FILES,
    ClineDefaultTool.ASK,
    ClineDefaultTool.ATTEMPT,
    ClineDefaultTool.NEW_TASK,
    ClineDefaultTool.PLAN_MODE,
    ClineDefaultTool.MCP_USE,
    ClineDefaultTool.MCP_ACCESS,
    ClineDefaultTool.MCP_DOCS,
  )
  .placeholders({
    MODEL_FAMILY: ModelFamily.XS,
  })
  .config({})
  .build();

// Apply component overrides after building the base configuration
// This is necessary because the builder pattern doesn't support bulk overrides
Object.assign(config.componentOverrides, xsComponentOverrides);

// Compile-time validation
const validationResult = validateVariant({ ...config, id: "xs" }, { strict: true });
if (!validationResult.isValid) {
  console.error("XS variant configuration validation failed:", validationResult.errors);
  throw new Error(`Invalid XS variant configuration: ${validationResult.errors.join(", ")}`);
}

// Export type information for better IDE support
export type XsVariantConfig = typeof config;

VariantBuilder API Reference

The VariantBuilder class provides a fluent, type-safe API for creating variant configurations:

typescript
import { createVariant } from "../VariantBuilder";

const config = createVariant(ModelFamily.GENERIC)
  .description("Brief description of this variant")  // Required
  .version(1)                                        // Required, defaults to 1
  .tags("tag1", "tag2", "tag3")                     // Optional, can be chained
  .labels({ stable: 1, production: 1 })             // Optional
  .template(baseTemplate)                           // Required
  .components(                                      // Required, type-safe component selection
    SystemPromptSection.AGENT_ROLE,
    SystemPromptSection.TOOL_USE,
    // ... more components
  )
  .tools(                                          // Optional, type-safe tool selection
    ClineDefaultTool.BASH,
    ClineDefaultTool.FILE_READ,
    // ... more tools
  )
  .placeholders({                                  // Optional
    MODEL_FAMILY: "generic",
    CUSTOM_PLACEHOLDER: "value",
  })
  .config({                                        // Optional, model-specific config
    temperature: 0.7,
    maxTokens: 4096,
  })
  .overrideComponent(SystemPromptSection.RULES, {  // Optional, component overrides
    template: customRulesTemplate,
  })
  .overrideTool(ClineDefaultTool.BASH, {          // Optional, tool overrides
    enabled: false,
  })
  .build();                                       // Returns Omit<PromptVariant, "id">

Usage Examples

Basic Usage

typescript
// Initialize registry (done once at startup)
const registry = PromptRegistry.getInstance();
await registry.load();

// Get prompt for specific model
const prompt = await registry.get("claude-3-5-sonnet-20241022", context);

// Get prompt for next-gen model (automatically detects model family)
const prompt = await registry.get("claude-4-20250101", context);

Version and Tag-based Retrieval

typescript
// Get specific version
const prompt = await registry.getVersion("next-gen", 2, context);

// Get by tag/label with next-gen prioritization
const prompt = await registry.getByTag("claude-4", "production", undefined, context, true);

// Get by label
const prompt = await registry.getByTag("generic", undefined, "stable", context);

Runtime Placeholder Resolution

typescript
// Add runtime placeholders to context
context.runtimePlaceholders = {
  "USER_NAME": "John",
  "PROJECT_TYPE": "React",
  "CUSTOM_INSTRUCTION": "Focus on TypeScript best practices"
};

const prompt = await registry.get("next-gen", context);

Model Family Detection

The system automatically detects model families based on model IDs:

typescript
function getModelFamily(modelId: string): ModelFamily {
  // Check for next-gen models first
  if (isNextGenModel(modelId)) {
    return ModelFamily.NEXT_GEN;
  }
  
  if (modelId.includes("qwen")) {
    return ModelFamily.XS;
  }
  
  // Default fallback
  return ModelFamily.GENERIC;
}

function isNextGenModel(modelId: string): boolean {
  return (
    isClaude4ModelFamily(mockApiHandlerModel) ||
    isGemini2dot5ModelFamily(mockApiHandlerModel) ||
    isGrok4ModelFamily(mockApiHandlerModel) ||
    isGPT5ModelFamily(mockApiHandlerModel)
  );
}

Available Components

The system includes the following built-in components:

  • AGENT_ROLE_SECTION: Agent identity and role definition
  • TOOL_USE_SECTION: Tool usage instructions and available tools
  • MCP_SECTION: MCP server information and capabilities
  • EDITING_FILES_SECTION: File editing guidelines and best practices
  • ACT_VS_PLAN_SECTION: Action vs planning mode instructions
  • TODO_SECTION: Todo management and task tracking
  • CAPABILITIES_SECTION: Agent capabilities and limitations
  • FEEDBACK_SECTION: Feedback and improvement instructions (next-gen only)
  • RULES_SECTION: Behavioral rules and constraints
  • SYSTEM_INFO_SECTION: System environment information
  • OBJECTIVE_SECTION: Current task objective
  • USER_INSTRUCTIONS_SECTION: User-provided custom instructions

Available Tools

The system supports the following tools (mapped to ClineDefaultTool enum):

  • BASH: Execute shell commands
  • FILE_READ: Read file contents
  • FILE_NEW: Create new files
  • FILE_EDIT: Edit existing files
  • SEARCH: Search through files
  • LIST_FILES: List directory contents
  • LIST_CODE_DEF: List code definitions
  • BROWSER: Browser automation (conditional)
  • WEB_FETCH: Web content fetching (next-gen only)
  • MCP_USE: Use MCP tools
  • MCP_ACCESS: Access MCP resources
  • ASK: Ask follow-up questions
  • ATTEMPT: Attempt task completion
  • NEW_TASK: Create new tasks
  • PLAN_MODE: Plan mode responses
  • MCP_DOCS: Load MCP documentation
  • TODO: Todo management

Adding New Tools

Tool Structure and Anatomy

Each tool in Cline follows a specific structure with variants for different model families. Here's the anatomy of a tool:

typescript
// src/core/prompts/system-prompt/tools/my_new_tool.ts
import { ModelFamily } from "@/shared/prompts"
import { ClineDefaultTool } from "@/shared/tools"
import type { ClineToolSpec } from "../spec"

const id = ClineDefaultTool.MY_NEW_TOOL // Add to enum first

const generic: ClineToolSpec = {
	variant: ModelFamily.GENERIC,
	id,
	name: "my_new_tool",
	description: "Description of what this tool does and when to use it",
	parameters: [
		{
			name: "required_param",
			required: true,
			instruction: "Description of this parameter and how to use it",
			usage: "Example value or placeholder text",
		},
		{
			name: "optional_param",
			required: false,
			instruction: "Description of optional parameter",
			usage: "Optional example (optional)",
			dependencies: [ClineDefaultTool.SOME_OTHER_TOOL], // Only show if dependency exists
		},
	],
}

// Create variants for different model families if needed
const nextGen = { ...generic, variant: ModelFamily.NEXT_GEN }
const gpt = { ...generic, variant: ModelFamily.GPT }
const gemini = { ...generic, variant: ModelFamily.GEMINI }

export const my_new_tool_variants = [generic, nextGen, gpt, gemini]

Step-by-Step Instructions for Adding a New Tool

1. Add Tool ID to Enum

First, add your tool ID to the ClineDefaultTool enum:

typescript
// src/shared/tools.ts
export enum ClineDefaultTool {
	// ... existing tools
	MY_NEW_TOOL = "my_new_tool",
}

2. Create Tool Specification File

Create a new file in src/core/prompts/system-prompt/tools/ following the naming convention {tool_name}.ts:

typescript
// src/core/prompts/system-prompt/tools/my_new_tool.ts
import { ModelFamily } from "@/shared/prompts"
import { ClineDefaultTool } from "@/shared/tools"
import type { ClineToolSpec } from "../spec"

const id = ClineDefaultTool.MY_NEW_TOOL

const generic: ClineToolSpec = {
	variant: ModelFamily.GENERIC,
	id,
	name: "my_new_tool",
	description: "Comprehensive description of the tool's purpose, when to use it, and what it accomplishes. Be specific about use cases and limitations.",
	parameters: [
		{
			name: "input_parameter",
			required: true,
			instruction: "Clear instruction on what this parameter expects and how to format it",
			usage: "Example input here",
		},
		{
			name: "options",
			required: false,
			instruction: "Optional configuration or settings for the tool",
			usage: "Configuration options (optional)",
		},
	],
}

// Export variants array - this is crucial for registration
export const my_new_tool_variants = [generic]

3. Export Tool from Index

Add your tool export to the tools index file:

typescript
// src/core/prompts/system-prompt/tools/index.ts
export * from "./my_new_tool"

4. Register Tool in Init File

Add your tool to the registration function:

typescript
// src/core/prompts/system-prompt/tools/init.ts
import { my_new_tool_variants } from "./my_new_tool"

export function registerClineToolSets(): void {
	const allToolVariants = [
		// ... existing tool variants
		...my_new_tool_variants,
	]

	allToolVariants.forEach((v) => {
		ClineToolSet.register(v)
	})
}

5. Implement Tool Handler (Backend)

Create the actual tool implementation in the appropriate handler:

typescript
// In your tool handler class (e.g., ClineProvider)
async handleMyNewTool(args: { input_parameter: string; options?: string }) {
	// Implement your tool logic here
	const result = await performToolOperation(args.input_parameter, args.options)
	
	return {
		type: "tool_result" as const,
		content: result,
	}
}

Advanced Tool Configuration

Context-Aware Tools

Tools can be conditionally enabled based on context:

typescript
const contextAwareTool: ClineToolSpec = {
	variant: ModelFamily.GENERIC,
	id: ClineDefaultTool.CONTEXT_TOOL,
	name: "context_tool",
	description: "Tool that only appears in certain contexts",
	contextRequirements: (context: SystemPromptContext) => {
		// Only show this tool if browser support is available
		return context.supportsBrowserUse === true
	},
	parameters: [
		// ... parameters
	],
}

Model-Specific Variants

Create different tool behaviors for different model families:

typescript
const claude: ClineToolSpec = {
	variant: ModelFamily.GENERIC,
	id: ClineDefaultTool.MODEL_SPECIFIC_TOOL,
	name: "model_specific_tool",
	description: "Tool optimized for Claude models with detailed instructions",
	parameters: [
		{
			name: "detailed_input",
			required: true,
			instruction: "Provide comprehensive details as Claude handles complex instructions well",
			usage: "Detailed input with context and examples",
		},
	],
}

const gpt: ClineToolSpec = {
	...claude,
	variant: ModelFamily.GPT,
	description: "Tool optimized for GPT models with concise instructions",
	parameters: [
		{
			name: "detailed_input",
			required: true,
			instruction: "Provide concise, structured input",
			usage: "Brief, structured input",
		},
	],
}

export const model_specific_tool_variants = [claude, gpt]

Parameter Dependencies

Tools can have parameters that only appear when other tools are available:

typescript
const dependentTool: ClineToolSpec = {
	variant: ModelFamily.GENERIC,
	id: ClineDefaultTool.DEPENDENT_TOOL,
	name: "dependent_tool",
	description: "Tool with conditional parameters",
	parameters: [
		{
			name: "always_present",
			required: true,
			instruction: "This parameter is always available",
			usage: "Standard input",
		},
		{
			name: "conditional_param",
			required: false,
			instruction: "This parameter only appears if TODO tool is available",
			usage: "Conditional input (optional)",
			dependencies: [ClineDefaultTool.TODO],
		},
	],
}

Best Practices

1. Tool Naming Conventions

  • Use snake_case for tool IDs and file names
  • Use descriptive names that clearly indicate the tool's purpose
  • Prefix with action verb when appropriate (e.g., create_file, search_code)

2. Parameter Design

  • Always provide clear, actionable instructions
  • Include usage examples that show expected format
  • Mark parameters as required/optional appropriately
  • Use dependencies to avoid cluttering the prompt with irrelevant parameters

3. Description Guidelines

  • Be specific about when and why to use the tool
  • Include limitations and constraints
  • Mention any prerequisites or setup requirements
  • Provide context about expected outcomes

4. Model Variant Strategy

  • Start with a GENERIC variant that works across all models
  • Create specific variants only when models need different instructions
  • Keep variant differences minimal and focused on instruction style
  • Test across different model families to ensure compatibility

5. Error Handling

  • Design tools to fail gracefully
  • Provide meaningful error messages
  • Consider edge cases in parameter validation
  • Document expected error scenarios

Testing Your New Tool

1. Unit Tests

Create unit tests for your tool specification:

typescript
// src/core/prompts/system-prompt/tools/__tests__/my_new_tool.test.ts
import { my_new_tool_variants } from "../my_new_tool"
import { ModelFamily } from "@/shared/prompts"

describe("my_new_tool", () => {
	it("should have correct structure", () => {
		const generic = my_new_tool_variants.find(v => v.variant === ModelFamily.GENERIC)
		expect(generic).toBeDefined()
		expect(generic?.name).toBe("my_new_tool")
		expect(generic?.parameters).toHaveLength(2)
	})
})

2. Integration Tests

Add your tool to the integration test suite:

typescript
// src/core/prompts/system-prompt/__tests__/integration.test.ts
// The test will automatically pick up your tool if properly registered

3. Manual Testing

  1. Run the unit tests: npm run test:unit
  2. Start the application and verify your tool appears in the system prompt
  3. Test tool execution with various parameter combinations
  4. Verify tool works across different model families

Complete Example: File Analyzer Tool

Here's a complete example of adding a new "analyze_file" tool:

typescript
// 1. Add to src/shared/tools.ts
export enum ClineDefaultTool {
	// ... existing tools
	ANALYZE_FILE = "analyze_file",
}

// 2. Create src/core/prompts/system-prompt/tools/analyze_file.ts
import { ModelFamily } from "@/shared/prompts"
import { ClineDefaultTool } from "@/shared/tools"
import type { ClineToolSpec } from "../spec"

const id = ClineDefaultTool.ANALYZE_FILE

const generic: ClineToolSpec = {
	variant: ModelFamily.GENERIC,
	id,
	name: "analyze_file",
	description: "Analyze a file's structure, dependencies, and potential issues. Use this when you need to understand a file's architecture, identify problems, or assess code quality before making changes.",
	parameters: [
		{
			name: "file_path",
			required: true,
			instruction: "The path to the file you want to analyze (relative to current working directory)",
			usage: "src/components/MyComponent.tsx",
		},
		{
			name: "analysis_type",
			required: false,
			instruction: "Type of analysis to perform: 'structure', 'dependencies', 'quality', or 'all'",
			usage: "all (optional)",
		},
		{
			name: "include_suggestions",
			required: false,
			instruction: "Whether to include improvement suggestions in the analysis",
			usage: "true (optional)",
		},
	],
}

const nextGen: ClineToolSpec = {
	...generic,
	variant: ModelFamily.NEXT_GEN,
	description: "Perform comprehensive file analysis including structure, dependencies, code quality, and improvement suggestions. Ideal for code review and refactoring planning.",
}

export const analyze_file_variants = [generic, nextGen]

// 3. Add to src/core/prompts/system-prompt/tools/index.ts
export * from "./analyze_file"

// 4. Add to src/core/prompts/system-prompt/tools/init.ts
import { analyze_file_variants } from "./analyze_file"

export function registerClineToolSets(): void {
	const allToolVariants = [
		// ... existing variants
		...analyze_file_variants,
	]
	// ... rest of function
}

This comprehensive guide should help developers understand both the architecture and practical steps needed to extend Cline with new tools.

Key Features

  • Modular Components: Reusable across different model variants
  • Template System: {{placeholder}} support with runtime resolution
  • Versioning: Full version control with tags and labels
  • Model Family Detection: Automatic model family detection and fallback
  • Flexible Tool Configuration: Per-variant tool selection and customization
  • Component Overrides: Custom templates for specific components
  • Runtime Placeholders: Dynamic value injection at build time
  • Performance Optimized: Efficient component building and template resolution
  • Error Handling: Graceful degradation when components fail
  • Conditional Logic: Context-aware tool and component inclusion