VT Code Tool Specifications
June 28, 2026 · View on GitHub
This document describes the canonical public tool surface exposed to VT Code models. Legacy aliases such as read_file, grep_file, list_files, and PTY helpers may still route internally, but prompts, schemas, and evaluations should target the unified tools below.
Canonical Public Tools
-
unified_searchPurpose: Read-only discovery and lookup across the workspace and related runtime state. Actions:grep: broad text searchlist: file discoverystructural: syntax-aware code search via local ast-grep (sg); requires a pattern/kindoutline: token-efficient symbol map of a file/directory viaast-grep outline(>= 0.44.0); no pattern needed, defaultview=digest. Use for "what's in this file?" before reading full source. Seedocs/development/grep-tool-guide.md"Outline mode".tools: tool discoveryerrors: archived/current error lookupagent: agent/runtime infoweb: fetch a URLskill: load a skill by name
-
unified_filePurpose: File reads and workspace-local edits. Actions:readwriteeditpatchdeletemovecopy
-
unified_execPurpose: Command execution and session control. Actions:runwritepollcontinueinspectlistclosecode
-
request_user_inputPurpose: Collect short structured user decisions when runtime permissions allow it. -
apply_patchPurpose: First-class patch application for models that support the freeform patch surface. -
web_searchPurpose: Searches the web via DuckDuckGo (keyless) and returns ranked results. Accepts:query(string, required),max_results(number, optional). See Web Search Tool for config and guard rails. -
defuddle_fetchPurpose: Fetch a URL through defuddle.md markdown extraction (fallback for complex pages). Accepts:url(string, required),max_bytes(number, optional). Hard-capped at one call per session. See Defuddle Fetch.
unified_file
read
Read file content with optional line or byte-range pagination.
- Required:
path - Optional:
max_bytes— maximum bytes to return (default: full file)offset_lines— line offset for line-based reads (1-indexed)page_size_lines— number of lines to read per pageoffset_bytes— byte offset for byte-range reads (0-indexed)page_size_bytes— number of bytes to read per page (default: 8192)
Line-range reads are useful for paginating through large text files:
{
"tool": "unified_file",
"action": "read",
"path": "src/main.rs",
"offset_lines": 100,
"page_size_lines": 50
}
Byte-range reads enable chunked preview of large or binary files:
{
"tool": "unified_file",
"action": "read",
"path": "large_file.bin",
"offset_bytes": 0,
"page_size_bytes": 8192
}
When byte-range parameters are provided, VT Code uses seek-based access for efficient partial reads without loading the entire file into memory.
write
edit
patch
delete
move
copy
unified_search
grep
- Required:
pattern - Optional:
path(default"."),case_sensitive,context_lines,max_results - Use when: you need broad text matching or a quick file-content sweep
list
- Optional:
path(default"."),mode,max_results - Use when: you need file names, directories, or tree-style discovery
structural
- Required:
action="structural" - Optional common fields:
workflow("query" | "scan" | "test" | "inspect" | "rewrite", default"query"),path(default"."for query/scan; public structural takes one root per call even though rawast-grep runaccepts multiple paths),config_path(default workspacesgconfig.ymlfor scan/test),filter,globs,context_lines,max_results workflow="query":- Required: at least one of
patternorkind - Optional:
lang,selector,strictness,debug_query - Internal mapping: read-only
ast-grep run --pattern ... --json=compact --color=never, plus optional--lang,--kind,--selector,--strictness, repeated--globs, and--context langaccepts ast-grep built-in aliases. VT Code normalizes and infers a local subset it can pre-parse itself, such as Rust, Python, JavaScript, TypeScript, TSX, Go, and Java, while explicit unsupported aliases are still passed through to ast-grep unchanged.strictnesstunes ast-grep matching for read-only queries.smartis the default; common alternatives arecst,ast,relaxed, andsignature.templateis passed through for ast-grep compatibility.context_linesmaps to ast-grep--context; raw--beforeand--afterare intentionally not exposed- ast-grep
runexit code1is normalized to an emptymatchesarray instead of surfacing as a VT Code error - Result shape: top-level
matchesarray withfile,line_number,text/lines,language, and compactrangemetadata, plusbackend: "ast-grep" - Raw ast-grep JSON contains fields such as
text,range,file,lines, optionalreplacement, optionalreplacementOffsets, and optionalmetaVariables; VT Code consumes that internal payload but returns a normalized query result instead of exposing the native ast-grep object directly - ast-grep JSON
line,column, and byte offsets are zero-based; VT Code preserves the range semantics in its structured metadata even when also surfacing a convenienceline_number
- Required: at least one of
workflow="scan":- Optional:
path,config_path,filter,globs,context_lines,max_results - Internal mapping: read-only
ast-grep scan --config ... --json=stream --include-metadata --color=never, plus optional--filter, repeated--globs, and--context workflow="scan"depends on project config. In raw ast-grep terms,scanrequires ansgconfig.yml, whileruncan still search without one.context_linesmaps to ast-grep--context; raw--beforeand--afterare intentionally not exposed- ast-grep
scanexit code1when error-severity findings exist is normalized to structuredfindingsinstead of surfacing as a VT Code error - Result shape: top-level
findingsarray withfile,line_number,text/lines,language,range,rule_id,severity,message,note, optionalmetadata, plussummary,truncated, andbackend: "ast-grep" - Raw ast-grep scan JSON extends the base match object with rule metadata such as
ruleId,severity,message, and optionalnote; VT Code maps those into its normalized finding fields rather than returning the raw scan object verbatim
- Optional:
workflow="test":- Optional:
config_path,filter,skip_snapshot_tests - Result shape:
passed,stdout,stderr,summary, andbackend: "ast-grep"
- Optional:
workflow="rewrite":- Required:
patternand eitherrewrite(string) orfix_config(object) - Optional:
lang,selector,strictness,globs,context_lines,max_results - Internal mapping: dry-run
ast-grep run --pattern=... --rewrite=... --json=compact --color=never, plus optional--lang,--selector,--strictness, repeated--globs, and--context - This is a preview-only workflow; no files are modified on disk
lang,selector, andstrictnessbehave the same asworkflow="query"config_path,filter, andskip_snapshot_testsare rejected (scan/test-only fields)fix_configis an advanced alternative to the simplerewritestring. It acceptstemplate(replacement text),expand_start(optional rule to widen the replacement range backwards), andexpand_end(optional rule to widen the replacement range forwards). Each expand rule supportsregex,kind, orpattern, plus optionalstop_by. Whenfix_configincludes expansion, the tool generates a temporary YAML rule and runssg scaninternally instead ofsg run --rewrite. Usefix_configwhen replacing only the matched node is not enough, especially for deleting list items or key-value pairs that also need a surrounding comma removed.- ast-grep
runexit code1is normalized to an emptyrewritesarray instead of surfacing as a VT Code error - Result shape: top-level
rewritesarray withfile,line_number,text/lines,language,range,replacement,replacementOffsets, and optionalmetaVariables, plustruncated,backend: "ast-grep", andworkflow: "rewrite"
- Required:
workflow="inspect":- Optional:
config_path,path - Does not require the ast-grep binary; reads
sgconfig.ymldirectly - Returns:
config_exists,rule_dir_hints,test_configs,util_dirs,language_injections,custom_languages,language_globs, anddiscovered_configs(when the default config is missing but found in a parent directory) - Use when: you need to inspect the project's ast-grep configuration without running any rules
- Optional:
- Constraints:
- Read-only only;
workflow="rewrite"is a dry-run preview that does not modify files; raw--interactive,--update-all, and other apply-mode flags are rejected lang,kind,selector,strictness, anddebug_queryare only valid forworkflow="query"andworkflow="rewrite"langis required whendebug_queryis setskip_snapshot_testsis only valid forworkflow="test"fix_configis only valid forworkflow="rewrite"workflow="inspect"does not require the ast-grep binary and rejectspattern,kind,lang,selector,strictness,debug_query,filter,skip_snapshot_tests,globs,context_lines, andmax_results- Default
config_pathis workspacesgconfig.yml; raw ast-grep also supports upward discovery ofsgconfig.yml, but VT Code’s public structural surface pins scan/test to an explicit config path for determinism - Requires a local
sg/ast-grepbinary; if missing, VT Code auto-installs it on first use (download into~/.vtcode/binwith checksum + 24h cooldown). SetVTCODE_AST_GREP_NO_INSTALL=1to opt out, in which case VT Code returns an actionable error pointing tovtcode dependencies install ast-grep - VT Code-managed installs live in
~/.vtcode/bin - On Linux, prefer the canonical
ast-grepbinary name instead ofsg - Raw ast-grep CLI flags such as
--stdin,--json,--color,--heading,--threads,--inspect,--follow,--no-ignore,--before,--after,--interactive,--update-all,--rule,--inline-rules,--format,--report-style,--error,--warning,--info,--hint, and--offare not part of the public structural surface and should go through the bundledast-grepskill plusunified_execwhen needed. Therewritefield is accepted only forworkflow="rewrite"(dry-run preview); passingrewritewith other workflows is silently ignored. - Test-only ast-grep flags such as
--test-dir,--snapshot-dir,--include-off, interactive snapshot review, and snapshot update flows are also CLI-only and not part of the public structural surface ast-grep new,ast-grep lsp,ast-grep completions, and top-level help/command-discovery flows are CLI-only and should go through the bundledast-grepskill plusunified_exec- Syntax-aware only; do not treat this surface as scope, type, or data-flow analysis
- Pattern syntax follows ast-grep rules:
$VARcaptures one named node,$$$ARGScaptures zero or more nodes,$$VARincludes unnamed nodes, and$_suppresses capture workflow="query"patterns must be valid parseable code; for fragments, unnamed-token cases, or role-sensitive matching, prefer the bundledast-grepskill workflow- Path and glob language inference use VT Code’s local subset of ast-grep-compatible extensions, not the full ast-grep built-in catalog; set
langexplicitly when inference is ambiguous or outside that subset - Custom languages are supported only through local ast-grep configuration, typically workspace
sgconfig.ymlcustomLanguagesplus a compiled tree-sitter dynamic library - Built-in embedded-language behavior is limited to what ast-grep already knows, such as HTML
<script>/<style>extraction; other nested-language cases depend on locallanguageInjections - Non-standard extensions and embedded languages should be handled through local ast-grep config such as
languageGlobsandlanguageInjections, not by guessing a different file language in the tool call - Public project support stops at read-only
sg scanandsg test - Use the bundled
ast-grepskill forsg new, rewrite/apply flows, interactive flags,transform,replace,substring,convert,toCase,separatedBy,rewrite,joinBy,rewriters, custom parser compilation, or non-trivialsgconfig.ymlauthoring/debugging
- Read-only only;
- Use when: you need syntax-aware search, read-only project rule scans, read-only ast-grep rule tests, ast-grep config inspection, or dry-run rewrite previews
- Avoid when: plain text grep is simpler, the search target is not syntax-sensitive, or the task depends on semantic/static-analysis facts
outline
- Required:
action="outline" - Optional:
path(default"."),lang,type(string or array of symbol types, comma-joined for--type),match(regex over item names/signatures/first lines),items("auto" | "structure" | "exports" | "imports" | "all", default"auto"—structurefor file input,exportsfor directory input),pub_members(bool, defaultfalse),follow(bool, defaultfalse),view("digest" | "names" | "full", default"digest") - Internal mapping:
ast-grep outline --json=stream [--lang ...] [--type ...] [--match ...] [--items ...] [--pub-members] [--follow] <path>. The CLI's own--viewflag is text-only and is never passed;viewis applied in Rust to shape the parsed NDJSON. - Requires ast-grep >= 0.44.0 (the
outlinesubcommand and--jsonstream output shipped there) - No pattern or kind is required; outline extracts the symbol table via ast-grep's bundled rule catalog
- No grep fallback when the binary is missing (outline has no text equivalent); auto-installs ast-grep on first use (download into
~/.vtcode/binwith checksum + 24h cooldown). SetVTCODE_AST_GREP_NO_INSTALL=1to opt out; the error then includes the manual install command (vtcode dependencies install ast-grep) - Tolerant deserialization: unknown JSON keys from future ast-grep versions are ignored (
#[serde(default)], nodeny_unknown_fields) - Result shape by
view:digest(default):{view:"digest", files:[{path, lang, groups:[{kind, names, members}]}]}— symbols grouped bysymbolType;namesare top-level item names of that kind;membersis the flat list of all member names across items of that kindnames: same asdigestbut withoutmembersfull:{view:"full", files:[{path, lang, items:[{role, kind, name, signature, astKind, isImport, isExported, members:[{role, kind, name, signature, isPublic}]}]}]}— passthrough of per-symbol records with ranges/signatures and nested members
- Use when: you want a cheap "what's in this file/directory?" symbol map before reading full source; a 200-line file is ~100-300 bytes in
digestvs ~10-40 KB of structural match JSON - Avoid when: you need pattern-based matches (use
structural), text/regex matches (usegrep), or semantic type resolution (use an LSP)
tools
- Required:
keyword - Optional:
detail_level
errors
- Optional:
scope
agent
- Optional:
mode
web
- Required:
url - Optional:
prompt,max_bytes,timeout_secs
skill
- Required:
name
Guidance
- Prefer
unified_searchover shellgrep/findfor normal workspace discovery. - Prefer
grepfor broad text search. - Prefer
structuralfor syntax-sensitive search, read-only project scans, and read-only ast-grep rule tests. - Prefer public
structuralstrictnessonworkflow="query"when the task is just tuning read-only matching betweencst,smart,ast,relaxed, andsignature. - Prefer
workflow="scan"for publicsg scanequivalents andworkflow="test"for publicsg testequivalents. - Prefer
workflow="inspect"when the task needs to introspect the project's ast-grep configuration (rule directories, test configs, language injections, custom languages, language globs) without running any rules. - Prefer
workflow="rewrite"withfix_configwhen the rewrite needs range expansion beyond the matched node, such as removing surrounding commas from list items or key-value pairs. - Prefer
load_skillwith the bundledast-grepskill when the task becomes rule authoring,sg new, rewrite/apply work, interactive ast-grep work, orsgconfig.ymlauthoring/debugging. - Prefer
load_skillwith the bundledast-grepskill when the task is a quick-start or install flow, includingast-grep --help, shell quoting for metavariables, or optional-chaining style first rewrites. - Prefer
load_skillwith the bundledast-grepskill when the task asks for ast-grep catalog examples, existing rewrite examples, or help adapting catalog rules to this repository. - Prefer
load_skillwith the bundledast-grepskill when the task needs project scaffolding viaast-grep neworast-grep new rule, or when it needs guidance aroundrules/,rule-tests/,utils/, andsgconfig.yml. - Prefer
load_skillwith the bundledast-grepskill when the task needssgconfig.ymltop-level config semantics such asruleDirs,testConfigs,testDir,snapshotDir,utilDirs,languageGlobsprecedence, target-triplelibraryPath,languageSymbol, or experimentallanguageInjections. - Prefer
load_skillwith the bundledast-grepskill when the task needsscan --rule,scan --inline-rules, scan severity overrides (--error,--warning,--info,--hint,--off), scan output modes such as--format/--report-style, relational/composite rule objects, positive-rule requirements, limitedkindESQuery syntax,matchesutility rules,nthChildformulas orreverse/ofRule,range, relationalfield, exactstopBysemantics, local/global utility rules, or rule-order debugging. - Prefer
load_skillwith the bundledast-grepskill when the task needstest --test-dir,--snapshot-dir,--include-off, snapshot update flows, interactive snapshot review, or detailedast-grep testCLI behavior beyond publicworkflow="test". - Prefer
load_skillwith the bundledast-grepskill when the task needs rule-config YAML keys such asurl,metadata,constraints,severity,message,note,labels,files,ignores,caseInsensitiveglob objects,severity: off,--include-metadata, or YAML multi-document rule files. - Prefer
load_skillwith the bundledast-grepskill when the task depends on config semantics like single-metaconstraints,constraintsafterrule,notewithout meta interpolation, label-variable scoping,files/ignoresprecedence, relative glob roots, the./path gotcha, or the difference between YAMLignoresand CLI--no-ignore. - Prefer
load_skillwith the bundledast-grepskill when the task needs--rewrite, YAML stringfix,FixConfig,template,expandStart,expandEnd, meta variables anywhere in replacement text, comma/list-item cleanup,--interactive,--update-all, or indentation-sensitive rewrite behavior. - Prefer
load_skillwith the bundledast-grepskill when the task needs transformation-object details such asreplace,substring,convert,toCase,separatedBy,CaseChange, string-form transforms, or experimentaltransform.rewriteordering semantics. - Prefer
load_skillwith the bundledast-grepskill when the task needs rewriter-specific semantics such as required rewriter fields, rewriter-local captures / utils / transforms, nested rewriter calls, or the barrel-import rewrite pattern. - Prefer
load_skillwith the bundledast-grepskill when the task needs raw ast-grep CLI behavior such as--stdin,--json,--heading,--threads,--inspect,--follow,--no-ignore,--before,--after, scan-r,lsp, shell completions, GitHub Action setup, direct--color nevercontrol, or run-command exit-code details. - Prefer
load_skillwith the bundledast-grepskill when the task is about top-level command discovery such asast-grep --help, subcommand selection,new project|rule|test|util,lsp,completions, orhelp. - Prefer
load_skillwith the bundledast-grepskill when the task is really about pattern syntax design, meta-variable capture rules,$$$ARGS,$_,$$VAR, or object-style patterns. - Prefer
load_skillwith the bundledast-grepskill when the task is troubleshooting incomplete fragments, interpretingdebug_query, comparing Playground vs CLI results, or using pattern-objectcontextplusselector. - Prefer
load_skillwith the bundledast-grepskill when the task needskindpluspatterntroubleshooting,rule orderguidance, prefix matching viaconstraints.regex, or multi language rule strategy vialanguageGlobsversus separate rules. - Prefer
load_skillwith the bundledast-grepskill when the task asks how ast-grep works at a high level, including Tree-Sitter parsing, pattern vs YAML vs API inputs, search/rewrite/lint/analyze modes, or multi-core processing. - Prefer
load_skillwith the bundledast-grepskill when the task is about pattern core concepts such as textual vs structural matching, AST vs CST, named vs unnamed nodes,kindvsfield, or significant vs trivial nodes. - Prefer
load_skillwith the bundledast-grepskill when the task is about pattern parsing heuristics such as invalid / incomplete / ambiguous snippets, effective-node selection viaselector, meta-variable detection, lazy multi-meta behavior, orexpandoChar. - Prefer
load_skillwith the bundledast-grepskill when the task is about ast-grep’s match algorithm itself, choosing between strictness levels, or using CLI / YAML strictness outside the public read-only query surface. - Prefer
load_skillwith the bundledast-grepskill when the task needs Find & Patch style rewrites such asrewriters,transform.rewrite,joinBy, recursive sub-node patching, or barrel-import splitting. - Prefer
load_skillwith the bundledast-grepskill when the task outgrows rule syntax and needs ast-grep’s Node NAPI / Python / Rust API,parse,Lang,pattern,kind,SgRoot,SgNode,NapiConfig,ast_grep_core, computed replacements, conditional AST edits, or node-order/count logic. - Prefer
load_skillwith the bundledast-grepskill when the task mentions deprecated language-specific JS objects likejs.parse(...); the current guidance should move that work to the unified NAPI functions instead. - Prefer
load_skillwith the bundledast-grepskill when the target snippet is not valid standalone code and needs pattern-objectcontextplusselector. - Prefer
load_skillwith the bundledast-grepskill when matching depends on$$VAR,field, modifiers/operators, or other CST-level distinctions. - Prefer
load_skillwith the bundledast-grepskill when the requested language is not built into ast-grep and needs workspacesgconfig.ymlcustomLanguagessetup orexpandoChar. - Prefer
load_skillwith the bundledast-grepskill when the task needs custom parser compilation viatree-sitter build, theTREE_SITTER_LIBDIRfallback, reused Neovim parser libraries, or parser inspection withtree-sitter parse. - Prefer
load_skillwith the bundledast-grepskill when the task is mainly about the built-in language catalog, alias selection, extension defaults, or deciding between built-in extension mapping andlanguageGlobs. - Prefer
load_skillwith the bundledast-grepskill when the task needslanguageGlobs,languageInjections,hostLanguage,injected, dynamic injected candidates through$LANG,$CONTENT-style embedded-region capture, styled-components CSS, GraphQL template literals, local/global utility rules,transform,rewrite,joinBy, orrewriters. - Prefer another analysis tool when the task depends on scope, type, control-flow, data-flow, taint, or constant-propagation facts; ast-grep’s structural surface does not provide that analysis.
action="intelligence"remains executor-compatible for legacy callers, but it is deprecated and not part of the public schema.
Common Recovery Patterns
Use these patterns to avoid the tool-selection failures called out in TD-015.
| Situation | Prefer | Avoid |
|---|---|---|
| Need prose, regex, filenames, or symbol-name matching | unified_search with action="grep" | action="structural" with non-parseable text fragments |
| Need syntax-aware code structure matching | unified_search with action="structural" and a valid parseable snippet | Treating structural search as a smarter grep |
| Existing file needs a small literal replacement | unified_file edit with exact old/new text | Large block rewrites through a small literal-edit call |
| Existing file needs a broad rewrite | unified_file write with mode="overwrite" or apply_patch with exact context | write without explicit overwrite intent |
| Patch failed after the file changed | Re-read a narrow fresh range, then re-run patch with exact surrounding lines | Synthetic anchors or remembered context such as @@ heading @@ |
| Shell inspection was denied by policy | Switch to unified_search, unified_file, or request_user_input; if shell access is still required, tell the user it was not applied and suggest /mode auto | Retrying the same denied unified_exec call |
| Recovery pass cannot apply a requested mutation | Finalize with explicit blocker text: not applied; tools were disabled or denied; next action is ... | Presenting generated prose or pseudo tool-call markup as if it ran |
Grep vs structural search
Use grep when the target is plain text or regex:
{
"tool": "unified_search",
"action": "grep",
"pattern": "TD-015|recovery-mode",
"path": "docs/"
}
Use structural only when the pattern is valid code in the target language:
{
"tool": "unified_search",
"action": "structural",
"workflow": "query",
"lang": "rust",
"pattern": "fn $NAME($$$ARGS) -> $RET { $$$BODY }",
"path": "src/"
}
Do not send prose fragments such as AgentLoop agent loop or selector-heavy text blobs to structural mode; ast-grep expects parseable code, not free-form search terms.
edit vs write/overwrite
Use edit when the old text is short, exact, and already present:
{
"tool": "unified_file",
"action": "edit",
"path": "README.md",
"old_str": "Old heading",
"new_str": "New heading"
}
Use write with mode="overwrite" when replacing most or all of an existing file:
{
"tool": "unified_file",
"action": "write",
"path": "README.md",
"mode": "overwrite",
"content": "# New README\n..."
}
If the rewrite is partial but larger than a safe literal edit, prefer apply_patch or unified_file patch with exact file context.
Patch context discipline
Good patch flow:
- Read only the narrow range you will edit.
- Copy the exact surrounding lines into the patch.
- If the patch fails, re-read the file and regenerate the patch from fresh context.
Bad patch flow:
- inventing anchors from memory,
- patching against stale content after another edit,
- widening context until the patch becomes a full-file replacement.
Policy-denied exec recovery
If unified_exec is denied for a read-only shell inspection:
- Do not retry the same shell command.
- Switch to
unified_searchorunified_filefor workspace inspection. - If shell execution is still necessary, say it was not applied and recommend
/mode autoor a user-approved mode change.
Recovery-mode final wording
When a mutating task could not be executed in recovery mode, prefer:
Not applied. Tools were disabled or denied during recovery, so no additional tool call ran. Latest verified evidence is above; next action: re-enable the needed tool or switch to
/mode auto.
Do not return pseudo tool-call markup, XML wrappers, or “done” language for changes that were never applied.