claude-code/spec/03_tools.md
2026-04-01 01:20:27 +05:30

2171 lines
65 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Claude Code — Tools System
## Table of Contents
1. [Tool Framework (Tool.ts)](#1-tool-framework)
2. [Tool Registry (tools.ts)](#2-tool-registry)
3. [Task Framework (Task.ts / tasks.ts)](#3-task-framework)
4. [Core File Tools](#4-core-file-tools)
- [FileReadTool](#41-filereadtool)
- [FileWriteTool](#42-filewritetool)
- [FileEditTool](#43-fileedittool)
5. [Shell Execution Tools](#5-shell-execution-tools)
- [BashTool](#51-bashtool)
- [PowerShellTool](#52-powershelltool)
6. [Search Tools](#6-search-tools)
- [GlobTool](#61-globtool)
- [GrepTool](#62-greptool)
7. [Agent / Multi-Agent Tools](#7-agent--multi-agent-tools)
- [AgentTool](#71-agenttool)
- [TeamCreateTool](#72-teamcreatetool)
- [TeamDeleteTool](#73-teamdeletetool)
- [SendMessageTool](#74-sendmessagetool)
8. [Task Management Tools](#8-task-management-tools)
- [TaskStopTool](#81-taskstoptool)
- [TaskOutputTool](#82-taskoutputtool)
- [TodoWriteTool (V1)](#83-todowritetool-v1)
- [TaskCreateTool (V2)](#84-taskcreatetool-v2)
- [TaskGetTool (V2)](#85-taskgettool-v2)
- [TaskUpdateTool (V2)](#86-taskunpdatetool-v2)
- [TaskListTool (V2)](#87-tasklisttool-v2)
9. [Web Tools](#9-web-tools)
- [WebFetchTool](#91-webfetchtool)
- [WebSearchTool](#92-websearchtool)
10. [MCP Integration Tools](#10-mcp-integration-tools)
- [MCPTool](#101-mcptool)
- [McpAuthTool](#102-mcpauthtool)
- [ListMcpResourcesTool](#103-listmcpresourcestool)
- [ReadMcpResourceTool](#104-readmcpresourcetool)
11. [Plan Mode Tools](#11-plan-mode-tools)
- [EnterPlanModeTool](#111-enterplanmodetool)
- [ExitPlanModeV2Tool](#112-exitplanmodev2tool)
12. [Notebook Tool](#12-notebook-tool)
13. [Worktree Tools](#13-worktree-tools)
- [EnterWorktreeTool](#131-enterworkreetool)
- [ExitWorktreeTool](#132-exitworkreetool)
14. [Scheduling Tools](#14-scheduling-tools)
- [CronCreateTool](#141-croncreatetool)
- [CronDeleteTool](#142-crondeletetool)
- [CronListTool](#143-cronlisttool)
15. [Meta / Discovery Tools](#15-meta--discovery-tools)
- [ToolSearchTool](#151-toolsearchtool)
- [AskUserQuestionTool](#152-askuserquestiontool)
16. [Kairos / Special Mode Tools](#16-kairos--special-mode-tools)
- [BriefTool (SendUserMessage)](#161-brieftool-senduserrmessage)
- [SleepTool](#162-sleeptool)
- [RemoteTriggerTool](#163-remotetriggertool)
17. [SDK / Output Tools](#17-sdk--output-tools)
- [SyntheticOutputTool (StructuredOutput)](#171-syntheticoutputtool-structuredoutput)
18. [Skill Tool](#18-skill-tool)
19. [LSP Tool](#19-lsp-tool)
20. [REPL Tool](#20-repl-tool)
21. [Config Tool](#21-config-tool)
22. [Shared Utilities](#22-shared-utilities)
- [tools/utils.ts](#221-toolsutilsts)
- [tools/shared/gitOperationTracking.ts](#222-toolssharedgitoperationtrackingts)
- [tools/shared/spawnMultiAgent.ts](#223-toolssharedspawnmultiagentts)
23. [Testing Utilities](#23-testing-utilities)
---
## 1. Tool Framework
**Source:** `src/Tool.ts`
### 1.1 Core Interface
```typescript
export type Tool<Input extends ZodType = ZodType, Output = unknown, Progress = unknown> = {
// Identity
name: string
isMcp?: boolean
mcpInfo?: { serverName: string; toolName: string }
isLsp?: boolean
alwaysLoad?: boolean
shouldDefer?: boolean
// Schema (getter properties for lazy init)
readonly inputSchema: Input
readonly outputSchema?: ZodType<Output>
// Metadata
description(): Promise<string>
prompt(): Promise<string>
userFacingName(input?: z.infer<Input>): string
maxResultSizeChars?: number
searchHint?: string
// Capability flags (accept input for per-call decisions)
isEnabled(permissionContext?: ToolPermissionContext): boolean
isConcurrencySafe(input?: z.infer<Input>): boolean
isReadOnly(input?: z.infer<Input>): boolean
isDestructive?(input: z.infer<Input>): boolean
toAutoClassifierInput(input: z.infer<Input>): string
isSearchOrReadCommand?: (input: z.infer<Input>) => { isSearch: boolean; isRead: boolean }
// Execution
validateInput?(input: z.infer<Input>): Promise<ValidationResult>
checkPermissions(input: z.infer<Input>, context: ToolUseContext): Promise<PermissionDecision>
call(input: z.infer<Input>, context: ToolUseContext): Promise<ToolResult<Output>>
// UI rendering (React / Ink)
renderToolUseMessage(input: z.infer<Input>, options: RenderOptions): ReactNode | null
renderToolUseProgressMessage?(progress: Progress, input?: z.infer<Input>): ReactNode | null
renderToolUseQueuedMessage?(input: z.infer<Input>): ReactNode | null
renderToolUseRejectedMessage?(input: z.infer<Input>, ...): ReactNode | null
renderToolResultMessage(output: Output, ...): ReactNode | null
renderToolUseErrorMessage?(error: Error, ...): ReactNode | null
// Output mapping
mapToolResultToToolResultBlockParam(
output: Output,
toolUseID: string,
context: ToolUseContext,
): ToolResultBlockParam
// Path tracking (for permission rules)
getPath?(input?: z.infer<Input>): string | undefined
}
```
### 1.2 ToolDef — Definition Shape
`ToolDef<Input, Output, Progress>` is the shape passed to `buildTool()`. It has the same fields as `Tool` minus the defaults filled in by `buildTool()`.
### 1.3 buildTool()
```typescript
function buildTool<Input, Output, Progress>(def: ToolDef<Input, Output, Progress>): Tool<...>
```
Fills in safe defaults:
- `isEnabled``() => true`
- `isConcurrencySafe``() => false`
- `isReadOnly``() => false`
- `checkPermissions``async () => ({ behavior: 'allow', updatedInput: input })`
- `toAutoClassifierInput``() => ''`
- `userFacingName``() => def.name`
### 1.4 ToolUseContext
The context object passed to every `call()` and `checkPermissions()`:
```typescript
type ToolUseContext = {
// Configuration
options: {
commands: Command[]
tools: Tool[]
mcpClients: MCPClient[]
mainLoopModel: string
thinkingConfig: ThinkingConfig
// ... additional options
}
// Abort signal
abortController: AbortController
// App state accessors
getAppState(): AppState
setAppState(fn: (prev: AppState) => AppState): void
// File read tracking (for read-before-write enforcement)
readFileState: Map<string, { mtime: number; content: string }>
// Permission context
permissionContext: ToolPermissionContext
// UI injection
setToolJSX: SetToolJSXFn
// Callbacks
onPermissionRequest(request: PermissionRequest): Promise<PermissionDecision>
onToolCallStart(toolName: string, input: unknown): void
onToolCallEnd(toolName: string, result: unknown): void
// Agent/teammate context
agentId?: AgentId
isSubagent?: boolean
isCoordinator?: boolean
// Additional fields for specific tool categories
globLimits?: { maxResults: number }
}
```
### 1.5 ToolPermissionContext
```typescript
type ToolPermissionContext = {
mode: PermissionMode // 'default' | 'plan' | 'auto' | 'bypassPermissions' | 'acceptEdits'
alwaysAllow: PermissionRule[]
alwaysDeny: PermissionRule[]
alwaysAsk: PermissionRule[]
additionalWorkingDirectories: string[]
toolPermissions: Record<string, ToolPermissionOverride>
}
```
### 1.6 PermissionResult / PermissionDecision
```typescript
type PermissionDecision =
| { behavior: 'allow'; updatedInput: Input }
| { behavior: 'ask'; message: string; decisionReason?: string }
| { behavior: 'deny'; message: string }
| { behavior: 'passthrough' } // Always asks user
type ValidationResult =
| { result: true }
| { result: false; message: string; errorCode?: number }
```
### 1.7 ToolResult
```typescript
type ToolResult<T> = { data: T }
```
### 1.8 Tools Type Alias & Helpers
```typescript
type Tools = Tool[]
function findToolByName(tools: Tools, name: string): Tool | undefined
function toolMatchesName(tool: Tool, name: string): boolean
```
---
## 2. Tool Registry
**Source:** `src/tools.ts`
### 2.1 getAllBaseTools()
Returns the full ordered list of built-in tools. Order must stay in sync with Statsig caching config.
```typescript
function getAllBaseTools(): Tool[]
```
Includes (conditionally):
- Always: BashTool, GlobTool, GrepTool, FileReadTool, FileEditTool, FileWriteTool, AgentTool, WebFetchTool, WebSearchTool, NotebookEditTool, TodoWriteTool, TaskStopTool, AskUserQuestionTool, SkillTool, MCPTool, EnterPlanModeTool, ExitPlanModeV2Tool, ToolSearchTool, TaskCreateTool, TaskGetTool, TaskUpdateTool, TaskListTool, TeamCreateTool, TeamDeleteTool, SendMessageTool, TaskOutputTool, SyntheticOutputTool, EnterWorktreeTool, ExitWorktreeTool, BriefTool, RemoteTriggerTool
- Ant-only: ConfigTool, TungstenTool, REPLTool
- Feature-gated (`feature('KAIROS')` + `isKairosCronEnabled()`): CronCreateTool, CronDeleteTool, CronListTool
- Feature-gated (`feature('AGENT_TRIGGERS')`): RemoteTriggerTool
- Feature-gated (`feature('SLEEP_TOOL')`): SleepTool
- Feature-gated (`feature('MONITOR_TOOL')`): MonitorMcpTask
- LSP enabled: LSPTool
### 2.2 getTools(permissionContext)
```typescript
function getTools(permissionContext: ToolPermissionContext): Tool[]
```
- If `CLAUDE_CODE_SIMPLE` env var is set: returns only `[BashTool, FileReadTool, FileEditTool]`
- Calls `getAllBaseTools()`, filters with `filterToolsByDenyRules()`
- In REPL mode: hides `REPL_ONLY_TOOLS` (Bash, Read, Write, Edit, Glob, Grep, NotebookEdit, Agent)
### 2.3 assembleToolPool()
```typescript
function assembleToolPool(
baseTools: Tool[],
mcpTools: Tool[],
): Tool[]
```
- Combines built-in + MCP tools
- Sorts by name for prompt-cache stability
- Deduplicates (built-in tools win over MCP tools with same name)
### 2.4 filterToolsByDenyRules()
```typescript
function filterToolsByDenyRules(
tools: Tool[],
permissionContext: ToolPermissionContext,
): Tool[]
```
Removes tools whose name matches a blanket deny rule in permissionContext.
### 2.5 Tool Presets
```typescript
const TOOL_PRESETS = {
'full': /* all tools */,
'minimal': /* BashTool, FileReadTool, FileEditTool */,
// ...
}
function parseToolPreset(preset: string): Tool[] | null
function getToolsForDefaultPreset(): Tool[]
function getMergedTools(base: Tool[], overrides: Tool[]): Tool[]
```
---
## 3. Task Framework
**Sources:** `src/Task.ts`, `src/tasks.ts`
### 3.1 TaskType
```typescript
type TaskType =
| 'local_bash' // prefix: 'b'
| 'local_agent' // prefix: 'a'
| 'remote_agent' // prefix: 'r'
| 'in_process_teammate' // prefix: 't'
| 'local_workflow' // prefix: 'w'
| 'monitor_mcp' // prefix: 'm'
| 'dream' // prefix: 'd'
```
### 3.2 TaskStatus
```typescript
type TaskStatus = 'pending' | 'running' | 'completed' | 'failed' | 'killed'
function isTerminalTaskStatus(status: TaskStatus): boolean
// Returns true for 'completed', 'failed', 'killed'
```
### 3.3 TaskStateBase
```typescript
type TaskStateBase = {
id: string // prefix + 8 random base-36 chars
type: TaskType
status: TaskStatus
description: string
toolUseId?: string
startTime: number // Date.now()
endTime?: number
totalPausedMs?: number
outputFile: string // getTaskOutputPath(id)
outputOffset: number
notified: boolean
}
```
### 3.4 ID Generation
```typescript
// Alphabet: '0123456789abcdefghijklmnopqrstuvwxyz'
// 36^8 ≈ 2.8 trillion combinations
function generateTaskId(type: TaskType): string
// Returns: prefix + 8 crypto-random base-36 chars
function createTaskStateBase(
id: string,
type: TaskType,
description: string,
toolUseId?: string,
): TaskStateBase
```
### 3.5 Task Interface
```typescript
type Task = {
name: string
type: TaskType
kill(taskId: string, setAppState: SetAppState): Promise<void>
}
```
### 3.6 Task Registry
```typescript
// src/tasks.ts
function getAllTasks(): Task[]
// Returns: [LocalShellTask, LocalAgentTask, RemoteAgentTask, DreamTask,
// optionally LocalWorkflowTask, MonitorMcpTask]
function getTaskByType(type: TaskType): Task | undefined
```
---
## 4. Core File Tools
### 4.1 FileReadTool
**Tool name:** `Read`
**Source:** `src/tools/FileReadTool/FileReadTool.ts`
**Characteristics:**
- `isConcurrencySafe: true`
- `isReadOnly: true`
- `maxResultSizeChars: Infinity` (prevents circular reads through disk persistence)
- `strict: true`
- `searchHint: 'read files, images, PDFs, notebooks'`
- `isSearchOrReadCommand: { isSearch: false, isRead: true }`
**Input Schema:**
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `file_path` | `string` | Yes | Absolute path to file |
| `offset` | `integer` | No | Line number to start reading from |
| `limit` | `integer` | No | Number of lines to read |
| `pages` | `string` | No | PDF page range (e.g. `"1-5"`, `"3"`, `"10-20"`); PDF only; max 20 pages per request |
**Output Schema (discriminated union on `type`):**
```typescript
// Text file
{ type: 'text'; content: string; numLines: number; startLine: number; totalLines: number }
// Image file
{ type: 'image'; base64: string; mimeType: string; originalSize: number; dimensions: { width: number; height: number } }
// Jupyter notebook
{ type: 'notebook'; cells: NotebookCell[] }
// PDF (full)
{ type: 'pdf'; base64: string; originalSize: number }
// PDF (extracted pages)
{ type: 'parts'; pages: PDFPage[] }
// Unchanged (content not modified since last read)
{ type: 'file_unchanged' }
```
**Security / Validation:**
- Blocked device paths: `/dev/zero`, `/dev/random`, `/dev/urandom`, `/dev/full`, `/dev/stdin`, `/dev/tty`, and other infinite-stream devices
- Registers file in `readFileState` cache (path → `{mtime, content}`) enabling FileEditTool/FileWriteTool read-before-write enforcement
- UNC path handling skipped on Windows
**Exports:**
```typescript
function registerFileReadListener(listener: FileReadListener): void
class MaxFileReadTokenExceededError extends Error
```
---
### 4.2 FileWriteTool
**Tool name:** `Write`
**Source:** `src/tools/FileWriteTool/FileWriteTool.ts`
**Characteristics:**
- `strict: true`
- `maxResultSizeChars: 100_000`
- `searchHint: 'create or overwrite files'`
**Input Schema:**
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `file_path` | `string` | Yes | Absolute path to file |
| `content` | `string` | Yes | Full file content to write |
**Output Schema:**
```typescript
{
type: 'create' | 'update'
filePath: string
content: string
structuredPatch: StructuredPatch
originalFile: string | null // null for new files
gitDiff?: string // Optional git diff of changes
}
```
**Validation / Safety:**
- **Read-before-write enforcement:** For existing files, file must appear in `readFileState` cache (must have been read with FileReadTool)
- **mtime staleness check:** If file mtime has changed since last read, refuses to overwrite to prevent clobbering concurrent changes
- **File size limit:** Max 1 GiB
- **UNC path security:** Skips read-check for UNC paths (`\\server\share`) on Windows
- **`.ipynb` files:** Redirected to `NotebookEditTool`
- **Team memory protection:** Blocks writes to team memory secret files
- **Deny rules:** Checks permission context deny rules
- **LF line endings:** New content uses LF regardless of platform
- Notifies LSP client on successful edit
---
### 4.3 FileEditTool
**Tool name:** `Edit`
**Source:** `src/tools/FileEditTool/FileEditTool.ts`
**Characteristics:**
- `strict: true`
- `maxResultSizeChars: 100_000`
- `searchHint: 'modify file contents in place'`
**Input Schema:**
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `file_path` | `string` | Yes | Absolute path to file to edit |
| `old_string` | `string` | Yes | Text to search for (must exist exactly once unless `replace_all` is true) |
| `new_string` | `string` | Yes | Replacement text (must differ from `old_string`) |
| `replace_all` | `boolean` | No (default `false`) | Replace all occurrences of `old_string` |
**Output Schema:**
```typescript
{
filePath: string
oldString: string
newString: string
originalFile: string // File content before edit
structuredPatch: StructuredPatch
userModified: boolean // Whether user modified the proposed diff
replaceAll: boolean
gitDiff?: string
}
```
**Validation / Safety:**
- `old_string` must differ from `new_string`
- File must have been read via `readFileState` (read-before-write enforcement)
- mtime staleness check (same as FileWriteTool)
- `old_string` must be found in file content
- Unless `replace_all` is true, at most 1 occurrence of `old_string` is allowed
- File size: max 1 GiB
- UNC path security skip
- `.ipynb` files redirected to `NotebookEditTool`
- Team memory secret guard
- Permission context deny rule check
- Uses `findActualString()` for quote normalization (handles straight/curly quotes interchangeably)
- Uses `preserveQuoteStyle()` to maintain original quote style
- Notifies LSP client on successful edit
---
## 5. Shell Execution Tools
### 5.1 BashTool
**Tool name:** `Bash`
**Source:** `src/tools/BashTool/BashTool.tsx`
**Characteristics:**
- `maxResultSizeChars` (varies by output type)
- `searchHint` (from prompt)
- Supports background task execution
- Supports sandboxing (bwrap on Linux, sandbox-exec on macOS)
**Input Schema:**
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `command` | `string` | Yes | Shell command to execute |
| `timeout` | `number` | No | Timeout in milliseconds (max varies by context) |
| `description` | `string` | No | Human-readable description of what the command does |
| `run_in_background` | `boolean` | No | Launch as background task; omitted from schema when `CLAUDE_CODE_DISABLE_BACKGROUND_TASKS=true` |
| `dangerouslyDisableSandbox` | `boolean` | No | Override sandbox mode |
| `_simulatedSedEdit` | internal | — | Never in model-facing schema; used for sed edit simulation |
**Output Schema:**
```typescript
{
stdout: string
stderr: string
interrupted: boolean
isImage?: boolean // stdout contains base64 image data
backgroundTaskId?: string // Set when run_in_background=true
backgroundedByUser?: boolean // User pressed Ctrl+B
assistantAutoBackgrounded?: boolean // Auto-backgrounded after blocking budget
dangerouslyDisableSandbox?: boolean
returnCodeInterpretation?: string // Semantic meaning of exit code
noOutputExpected?: boolean
structuredContent?: unknown
persistedOutputPath?: string // Path when output too large for inline
persistedOutputSize?: number // Total bytes when persisted
}
```
**Timing Constants:**
- Progress threshold: `2_000ms` (show progress spinner after 2s)
- Auto-background threshold: `120_000ms` (auto-background after 2 minutes)
- `ASSISTANT_BLOCKING_BUDGET_MS`: `15_000ms` (in assistant/Kairos mode, auto-background after 15s in main agent)
**Permission Behavior:**
- Checks permission rules against the command string
- `bypassPermissions` mode: allows all commands
- `auto`/`acceptEdits` mode: allows read-only bash commands without asking; asks for write commands
- `default` mode: always asks unless explicitly allowed
**Sandbox:**
- Linux: `bwrap` (bubblewrap) based isolation
- macOS: `sandbox-exec` based isolation
- Windows: no sandbox (bwrap/sandbox-exec are POSIX-only)
- `dangerouslyDisableSandbox` overrides per-call
- Enterprise policy: if sandbox required and unavailable, execution is blocked
**Blocked patterns:**
- `detectBlockedSleepPattern()`: detects bare `sleep N` commands with N>=2 as first statement; suggests using SleepTool instead
**Git operation tracking:**
- `trackGitOperations()` called after each bash command to fire analytics for commits, pushes, PR creation
- `isSearchOrReadBashCommand()`: classifies command for UI collapsing
**Exports:**
```typescript
function isSearchOrReadBashCommand(command: string): { isSearch: boolean; isRead: boolean }
function detectBlockedSleepPattern(command: string): string | null
type BashToolInput = { command: string; timeout?: number; description?: string; run_in_background?: boolean; dangerouslyDisableSandbox?: boolean }
```
---
### 5.2 PowerShellTool
**Tool name:** `PowerShell`
**Source:** `src/tools/PowerShellTool/PowerShellTool.tsx`
Windows-native PowerShell execution tool, mirroring BashTool's interface.
**Characteristics:**
- Windows-specific; requires PowerShell (`pwsh`) to be installed
- Detects PowerShell path via `getCachedPowerShellPath()`
- Tracks git operations via shared `trackGitOperations()`
- Same sandbox policy as BashTool (POSIX sandbox applies on Linux/macOS when running `pwsh`)
- `PROGRESS_THRESHOLD_MS: 2_000ms`
- `ASSISTANT_BLOCKING_BUDGET_MS: 15_000ms`
**Input Schema:**
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `command` | `string` | Yes | PowerShell command to execute |
| `timeout` | `number` | No | Optional timeout in milliseconds (`max getMaxTimeoutMs()`) |
| `description` | `string` | No | Description of what the command does |
| `run_in_background` | `boolean` | No | Background execution; omitted when `CLAUDE_CODE_DISABLE_BACKGROUND_TASKS=true` |
| `dangerouslyDisableSandbox` | `boolean` | No | Override sandbox mode |
**Output Schema:**
```typescript
{
stdout: string
stderr: string
interrupted: boolean
returnCodeInterpretation?: string
isImage?: boolean
persistedOutputPath?: string
persistedOutputSize?: number
backgroundTaskId?: string
backgroundedByUser?: boolean
assistantAutoBackgrounded?: boolean
}
```
**PowerShell-specific features:**
- `PS_SEARCH_COMMANDS`: `Select-String`, `Get-ChildItem`, `FindStr`, `where.exe` (grep/find equivalents)
- `PS_READ_COMMANDS`: `Get-Content`, `Get-Item`, `Test-Path`, `Resolve-Path`, `Get-Process`, `Get-Service`, `Get-ChildItem`, `Get-Location`, `Get-FileHash`, `Get-Acl`, `Format-Hex`
- `PS_SEMANTIC_NEUTRAL_COMMANDS`: `Write-Output`, `Write-Host`
- `detectBlockedSleepPattern()`: catches `Start-Sleep N`, `Start-Sleep -Seconds N`, `sleep N` as first statement
- `DISALLOWED_AUTO_BACKGROUND_COMMANDS`: `['start-sleep', 'sleep']` (not auto-backgrounded)
- Windows-native sandbox policy: if enterprise requires sandbox but Windows native, execution blocked
**Exports:**
```typescript
export type PowerShellToolInput
function detectBlockedSleepPattern(command: string): string | null
```
---
## 6. Search Tools
### 6.1 GlobTool
**Tool name:** `Glob`
**Source:** `src/tools/GlobTool/GlobTool.ts`
**Characteristics:**
- `isConcurrencySafe: true`
- `isReadOnly: true`
- `searchHint: 'find files by name pattern or wildcard'`
- `isSearchOrReadCommand: { isSearch: true, isRead: false }`
**Input Schema:**
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `pattern` | `string` | Yes | Glob pattern (e.g. `"**/*.js"`, `"src/**/*.ts"`) |
| `path` | `string` | No | Directory to search in (defaults to cwd) |
**Output Schema:**
```typescript
{
filenames: string[] // Relative to cwd
durationMs: number
numFiles: number
truncated: boolean // True if results were truncated
}
```
**Behavior:**
- Default limit: 100 files (overridable via `context.globLimits?.maxResults`)
- Results are sorted by modification time (most recent first)
- Paths relativized to cwd for compactness
---
### 6.2 GrepTool
**Tool name:** `Grep`
**Source:** `src/tools/GrepTool/GrepTool.ts`
**Characteristics:**
- `isConcurrencySafe: true`
- `isReadOnly: true`
- `strict: true`
- `maxResultSizeChars: 20_000`
- `searchHint: 'search file contents with regex (ripgrep)'`
- `isSearchOrReadCommand: { isSearch: true }`
**Input Schema:**
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `pattern` | `string` | Yes | Regular expression pattern |
| `path` | `string` | No | File or directory to search |
| `glob` | `string` | No | Glob filter (e.g. `"*.js"`, `"**/*.tsx"`) |
| `output_mode` | `'content' \| 'files_with_matches' \| 'count'` | No (default `'files_with_matches'`) | Output format |
| `-B` | `number` | No | Lines before each match (requires `output_mode: 'content'`) |
| `-A` | `number` | No | Lines after each match (requires `output_mode: 'content'`) |
| `-C` | `number` | No | Lines before and after each match |
| `context` | `number` | No | Alias for `-C` |
| `-n` | `boolean` | No | Show line numbers (requires `output_mode: 'content'`) |
| `-i` | `boolean` | No | Case-insensitive search |
| `type` | `string` | No | File type filter (e.g. `"js"`, `"py"`) |
| `head_limit` | `number` | No (default `250`) | Limit output to first N lines/entries |
| `offset` | `number` | No (default `0`) | Skip first N entries |
| `multiline` | `boolean` | No (default `false`) | Enable multiline matching (`.` matches newlines) |
**Output Schema:**
```typescript
{
mode: 'content' | 'files_with_matches' | 'count'
numFiles: number
filenames: string[]
content?: string // When mode='content'
numLines?: number // When mode='content'
numMatches?: number // When mode='count'
appliedLimit?: number
appliedOffset?: number
}
```
**Implementation Details:**
- Backed by `ripgrep` (`rg`) binary
- Excludes VCS directories: `.git`, `.svn`, `.hg`, `.bzr`, `.jj`, `.sl`
- `max-columns: 500` to prevent oversized lines
- Default `head_limit: 250` when unspecified
---
## 7. Agent / Multi-Agent Tools
### 7.1 AgentTool
**Tool name:** `Agent` (alias: `Task`)
**Constants:** `AGENT_TOOL_NAME`, `LEGACY_AGENT_TOOL_NAME`
**Source:** `src/tools/AgentTool/AgentTool.tsx`
**Input Schema:**
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `description` | `string` | Yes | 3-5 word description of the task |
| `prompt` | `string` | Yes | Full task prompt for the agent |
| `subagent_type` | `string` | No | Type of subagent to spawn |
| `model` | `'sonnet' \| 'opus' \| 'haiku'` | No | Model alias for the agent |
| `run_in_background` | `boolean` | No | Launch as background task |
| `name` | `string` | No | Named agent for messaging |
| `team_name` | `string` | No | Associate with this team |
| `mode` | `string` | No | Permission mode override |
| `isolation` | `'worktree' \| 'remote'` | No | Isolation strategy |
| `cwd` | `string` | No | Working directory (Kairos only) |
**Output Schema:**
Synchronous completion:
```typescript
{
status: 'completed'
result: string
}
```
Asynchronous launch:
```typescript
{
status: 'async_launched'
agentId: string
description: string
prompt: string
}
```
**Behavior:**
- Auto-backgrounds after `120_000ms`
- Progress shown after `2_000ms`
- Supports fork subagent (`subagent_type: 'fork'`)
- Multi-agent swarm integration: when inside a team, can spawn named teammates
- `isolation: 'worktree'` creates git worktree for isolated execution
- `isolation: 'remote'` runs in remote session
---
### 7.2 TeamCreateTool
**Tool name:** `TeamCreate`
**Source:** `src/tools/TeamCreateTool/TeamCreateTool.ts`
**Gate:** `isAgentSwarmsEnabled()`
**Input Schema:**
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `team_name` | `string` | Yes | Name for the team |
| `description` | `string` | No | Team description |
| `agent_type` | `string` | No | Default agent type for team members |
**Output Schema:**
```typescript
{
team_name: string
team_file_path: string
lead_agent_id: string
}
```
**Behavior:**
- Creates team file at `~/.claude/teams/<team_name>.json`
- Resets task list to team-scoped task list
- Registers team for session cleanup (auto-cleanup on exit)
- One team per leader enforced: calling again while a team exists returns an error
---
### 7.3 TeamDeleteTool
**Tool name:** `TeamDelete`
**Source:** `src/tools/TeamDeleteTool/TeamDeleteTool.ts`
**Gate:** `isAgentSwarmsEnabled()`
**Input:** `{}` (empty object)
**Output Schema:**
```typescript
{
success: boolean
message: string
team_name?: string
}
```
**Behavior:**
- Refuses to delete if any non-lead members have `isActive !== false` (still running)
- Calls `cleanupTeamDirectories(teamName)` to remove team files and worktrees
- Unregisters team from session cleanup
- Clears teammate color assignments
- Clears leader team name (task list falls back to session ID)
- Clears team context and inbox from app state
---
### 7.4 SendMessageTool
**Tool name:** `SendMessage`
**Source:** `src/tools/SendMessageTool/SendMessageTool.ts`
**Gate:** `isAgentSwarmsEnabled()`
**Input Schema:**
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `to` | `string` | Yes | Recipient: agent name, `'*'` (broadcast), `'uds:<path>'`, or `'bridge:<session-id>'` |
| `summary` | `string` | No | Short summary of message for UI |
| `message` | `string \| StructuredMessage` | Yes | Message content |
**StructuredMessage union:**
```typescript
type StructuredMessage =
| { type: 'shutdown_request'; reason?: string }
| { type: 'shutdown_response'; status: 'ok' | 'error'; message?: string }
| { type: 'plan_approval_response'; approved: boolean; comment?: string; requestId: string }
```
**Routing:**
- **In-process agents:** Queues message in agent's inbox or resumes paused agent
- **Mailbox (teammates):** Writes to `~/.claude/mailboxes/<name>.json`
- **UDS socket:** Sends via Unix domain socket (for local inter-process)
- **Bridge (cross-machine):** Routes via Remote Control API; requires user safety check (not auto-approvable)
**Permission:** Bridge messages require user consent via `decisionReason` safety gate.
---
## 8. Task Management Tools
### 8.1 TaskStopTool
**Tool name:** `TaskStop` (alias: `KillShell`)
**Source:** `src/tools/TaskStopTool/TaskStopTool.ts`
**Characteristics:** `shouldDefer: true`, `isConcurrencySafe: true`
**Input Schema:**
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `task_id` | `string` | No | Task ID to stop (from background task launch) |
| `shell_id` | `string` | No | Deprecated alias for `task_id` |
**Output Schema:**
```typescript
{
message: string
task_id: string
task_type: TaskType
command?: string // For bash tasks
}
```
**Validation:**
- Task must exist in app state
- Task must be in a running (non-terminal) state
---
### 8.2 TaskOutputTool
**Tool name:** `TaskOutput` (`TASK_OUTPUT_TOOL_NAME`)
**Source:** `src/tools/TaskOutputTool/TaskOutputTool.tsx`
**Input Schema:**
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `task_id` | `string` | Yes | Task ID to read output from |
| `block` | `boolean` | No (default `true`) | Block until task completes |
| `timeout` | `number` | No (default `30_000`, range `0600_000ms`) | Maximum wait time in ms |
**Output Schema:**
```typescript
{
retrieval_status: 'success' | 'timeout' | 'not_ready'
task: TaskOutput | null
}
type TaskOutput = {
task_id: string
task_type: TaskType
status: TaskStatus
description: string
output: string
exitCode?: number
error?: string
prompt?: string // For agent tasks
result?: string // Final result text for agent tasks
}
```
---
### 8.3 TodoWriteTool (V1)
**Tool name:** `TodoWrite`
**Source:** `src/tools/TodoWriteTool/TodoWriteTool.ts`
**Characteristics:** `strict: true`, `shouldDefer: true`, `maxResultSizeChars: 100_000`
**Gate:** Disabled when `isTodoV2Enabled()`
**Input Schema:**
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `todos` | `TodoItem[]` | Yes | Full replacement list of todo items |
`TodoItem` schema:
```typescript
{
id: string
content: string
status: 'pending' | 'in_progress' | 'completed'
priority: 'high' | 'medium' | 'low'
}
```
**Output Schema:**
```typescript
{
oldTodos: TodoItem[]
newTodos: TodoItem[]
verificationNudgeNeeded?: boolean
}
```
**Behavior:**
- Replaces the entire todo list atomically
- Clears list automatically when all todos are completed
- `verificationNudgeNeeded`: signals UI to nudge model to verify completed items
---
### 8.4 TaskCreateTool (V2)
**Tool name:** `TaskCreate`
**Source:** `src/tools/TaskCreateTool/TaskCreateTool.ts`
**Gate:** `isTodoV2Enabled()`
**Input Schema:**
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `subject` | `string` | Yes | Short task title |
| `description` | `string` | Yes | Detailed task description |
| `activeForm` | `object` | No | Form data for active task |
| `metadata` | `object` | No | Arbitrary metadata |
**Output Schema:**
```typescript
{
task: {
id: string
subject: string
}
}
```
**Behavior:**
- Runs `executeTaskCreatedHooks` after creation
- Auto-expands task panel in UI
- Deletes task if hook throws an error
---
### 8.5 TaskGetTool (V2)
**Tool name:** `TaskGet`
**Source:** `src/tools/TaskGetTool/TaskGetTool.ts`
**Gate:** `isTodoV2Enabled()`
**Characteristics:** `shouldDefer: true`, `isReadOnly: true`
**Input Schema:**
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `taskId` | `string` | Yes | Task ID to retrieve |
**Output Schema:**
```typescript
{
task: {
id: string
subject: string
description: string
status: TaskStatus
blocks: string[] // Task IDs this task blocks
blockedBy: string[] // Task IDs blocking this task
} | null
}
```
---
### 8.6 TaskUpdateTool (V2)
**Tool name:** `TaskUpdate`
**Source:** `src/tools/TaskUpdateTool/TaskUpdateTool.ts`
**Gate:** `isTodoV2Enabled()`
**Input Schema:**
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `taskId` | `string` | Yes | Task ID to update |
| `subject` | `string` | No | Updated title |
| `description` | `string` | No | Updated description |
| `activeForm` | `object` | No | Updated form data |
| `status` | `TaskStatus \| 'deleted'` | No | New status; `'deleted'` removes the task |
| `addBlocks` | `string[]` | No | Task IDs this task should block |
| `addBlockedBy` | `string[]` | No | Task IDs that block this task |
| `owner` | `string` | No | Assign ownership |
| `metadata` | `object` | No | Updated metadata |
**Output Schema:**
```typescript
{
success: boolean
taskId: string
updatedFields: string[]
error?: string
statusChange?: { from: TaskStatus; to: TaskStatus | 'deleted' }
verificationNudgeNeeded?: boolean
}
```
**Behavior:**
- Runs `executeTaskCompletedHooks` when status transitions to `completed`
- Auto-sets owner to calling agent on `in_progress` status
- Writes mailbox notification on owner change
- `verificationNudgeNeeded`: set when 3+ tasks completed without a verification step
---
### 8.7 TaskListTool (V2)
**Tool name:** `TaskList`
**Source:** `src/tools/TaskListTool/TaskListTool.ts`
**Gate:** `isTodoV2Enabled()`
**Characteristics:** `shouldDefer: true`, `isReadOnly: true`
**Input:** `{}` (empty object)
**Output Schema:**
```typescript
{
tasks: Array<{
id: string
subject: string
status: TaskStatus
owner?: string
blockedBy: string[] // Only non-completed blocking tasks
}>
}
```
**Behavior:**
- Filters out tasks with `_internal` metadata flag
- Filters already-completed IDs from `blockedBy` lists
---
## 9. Web Tools
### 9.1 WebFetchTool
**Tool name:** `WebFetch`
**Source:** `src/tools/WebFetchTool/WebFetchTool.ts`
**Characteristics:** `shouldDefer: true`, `maxResultSizeChars: 100_000`
**Input Schema:**
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `url` | `string` (URL) | Yes | URL to fetch |
| `prompt` | `string` | Yes | Instruction for summarizing the fetched content |
**Output Schema:**
```typescript
{
bytes: number
code: number // HTTP status code
codeText: string
result: string // Processed/summarized content
durationMs: number
url: string
}
```
**Permission:** Per-hostname rules. Preapproved hosts bypass prompt. Rule format: `domain:hostname`.
**Implementation:**
- Converts HTML to Markdown before processing
- Applies Haiku model summarization via `applyPromptToMarkdown(prompt, markdown)` when content exceeds threshold
- Respects `abortController` signal
---
### 9.2 WebSearchTool
**Tool name:** `WebSearch`
**Source:** `src/tools/WebSearchTool/WebSearchTool.ts`
**Characteristics:** `shouldDefer: true`, `maxResultSizeChars: 100_000`
**Input Schema:**
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `query` | `string` (min 2 chars) | Yes | Search query |
| `allowed_domains` | `string[]` | No | Restrict results to these domains |
| `blocked_domains` | `string[]` | No | Exclude results from these domains |
**Output Schema:**
```typescript
{
query: string
results: SearchResult[] | string // String when no results or commentary
durationSeconds: number
}
type SearchResult = {
title: string
url: string
snippet: string
}
```
**Permission:** `'passthrough'` — always prompts user
**Implementation:**
- Uses beta tool: `web_search_20250305`
- Maximum 8 search operations per call
- Enabled only for `firstParty`, `vertex`, and `foundry` API providers
---
## 10. MCP Integration Tools
### 10.1 MCPTool
**Tool name:** `mcp` (overridden per server to `mcp__<server>__<tool>`)
**Source:** `src/tools/MCPTool/MCPTool.ts`
**Characteristics:**
- `isMcp: true`
- `maxResultSizeChars: 100_000`
- `permission: 'passthrough'` (always asks)
- All methods are overridden in `mcpClient.ts` when instantiated per server
**Input Schema:** `z.object({}).passthrough()` — accepts any object
**Output:** `string`
**Exports:**
```typescript
type MCPProgress // Re-exported from mcp progress types
```
---
### 10.2 McpAuthTool
**Tool name:** `mcp__<serverName>__authenticate`
**Source:** `src/tools/McpAuthTool/McpAuthTool.ts`
A pseudo-tool factory, not a standard `buildTool()` instance.
```typescript
function createMcpAuthTool(
serverName: string,
config: ScopedMcpServerConfig,
): Tool<InputSchema, McpAuthOutput>
```
**Input:** `{}` (empty)
**Output Schema:**
```typescript
type McpAuthOutput = {
status: 'auth_url' | 'unsupported' | 'error'
message: string
authUrl?: string // Present when status='auth_url'
}
```
**Behavior:**
- Created for MCP servers that are installed but need OAuth authentication
- Starts `performMCPOAuthFlow()` with `skipBrowserOpen: true`
- Returns authorization URL for user to open in browser
- Background continuation: when OAuth completes, calls `reconnectMcpServerImpl()` and swaps real tools into app state via prefix-based replacement
- `claudeai-proxy` transport: returns `'unsupported'` and directs user to `/mcp`
- Silent auth (cached IdP token): returns success message without URL
---
### 10.3 ListMcpResourcesTool
**Tool name:** `mcp__listResources` (`LIST_MCP_RESOURCES_TOOL_NAME`)
**Source:** `src/tools/ListMcpResourcesTool/ListMcpResourcesTool.ts`
**Characteristics:** `shouldDefer: true`
**Input Schema:**
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `server` | `string` | No | Filter by server name |
**Output Schema:**
```typescript
Array<{
uri: string
name: string
mimeType?: string
description?: string
server: string
}>
```
**Note:** Not included in `getTools()` directly; only added when MCP servers with resources are present.
---
### 10.4 ReadMcpResourceTool
**Tool name:** `ReadMcpResourceTool`
**Source:** `src/tools/ReadMcpResourceTool/ReadMcpResourceTool.ts`
**Characteristics:** `shouldDefer: true`
**Input Schema:**
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `server` | `string` | Yes | MCP server name |
| `uri` | `string` | Yes | Resource URI to read |
**Output Schema:**
```typescript
{
contents: Array<{
uri: string
mimeType?: string
text?: string
blobSavedTo?: string // Path when binary blob saved to disk
}>
}
```
**Implementation:**
- Binary blobs are saved to disk; `getBinaryBlobSavedMessage()` returns path reference string
---
## 11. Plan Mode Tools
### 11.1 EnterPlanModeTool
**Tool name:** `EnterPlanMode`
**Source:** `src/tools/EnterPlanModeTool/EnterPlanModeTool.ts`
**Characteristics:** `shouldDefer: true`, `maxResultSizeChars: 100_000`, `isConcurrencySafe: true`, `isReadOnly: true`
**Input:** `{}` (empty object)
**Output Schema:**
```typescript
{
message: string // Confirmation message
}
```
**Behavior:**
- Sets permission mode to `'plan'` (read-only planning mode)
- Disabled with `--channels` flag
- Cannot be called from agent (teammate) context; only from main agent
- Saves current permission mode as `prePlanMode` for restoration by `ExitPlanMode`
---
### 11.2 ExitPlanModeV2Tool
**Tool name:** `ExitPlanMode` (`EXIT_PLAN_MODE_V2_TOOL_NAME`)
**Source:** `src/tools/ExitPlanModeTool/ExitPlanModeV2Tool.ts`
**Characteristics:** `shouldDefer: true`, `maxResultSizeChars: 100_000`
**Input Schema (model-facing):**
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `allowedPrompts` | `AllowedPrompt[]` | No | Pre-approved tool calls (passthrough schema) |
`AllowedPrompt`:
```typescript
{
tool: 'Bash'
prompt: string
}
```
**SDK Input Schema** (adds):
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `plan` | `string` | No | Plan text for SDK mode |
| `planFilePath` | `string` | No | Path to plan file for SDK mode |
**Output Schema:**
```typescript
type Output = {
plan: string
isAgent: boolean
filePath?: string
hasTaskTool?: boolean
planWasEdited?: boolean
awaitingLeaderApproval?: boolean
requestId?: string
}
```
**Behavior:**
- For teammates with `isPlanModeRequired()`: sends `plan_approval_request` message to team-lead's mailbox; returns `awaitingLeaderApproval: true` with a `requestId`
- For non-teammates: restores permission mode to `prePlanMode` (typically `default` or `auto`)
- Disabled with `--channels` flag
**Exports:**
```typescript
type AllowedPrompt
const _sdkInputSchema // Extended schema with plan/planFilePath
type Output
```
---
## 12. Notebook Tool
### NotebookEditTool
**Tool name:** `NotebookEdit`
**Source:** `src/tools/NotebookEditTool/NotebookEditTool.ts`
**Characteristics:** `shouldDefer: true`, `maxResultSizeChars: 100_000`
**Input Schema:**
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `notebook_path` | `string` | Yes | Absolute path to `.ipynb` file |
| `cell_id` | `string` | No | Target cell ID (required for edit/delete; omit for new cell) |
| `new_source` | `string` | Yes | New source content for the cell |
| `cell_type` | `'code' \| 'markdown'` | No | Cell type (for new cells) |
| `edit_mode` | `'replace' \| 'insert' \| 'delete'` | No (default `'replace'`) | Edit operation |
**Output Schema:**
```typescript
{
new_source: string
cell_id?: string
cell_type: 'code' | 'markdown'
language: string
edit_mode: 'replace' | 'insert' | 'delete'
error?: string
notebook_path: string
original_file: string // Full notebook JSON before edit
updated_file: string // Full notebook JSON after edit
}
```
**Validation / Safety:**
- Read-before-write required (same as FileEditTool/FileWriteTool)
- mtime staleness check
- UNC path security skip
- Clears `execution_count` and `outputs` on cell replace (avoids stale output display)
---
## 13. Worktree Tools
### 13.1 EnterWorktreeTool
**Tool name:** `EnterWorktree` (`ENTER_WORKTREE_TOOL_NAME`)
**Source:** `src/tools/EnterWorktreeTool/EnterWorktreeTool.ts`
**Characteristics:** `shouldDefer: true`, `maxResultSizeChars: 100_000`
**Input Schema:**
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `name` | `string` | No | Optional name for the worktree. Each `/`-separated segment: letters, digits, dots, underscores, dashes only; max 64 chars total. Random name generated if not provided. |
**Output Schema:**
```typescript
{
worktreePath: string
worktreeBranch?: string
message: string
}
```
**Behavior:**
- Validates not already in a worktree session created by this session
- Resolves to main repo root before creating worktree
- Calls `createWorktreeForSession(sessionId, slug)` to create git worktree (or hooks-based worktree)
- Updates `cwd`, `originalCwd` to worktree path
- Clears system prompt sections cache (so `env_info_simple` recomputes with worktree context)
- Clears memoized caches that depend on cwd
- Logs `tengu_worktree_created` analytics event
---
### 13.2 ExitWorktreeTool
**Tool name:** `ExitWorktree` (`EXIT_WORKTREE_TOOL_NAME`)
**Source:** `src/tools/ExitWorktreeTool/ExitWorktreeTool.ts`
**Characteristics:** `shouldDefer: true`, `maxResultSizeChars: 100_000`
**Input Schema:**
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `action` | `'keep' \| 'remove'` | Yes | `'keep'` preserves worktree and branch on disk; `'remove'` deletes both |
| `discard_changes` | `boolean` | No | Required `true` when `action='remove'` and worktree has uncommitted files or unmerged commits |
**Output Schema:**
```typescript
{
action: 'keep' | 'remove'
originalCwd: string
worktreePath: string
worktreeBranch?: string
tmuxSessionName?: string
discardedFiles?: number // Set when action='remove'
discardedCommits?: number // Set when action='remove'
message: string
}
```
**Validation:**
- Only operates on worktrees created by `EnterWorktreeTool` in the current session (scope guard via `getCurrentWorktreeSession()`)
- When `action='remove'` without `discard_changes: true`:
- Runs `countWorktreeChanges()`: uses `git status --porcelain` + `git rev-list --count <originalHead>..HEAD`
- Returns error listing uncommitted files and unmerged commits
- Returns error if git state cannot be determined (fail-closed)
**Behavior:**
- `action='keep'`: calls `keepWorktree()`, restores session to original cwd, preserves worktree for later use
- `action='remove'`: kills tmux session if any, calls `cleanupWorktree()` (removes worktree and branch), restores session
- Both actions: restore `cwd`, `originalCwd`, optionally `projectRoot`, clear caches
- Logs `tengu_worktree_kept` or `tengu_worktree_removed` analytics events
---
## 14. Scheduling Tools
These tools are gated by `isKairosCronEnabled()` (requires `feature('KAIROS')` + GB gate).
### 14.1 CronCreateTool
**Tool name:** `CronCreate` (`CRON_CREATE_TOOL_NAME`)
**Source:** `src/tools/ScheduleCronTool/CronCreateTool.ts`
**Characteristics:** `shouldDefer: true`, `maxResultSizeChars: 100_000`
**Input Schema:**
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `cron` | `string` | Yes | Standard 5-field cron expression in local time: `"M H DoM Mon DoW"` |
| `prompt` | `string` | Yes | Prompt to enqueue at each fire time |
| `recurring` | `boolean` | No (default `true`) | `true` = fire on every cron match (auto-expires after `DEFAULT_MAX_AGE_DAYS` days); `false` = fire once then auto-delete |
| `durable` | `boolean` | No (default `false`) | `true` = persist to `.claude/scheduled_tasks.json` and survive restarts; `false` = in-memory only, dies when session ends |
**Output Schema:**
```typescript
{
id: string // Job ID for reference in CronDelete/CronList
humanSchedule: string // Human-readable schedule (e.g. "Every 5 minutes")
recurring: boolean
durable?: boolean
}
```
**Validation:**
- Valid 5-field cron expression required
- Expression must match at least one calendar date within the next year
- Maximum 50 concurrent scheduled jobs
- Durable crons not supported for teammates (teammates don't persist across sessions)
**Constants:**
- `MAX_JOBS: 50`
- `DEFAULT_MAX_AGE_DAYS`: defined in prompt.ts
---
### 14.2 CronDeleteTool
**Tool name:** `CronDelete` (`CRON_DELETE_TOOL_NAME`)
**Source:** `src/tools/ScheduleCronTool/CronDeleteTool.ts`
**Characteristics:** `shouldDefer: true`, `maxResultSizeChars: 100_000`
**Input Schema:**
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | `string` | Yes | Job ID returned by `CronCreate` |
**Output Schema:**
```typescript
{
id: string // Cancelled job ID
}
```
**Validation:**
- Job with given ID must exist
- Teammates may only delete their own cron jobs (ownership enforced by `agentId`)
---
### 14.3 CronListTool
**Tool name:** `CronList` (`CRON_LIST_TOOL_NAME`)
**Source:** `src/tools/ScheduleCronTool/CronListTool.ts`
**Characteristics:** `shouldDefer: true`, `isConcurrencySafe: true`, `isReadOnly: true`, `maxResultSizeChars: 100_000`
**Input:** `{}` (empty object)
**Output Schema:**
```typescript
{
jobs: Array<{
id: string
cron: string
humanSchedule: string
prompt: string
recurring?: boolean
durable?: boolean
}>
}
```
**Behavior:**
- Teammates only see their own cron jobs (filtered by `agentId`)
- Team lead (no teammate context) sees all jobs
---
## 15. Meta / Discovery Tools
### 15.1 ToolSearchTool
**Tool name:** `ToolSearch` (`TOOL_SEARCH_TOOL_NAME`)
**Source:** `src/tools/ToolSearchTool/ToolSearchTool.ts`
**Characteristics:** `maxResultSizeChars: 100_000`, `isConcurrencySafe: true`, `isReadOnly: true`
**Input Schema:**
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `query` | `string` | Yes | `select:<name>` for exact tool lookup by name, or keywords for fuzzy search |
| `max_results` | `number` | No (default `5`) | Maximum number of tools to return |
**Output Schema:**
```typescript
{
matches: string[] // Tool names that matched
query: string
total_deferred_tools: number
pending_mcp_servers?: string[] // MCP servers still loading
}
```
**Scoring algorithm:**
| Match type | Built-in score | MCP score |
|------------|---------------|-----------|
| Exact name part match | 10 | 12 |
| Substring in name | 5 | 6 |
| Word boundary in `searchHint` | 4 | — |
| Match in description | 2 | — |
**Behavior:**
- `select:<name>` prefix: exact name lookup, fetches full schema definition
- Keywords: fuzzy scoring across all deferred tools
- `mapToolResultToToolResultBlockParam`: returns `tool_reference` blocks for matched tools (injects their schemas into context)
**Exports:**
```typescript
function clearToolSearchDescriptionCache(): void
```
---
### 15.2 AskUserQuestionTool
**Tool name:** `AskUserQuestion` (`ASK_USER_QUESTION_TOOL_NAME`)
**Source:** `src/tools/AskUserQuestionTool/AskUserQuestionTool.tsx`
**Characteristics:** `shouldDefer: true`, `maxResultSizeChars: 100_000`, `requiresUserInteraction: true`
**Input Schema (model-facing):**
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `questions` | `Question[]` | Yes | 14 questions to ask the user |
| `answers` | `object` | No | Injected by UI after user responds (not in model-facing schema) |
| `annotations` | `object` | No | Metadata annotations |
| `metadata` | `object` | No | Arbitrary metadata |
`Question` schema:
```typescript
{
question: string
header?: string
options: QuestionOption[] // 2-4 options
multiSelect?: boolean
}
type QuestionOption = {
label: string
value: string
description?: string
}
```
**Output Schema:**
```typescript
{
questions: Question[]
answers: Record<string, string | string[]> // question → answer(s)
annotations?: object
}
```
**Behavior:**
- Disabled when `--channels` flag is active (no terminal for dialog)
- UI renders interactive question dialog; `answers` field is injected by UI layer
- Supports single-select and multi-select question types
**Exports:**
```typescript
const _sdkInputSchema // Full input schema including answers field
const _sdkOutputSchema // Full output schema
type Question
type QuestionOption
```
---
## 16. Kairos / Special Mode Tools
### 16.1 BriefTool (SendUserMessage)
**Tool name:** `SendUserMessage` (`BRIEF_TOOL_NAME`, alias: `LEGACY_BRIEF_TOOL_NAME`)
**Source:** `src/tools/BriefTool/BriefTool.ts`
**Gate:** `isBriefEnabled()` — requires `feature('KAIROS')` or `feature('KAIROS_BRIEF')` + Growthbook gate + `userMsgOptIn` or `kairosActive`
**Input Schema:**
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `message` | `string` (markdown) | Yes | Message to send to the user |
| `attachments` | `string[]` | No | File paths to attach |
| `status` | `'normal' \| 'proactive'` | Yes | Message disposition: `'proactive'` for unsolicited updates |
**Output Schema:**
```typescript
type Output = {
message: string
attachments?: Array<{
path: string
size: number
isImage: boolean
file_uuid: string
}>
sentAt?: string // ISO timestamp
}
```
**Exports:**
```typescript
function isBriefEntitled(): boolean // Has the entitlement
function isBriefEnabled(): boolean // Has entitlement AND feature flags on
type Output
```
---
### 16.2 SleepTool
**Tool name:** `Sleep`
**Source:** `src/tools/SleepTool/` (only `prompt.ts` present; tool implementation loaded via `require()` when `feature('SLEEP_TOOL')`)
**Gate:** `feature('SLEEP_TOOL')` feature flag
**Purpose:** Wait for a specified duration without holding a shell process. Can be interrupted by the user.
**Prompt highlights:**
- Use when user says to sleep/rest, when waiting for something, or when nothing to do
- Can be called concurrently with other tools
- Prefer over `Bash(sleep ...)` — doesn't hold a shell process
- Receives periodic `<tick>` prompts during sleep; check for useful work before sleeping
- Each wake-up costs an API call; prompt cache expires after 5 minutes of inactivity
---
### 16.3 RemoteTriggerTool
**Tool name:** `RemoteTrigger` (`REMOTE_TRIGGER_TOOL_NAME`)
**Source:** `src/tools/RemoteTriggerTool/RemoteTriggerTool.ts`
**Gate:** `feature('AGENT_TRIGGERS')` + `getFeatureValue_CACHED_MAY_BE_STALE('tengu_surreal_dali', false)` + `isPolicyAllowed('allow_remote_sessions')`
**Characteristics:** `shouldDefer: true`, `maxResultSizeChars: 100_000`, `isConcurrencySafe: true`
**Input Schema:**
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `action` | `'list' \| 'get' \| 'create' \| 'update' \| 'run'` | Yes | CRUD operation on triggers |
| `trigger_id` | `string` | No | Required for `get`, `update`, `run` (regex: `[\w-]+`) |
| `body` | `Record<string, unknown>` | No | JSON body for `create` and `update` |
**Output Schema:**
```typescript
{
status: number // HTTP status code
json: string // Serialized response body
}
```
**Implementation:**
- Calls `${BASE_API_URL}/v1/code/triggers` REST API
- Uses OAuth token (`checkAndRefreshOAuthTokenIfNeeded()` + `getClaudeAIOAuthTokens()`)
- Requires org UUID (`getOrganizationUUID()`)
- API beta header: `ccr-triggers-2026-01-30`
- Timeout: `20_000ms`
- `isReadOnly`: `true` for `list` and `get` actions; `false` otherwise
---
## 17. SDK / Output Tools
### 17.1 SyntheticOutputTool (StructuredOutput)
**Tool name:** `StructuredOutput`
**Source:** `src/tools/SyntheticOutputTool/SyntheticOutputTool.ts`
**Gate:** Enabled only in non-interactive sessions (SDK / `--output-format json` mode)
**Input:** Passthrough — any object, validated against the provided JSON schema via AJV
**Output:** `'Structured output provided successfully'` (string)
**Factory:**
```typescript
function createSyntheticOutputTool(jsonSchema: object): Tool
```
- Creates a validated instance with `WeakMap` caching (same schema object returns same tool)
- Used in SDK/`--output-format json` mode to force the model to emit a structured final response
- AJV validation ensures output matches the caller-provided JSON schema
---
## 18. Skill Tool
**Tool name:** `Skill` (`SKILL_TOOL_NAME`)
**Source:** `src/tools/SkillTool/SkillTool.ts`
**Characteristics:** `maxResultSizeChars: 100_000`
**Purpose:** Runs prompt commands (skills) defined in local files or MCP skill servers.
**Exports:**
```typescript
type Progress // Re-export of SkillToolProgress
```
---
## 19. LSP Tool
**Tool name:** `LSP` (`LSP_TOOL_NAME`)
**Source:** `src/tools/LSPTool/LSPTool.ts`
**Gate:** `ENABLE_LSP_TOOL=true` environment variable
**Characteristics:** `isLsp: true`
**Input Schema:**
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `operation` | `string` | Yes | One of: `goToDefinition`, `findReferences`, `hover`, `documentSymbol`, `workspaceSymbol`, `goToImplementation`, `prepareCallHierarchy`, `incomingCalls`, `outgoingCalls` |
| `filePath` | `string` | Yes | Absolute path to file |
| `line` | `number` | Yes | 1-based line number |
| `character` | `number` | Yes | 1-based character position |
**Constraints:**
- Max file size: 10 MB
---
## 20. REPL Tool
**Tool name:** `REPL`
**Source:** `src/tools/REPLTool/`
**Gate:** Ant-only (`USER_TYPE === 'ant'`) + loaded via `require()`
**Constants (`src/tools/REPLTool/constants.ts`):**
```typescript
const REPL_TOOL_NAME = 'REPL'
function isReplModeEnabled(): boolean
// true when: CLAUDE_CODE_REPL not falsy AND (CLAUDE_REPL_MODE=1 OR (USER_TYPE='ant' AND CLAUDE_CODE_ENTRYPOINT='cli'))
// SDK entrypoints default to REPL mode OFF
const REPL_ONLY_TOOLS = new Set([
'Read', 'Write', 'Edit', 'Glob', 'Grep', 'Bash', 'NotebookEdit', 'Agent',
])
// Hidden from model in REPL mode; model must use REPL for batch operations
```
**Primitive Tools (`src/tools/REPLTool/primitiveTools.ts`):**
```typescript
function getReplPrimitiveTools(): readonly Tool[]
// Returns: [FileReadTool, FileWriteTool, FileEditTool, GlobTool, GrepTool, BashTool, NotebookEditTool, AgentTool]
// Lazy getter to avoid TDZ circular dependency
// These tools remain accessible inside REPL VM context even when hidden from model
```
---
## 21. Config Tool
**Tool name:** `Config` (`CONFIG_TOOL_NAME`)
**Source:** `src/tools/ConfigTool/ConfigTool.ts`
**Gate:** Ant-only
**Characteristics:** `shouldDefer: true`, `maxResultSizeChars: 100_000`
**Input Schema:**
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `setting` | `string` | Yes | Configuration key (e.g. `"theme"`, `"model"`) |
| `value` | `string \| boolean \| number` | No | New value (omit for GET operation) |
**Output Schema:**
```typescript
{
success: boolean
operation?: 'get' | 'set'
setting?: string
value?: unknown
previousValue?: unknown
newValue?: unknown
error?: string
}
```
**Permission:**
- GET operations: auto-allow
- SET operations: requires user permission prompt
**Sources:** Settings can come from `'global'` config or `'settings'` config.
**Exports:**
```typescript
type Input
type Output
```
---
## 22. Shared Utilities
### 22.1 tools/utils.ts
```typescript
/**
* Tags user messages with a sourceToolUseID so they stay transient
* until the tool resolves. Prevents "is running" message duplication in UI.
*/
function tagMessagesWithToolUseID(
messages: (UserMessage | AttachmentMessage | SystemMessage)[],
toolUseID: string | undefined,
): (UserMessage | AttachmentMessage | SystemMessage)[]
/**
* Extracts the tool use ID from a parent message for a given tool name.
*/
function getToolUseIDFromParentMessage(
parentMessage: AssistantMessage,
toolName: string,
): string | undefined
```
---
### 22.2 tools/shared/gitOperationTracking.ts
Shell-agnostic git operation tracking for usage metrics. Works identically for BashTool and PowerShellTool.
**Exported Types:**
```typescript
type CommitKind = 'committed' | 'amended' | 'cherry-picked'
type BranchAction = 'merged' | 'rebased'
type PrAction = 'created' | 'edited' | 'merged' | 'commented' | 'closed' | 'ready'
```
**Key Functions:**
```typescript
/**
* Scan command + output for git operations worth surfacing in UI summary.
* Detects: git commit, git push, git merge, git rebase, gh pr *, glab mr create, curl PR APIs.
*/
function detectGitOperation(
command: string,
output: string,
): {
commit?: { sha: string; kind: CommitKind }
push?: { branch: string }
branch?: { ref: string; action: BranchAction }
pr?: { number: number; url?: string; action: PrAction }
}
/**
* Fire analytics events and OTLP counters for git operations.
* Called after each Bash/PowerShell command completes (exit code 0 only).
*/
function trackGitOperations(
command: string,
exitCode: number,
stdout?: string,
): void
// Exported for testing
function parseGitCommitId(stdout: string): string | undefined
```
**Detected operations:**
- `git commit``tengu_git_operation{operation: 'commit'}`, increments commit OTLP counter
- `git commit --amend` → additionally fires `tengu_git_operation{operation: 'commit_amend'}`
- `git push``tengu_git_operation{operation: 'push'}`
- `gh pr create/edit/merge/comment/close/ready``tengu_git_operation{operation: 'pr_<action>'}`, creates fires PR OTLP counter + links session to PR URL
- `glab mr create``tengu_git_operation{operation: 'pr_create'}`, increments PR OTLP counter
- `curl POST` to PR endpoints → `tengu_git_operation{operation: 'pr_create'}`
**Git command regex:** Tolerates global options between `git` and subcommand (e.g. `git -c commit.gpgsign=false commit`).
---
### 22.3 tools/shared/spawnMultiAgent.ts
Shared module for teammate/subagent creation, extracted from TeammateTool for reuse by AgentTool.
**Key functions:**
```typescript
// Internal helper
function getDefaultTeammateModel(leaderModel: string | null): string
// Checks globalConfig.teammateDefaultModel; null → follow leader; undefined → use hardcoded fallback
```
**Backend types:**
- `in-process`: Spawns teammate as in-process coroutine (no external process)
- Tmux-based pane: Spawns in new tmux pane within swarm session
- External process backends
**Detection:**
- `detectAndGetBackend()`: Probes available backends
- `isInProcessEnabled()`: Checks if in-process spawning is available
- `isTmuxAvailable()`: Checks tmux availability for pane backend
**Environment inheritance:**
- `buildInheritedEnvVars()`: Builds environment variable set for spawned teammate
- Key env vars propagated: `TEAMMATE_COMMAND_ENV_VAR`, model overrides, plugin paths, etc.
---
## 23. Testing Utilities
### TestingPermissionTool
**Tool name:** `TestingPermission`
**Source:** `src/tools/testing/TestingPermissionTool.tsx`
**Gate:** Only enabled when `NODE_ENV === 'test'` (hardcoded: `"production" === 'test'` → always disabled in production)
```typescript
export const TestingPermissionTool: Tool<InputSchema, string>
```
**Input:** `{}` (empty object)
**Output:** `'TestingPermission executed successfully'`
**Behavior:**
- Always returns `{ behavior: 'ask', message: 'Run test?' }` from `checkPermissions()`
- Used for end-to-end permission dialog testing
- All render functions return `null`
- `isConcurrencySafe: true`, `isReadOnly: true`
- Never appears in production tool list (disabled at build time)
---
## Appendix: Tool Name Constants
| Constant | Value | Source |
|----------|-------|--------|
| `BASH_TOOL_NAME` | `'Bash'` | `BashTool/toolName.ts` |
| `FILE_READ_TOOL_NAME` | `'Read'` | `FileReadTool/prompt.ts` |
| `FILE_WRITE_TOOL_NAME` | `'Write'` | `FileWriteTool/prompt.ts` |
| `FILE_EDIT_TOOL_NAME` | `'Edit'` | `FileEditTool/constants.ts` |
| `GLOB_TOOL_NAME` | `'Glob'` | `GlobTool/prompt.ts` |
| `GREP_TOOL_NAME` | `'Grep'` | `GrepTool/prompt.ts` |
| `AGENT_TOOL_NAME` | `'Agent'` | `AgentTool/constants.ts` |
| `LEGACY_AGENT_TOOL_NAME` | `'Task'` | `AgentTool/constants.ts` |
| `NOTEBOOK_EDIT_TOOL_NAME` | `'NotebookEdit'` | `NotebookEditTool/constants.ts` |
| `TASK_OUTPUT_TOOL_NAME` | `'TaskOutput'` | `TaskOutputTool/` |
| `ASK_USER_QUESTION_TOOL_NAME` | `'AskUserQuestion'` | `AskUserQuestionTool/` |
| `SKILL_TOOL_NAME` | `'Skill'` | `SkillTool/` |
| `TOOL_SEARCH_TOOL_NAME` | `'ToolSearch'` | `ToolSearchTool/` |
| `CONFIG_TOOL_NAME` | `'Config'` | `ConfigTool/` |
| `BRIEF_TOOL_NAME` | `'SendUserMessage'` | `BriefTool/` |
| `SLEEP_TOOL_NAME` | `'Sleep'` | `SleepTool/prompt.ts` |
| `REMOTE_TRIGGER_TOOL_NAME` | (from `prompt.ts`) | `RemoteTriggerTool/prompt.ts` |
| `ENTER_WORKTREE_TOOL_NAME` | (from `constants.ts`) | `EnterWorktreeTool/constants.ts` |
| `EXIT_WORKTREE_TOOL_NAME` | (from `constants.ts`) | `ExitWorktreeTool/constants.ts` |
| `TEAM_DELETE_TOOL_NAME` | (from `constants.ts`) | `TeamDeleteTool/constants.ts` |
| `CRON_CREATE_TOOL_NAME` | (from `prompt.ts`) | `ScheduleCronTool/prompt.ts` |
| `CRON_DELETE_TOOL_NAME` | (from `prompt.ts`) | `ScheduleCronTool/prompt.ts` |
| `CRON_LIST_TOOL_NAME` | (from `prompt.ts`) | `ScheduleCronTool/prompt.ts` |
| `EXIT_PLAN_MODE_V2_TOOL_NAME` | `'ExitPlanMode'` | `ExitPlanModeTool/` |
| `LIST_MCP_RESOURCES_TOOL_NAME` | `'mcp__listResources'` | `ListMcpResourcesTool/` |
| `POWERSHELL_TOOL_NAME` | `'PowerShell'` | `PowerShellTool/toolName.ts` |
| `REPL_TOOL_NAME` | `'REPL'` | `REPLTool/constants.ts` |
| `LSP_TOOL_NAME` | `'LSP'` | `LSPTool/` |
---
## Appendix: Tool Feature Gates Summary
| Tool | Gate / Condition |
|------|-----------------|
| `ConfigTool`, `REPLTool` | `USER_TYPE === 'ant'` |
| `CronCreate/Delete/List` | `feature('KAIROS')` + `isKairosCronEnabled()` GB gate |
| `SleepTool` | `feature('SLEEP_TOOL')` |
| `RemoteTriggerTool` | `feature('AGENT_TRIGGERS')` + `tengu_surreal_dali` GB flag + `allow_remote_sessions` policy |
| `BriefTool` | `feature('KAIROS')` or `feature('KAIROS_BRIEF')` + GB gate + userMsgOptIn or kairosActive |
| `TeamCreate/Delete`, `SendMessage` | `isAgentSwarmsEnabled()` |
| `TaskCreate/Get/Update/List` | `isTodoV2Enabled()` |
| `TodoWriteTool` | `!isTodoV2Enabled()` |
| `LSPTool` | `ENABLE_LSP_TOOL=true` env var |
| `TestingPermissionTool` | `NODE_ENV === 'test'` (always disabled in production) |
| `MonitorMcpTask` | `feature('MONITOR_TOOL')` |
| `LocalWorkflowTask` | `feature('WORKFLOW_SCRIPTS')` |
---
## Appendix: deferred vs. alwaysLoad
Tools with `shouldDefer: true` are hidden from the initial prompt context to save tokens. The model discovers them via `ToolSearchTool` using keyword search or `select:<name>` queries. `ToolSearchTool` injects `tool_reference` blocks for matched tools, which causes the tool schemas to be loaded into context.
Tools with `alwaysLoad: true` are always included even in contexts where deferral is the default.
Tools without either flag are included in the initial prompt by default.