Qwen Code Rust ships a set of built-in tools that the agent can call during a conversation. Each tool has a name, a JSON schema for parameters, an approval policy, and a read-only flag.
| Mode | Behavior |
|---|---|
never (auto) |
All tools execute without prompting |
suggest |
Write/execute tools prompt the user; read-only tools auto-approve |
always |
All tools are denied (dry-run / inspect mode) |
Set via --approval-mode CLI flag or QCR_APPROVAL_MODE environment variable.
These tools never modify the filesystem or execute commands. They are always auto-approved regardless of approval mode.
Read the contents of a file with optional line-range limiting.
Schema
{
"type": "object",
"properties": {
"path": { "type": "string", "description": "File path to read." },
"start_line": { "type": "integer", "description": "First line to read (1-based, optional)." },
"end_line": { "type": "integer", "description": "Last line to read (inclusive, optional)." }
},
"required": ["path"]
}Output: File content as text, with line numbers prepended. If start_line/end_line are provided, only that slice is returned.
List the contents of a directory with file type and size metadata.
Schema
{
"type": "object",
"properties": {
"path": { "type": "string", "description": "Directory path to list." }
},
"required": ["path"]
}Output: One entry per line: type size name (e.g. f 1234 main.rs, d - src/).
Find files matching a glob pattern, respecting .gitignore rules.
Schema
{
"type": "object",
"properties": {
"pattern": { "type": "string", "description": "Glob pattern, e.g. '**/*.rs'." },
"base": { "type": "string", "description": "Base directory to search from (optional, defaults to cwd)." }
},
"required": ["pattern"]
}Output: Newline-separated list of matching paths, relative to the base directory.
Search file contents using a regular expression, respecting .gitignore rules.
Schema
{
"type": "object",
"properties": {
"pattern": { "type": "string", "description": "Regular expression to search for." },
"path": { "type": "string", "description": "File or directory to search in." },
"case_insensitive": { "type": "boolean", "description": "Case-insensitive match (default false)." },
"max_results": { "type": "integer", "description": "Maximum number of results to return (default 50)." }
},
"required": ["pattern", "path"]
}Output: Matching lines in file:line: content format.
Fetch a URL and return the page content as plain text (HTML stripped).
Schema
{
"type": "object",
"properties": {
"url": { "type": "string", "description": "URL to fetch." },
"max_chars": { "type": "integer", "description": "Truncate output to this many characters (default 8000)." }
},
"required": ["url"]
}Output: Extracted plain text from the page, truncated to max_chars.
Search the web using DuckDuckGo and return structured results.
Schema
{
"type": "object",
"properties": {
"query": { "type": "string", "description": "Search query string." },
"max_results": { "type": "integer", "description": "Number of results to return (default 5, max 10)." }
},
"required": ["query"]
}Output: Up to max_results results, each with title, URL, and snippet.
Send a one-shot LSP request to a language server (hover, definition, references).
Schema
{
"type": "object",
"properties": {
"server_command": { "type": "string", "description": "Command to start the LSP server (e.g. 'rust-analyzer')." },
"request": { "type": "string", "enum": ["hover", "definition", "references"], "description": "LSP request type." },
"file": { "type": "string", "description": "Absolute path to the file." },
"line": { "type": "integer", "description": "Zero-based line number." },
"character": { "type": "integer", "description": "Zero-based character offset." }
},
"required": ["server_command", "request", "file", "line", "character"]
}Output: Formatted LSP response (hover markdown, definition location, or references list).
Note: Spawns a fresh LSP process per request. Suitable for one-off queries; not for long-lived sessions.
Retrieve a skill's instruction content by name. Skills provide reusable, focused instruction sets for specific workflows (e.g. spec-driven development, browser automation, QA testing).
Schema
{
"type": "object",
"properties": {
"name": { "type": "string", "description": "Skill name (filename without .md extension)." }
},
"required": ["name"]
}Output: The skill's Markdown content (frontmatter stripped, body returned).
Skill resolution order (first match wins):
- Project plugin skills —
.qcr/plugins/<plugin>/skills/<name>.md - Global plugin skills —
~/.qcr/plugins/<plugin>/skills/<name>.md - Project skills —
.qcr/skills/<name>.md - Built-in skills — compiled into the binary at build time
Built-in skills
The following skills are always available without any installation:
| Name | Description |
|---|---|
spec-driven-development |
Structured Requirements → Design → Tasks pipeline for complex features. Five phases: Requirements, Design, Tasks, Execution, and E2E Verification. Includes review checkpoints, auto-continue execution rules, Phase 5 e2e-fix verification gate, and template references. |
agent-browser |
Browser automation via the agent-browser CLI — open pages, snapshot DOM, click, fill, screenshot. |
dogfood |
Exploratory QA workflow — systematically test a web app, find bugs, and produce a structured report with screenshot evidence. |
e2e-fix |
End-to-end fix loop — run a user flow, find bugs, fix them in code, re-run to verify. Loops until all issues are resolved. |
electron |
Automate Electron desktop apps (VS Code, Slack, etc.) via Chrome DevTools Protocol. |
slack |
Interact with Slack workspaces using browser automation. |
vercel-sandbox |
Run browser automation inside Vercel Sandbox microVMs with ephemeral Chrome instances. |
A user-provided skill with the same name as a built-in always takes precedence — built-ins are never overwritten.
Multi-feature decomposition & e2e verification
When a request involves multiple independent features, the agent decomposes it into separate specs (one per feature) and runs an e2e verification gate between each:
- Each feature gets its own
.kiro/specs/<feature-slug>/spec (requirements → design → tasks → execution). - After all tasks in a spec are done,
GetSkill("e2e-fix")is called to verify the feature end-to-end. - The next spec only starts after verification passes (0 bugs remaining).
For library/CLI projects with no web UI, the full test suite replaces the e2e-fix step.
Example
GetSkill { "name": "spec-driven-development" }
Returns the full spec-driven-development workflow instructions including all five phases (Requirements, Design, Tasks, Execute, E2E Verification) and execution rules.
GetSkill { "name": "e2e-fix" }
Returns the end-to-end fix loop instructions for verifying a feature after implementation.
Read, write, or append to a persistent memory file (QCR.md). The agent uses this to persist knowledge, project conventions, architecture decisions, and user preferences across sessions.
Two scopes are supported:
- project —
.qcr/QCR.mdrelative to the current working directory - user —
~/.qcr/QCR.mdin the user's home directory
At session start, both memory files (if they exist) are automatically loaded into the system prompt so the agent has immediate context.
Schema
{
"type": "object",
"properties": {
"action": { "type": "string", "enum": ["read", "write", "append"], "description": "Operation to perform." },
"content": { "type": "string", "description": "Content to write or append. Required for write/append." },
"scope": { "type": "string", "enum": ["project", "user"], "description": "Which memory file (default: project)." }
},
"required": ["action"]
}Output:
read— returns file contents (empty string if file does not exist). Includes a warning if file exceeds 100KB.write— overwrites the file, returns byte count. Rejects content over 100KB.append— appends to the file (newline-separated), returns byte count. Rejects if result would exceed 100KB.
Note: Does not require approval — memory operations are agent-internal. The 100KB size limit prevents unbounded context growth.
Returns the current datetime in RFC 3339 format. Use this when the task genuinely requires knowing the current date or time (e.g. generating timestamps, date-aware filenames).
Schema
{
"type": "object",
"properties": {
"timezone": { "type": "string", "enum": ["utc", "local"], "description": "Timezone for the returned datetime. 'utc' or 'local'." }
},
"required": ["timezone"]
}Output: RFC 3339 datetime string, e.g. 2026-03-09T00:12:54+07:00.
Note: Does not require approval — read-only, no side effects.
Fetch full JSON schema definitions for deferred tools (typically MCP tools) so they can be invoked. MCP tools are registered with only their name and description in the system prompt to save tokens. Use ToolSearch to retrieve full parameter schemas before calling a deferred tool.
Schema
{
"type": "object",
"properties": {
"query": { "type": "string", "description": "Tool name or keyword to search for." },
"max_results": { "type": "number", "description": "Maximum results to return (default 5)." }
},
"required": ["query"]
}Output: Full tool declarations (name, description, parameters JSON schema) for matched deferred tools. If no match found, lists all available deferred tools.
Note: Does not require approval — read-only query against the local tool registry.
A visible chain-of-thought scratchpad. The agent calls this to reason through complex problems, plan approaches, or brainstorm before taking action. The content is shown in the TUI as a tool call argument but has no side effects and is not stored.
Schema
{
"type": "object",
"properties": {
"content": { "type": "string", "description": "The reasoning, planning, or brainstorming content to think through." }
},
"required": ["content"]
}Output: "Finished thinking." — always.
Note: Does not require approval — read-only, no side effects.
These tools can modify the filesystem or run commands. They are subject to the approval policy.
Create or overwrite a file with given content. Parent directories are created automatically.
Schema
{
"type": "object",
"properties": {
"path": { "type": "string", "description": "Destination file path." },
"content": { "type": "string", "description": "Full file content to write." }
},
"required": ["path", "content"]
}Output: Confirmation message with the path written.
Edit an existing file by replacing an exact block of text with new text. The old_text must appear exactly once in the file to avoid ambiguous edits.
Schema
{
"type": "object",
"properties": {
"path": { "type": "string", "description": "Path to the file to edit." },
"old_text": { "type": "string", "description": "The exact text to find and replace. Must appear exactly once." },
"new_text": { "type": "string", "description": "The replacement text." }
},
"required": ["path", "old_text", "new_text"]
}Output: A unified diff of the applied change, or an error if old_text was not found or was ambiguous.
Tips:
- Include surrounding context lines in
old_textto make it unique. - Set
new_textto""(empty string) to delete a block. - For multiline replacements, include the exact newline characters.
Execute a shell command and return stdout/stderr.
Schema
{
"type": "object",
"properties": {
"command": { "type": "string", "description": "Shell command to run." },
"timeout_ms": { "type": "integer", "description": "Timeout in milliseconds (default 30000)." },
"working_dir": { "type": "string", "description": "Working directory (optional, defaults to cwd)." }
},
"required": ["command"]
}Output: Combined stdout and stderr, truncated if very long. Exit code is included on failure.
Safety: Commands run with the same privileges as the qcr process. In suggest approval mode, the user is prompted before execution.
Spawn a sub-agent with its own conversation loop to complete a focused task. Useful for parallelising independent work items or delegating to a plugin-defined agent with a specialised system prompt.
Schema
{
"type": "object",
"properties": {
"task": {
"type": "string",
"description": "Natural-language description of the task the sub-agent should execute."
},
"tools": {
"type": "array",
"items": { "type": "string" },
"description": "Optional list of tool names to make available to the sub-agent. If omitted (and no agent is specified), all tools are available."
},
"agent": {
"type": "string",
"description": "Optional name of a plugin agent. When provided, the agent's system prompt, tool restrictions, and max turns are used. Explicit 'tools' parameter overrides the agent's tool list."
}
},
"required": ["task"]
}Output: The sub-agent's accumulated text output plus a summary line showing turn count and tool call count. When an agent is specified, the agent name appears in the summary.
Notes:
- Sub-agents share the same tool registry as the parent (minus the Task tool itself, preventing recursive spawning).
- Sub-agents run with
AutoApproval— they do not prompt the user for tool approval. - When
agentis specified, the named plugin agent'ssystem_prompt,tools, andmax_turnsare applied. See docs/plugins.md for how to define plugin agents. - If
agentis specified but the name is not found in the plugin agent registry, an error is returned.
Revert a file to its previous state by popping from the per-file undo stack. The stack is populated automatically whenever EditFile, WriteFile, or the line-range editor writes to a file. Up to 5 undo levels are retained per file.
Schema
{
"type": "object",
"properties": {
"path": { "type": "string", "description": "Path to the file to revert." }
},
"required": ["path"]
}Output: A unified diff showing what was restored, or an error if there is nothing to undo for that path.
Approval: Subject to the active approval mode (same as other write tools).
Ask a second-opinion LLM a question and return its answer as plain text. Useful for architectural review, sanity-checking a plan, or getting a fresh perspective without switching models mid-conversation.
Schema
{
"type": "object",
"properties": {
"query": { "type": "string", "description": "The question or request to send to the Oracle." },
"context": { "type": "string", "description": "Optional background context to prepend to the query." }
},
"required": ["query"]
}Output: The Oracle's response as plain text.
Configuration: The Oracle provider and model are set via oracle.provider and oracle.model in ~/.qcr/settings.json. When no separate Oracle config is present, the main provider is used as a fallback.
Approval: Auto-approved — Oracle calls are read-only advisory queries.
Format a source file using the appropriate code formatter, then return a unified diff of the changes applied. The formatter is auto-detected from the file extension, or can be overridden explicitly.
Auto-detected formatters
| Extension | Formatter |
|---|---|
.rs |
rustfmt |
.js .ts .jsx .tsx .json .css .md |
prettier |
.py |
ruff format (fallback: black) |
.go |
gofmt |
.c .cpp .h .hpp |
clang-format |
.sh .bash |
shfmt |
.toml |
taplo fmt |
.lua |
stylua |
Schema
{
"type": "object",
"properties": {
"path": { "type": "string", "description": "File to format." },
"formatter": { "type": "string", "description": "Override the auto-detected formatter (e.g. 'prettier', 'rustfmt')." }
},
"required": ["path"]
}Output: Unified diff of changes, or a message indicating the file was already well-formatted. Returns a warning (not an error) if the formatter binary is not found.
Approval: Subject to the active approval mode (modifies the file in-place).
Search for a regex pattern across all files in a directory tree and replace every match. Supports include/exclude glob filters and a dry-run mode that shows what would change without writing anything.
Schema
{
"type": "object",
"properties": {
"directory": { "type": "string", "description": "Root directory to search." },
"pattern": { "type": "string", "description": "Regular expression to search for." },
"replacement": { "type": "string", "description": "Replacement string (supports $1, $2 capture groups)." },
"file_glob": { "type": "string", "description": "Glob to restrict which files are examined (e.g. '**/*.rs')." },
"exclude_glob": { "type": "string", "description": "Glob to exclude files/directories (e.g. 'target/**')." },
"dry_run": { "type": "boolean", "description": "When true, report matches without writing (default false)." }
},
"required": ["directory", "pattern", "replacement"]
}Output: Summary of files scanned, files matched, and edits applied, followed by a per-file diff. In dry-run mode no files are modified.
Default exclusions: target/, node_modules/, .git/ are always excluded.
Approval: Subject to the active approval mode (may modify many files at once).
Validate and save a Mermaid diagram to a file. Optionally render it to an image using the mmdc CLI if it is installed.
Supported diagram types: flowchart, graph, sequenceDiagram, classDiagram, stateDiagram-v2, stateDiagram, erDiagram, journey, gantt, pie, gitGraph, mindmap, timeline, xychart-beta, block-beta, quadrantChart, sankey-beta, requirementDiagram, C4Context.
Schema
{
"type": "object",
"properties": {
"diagram": { "type": "string", "description": "The Mermaid diagram source." },
"path": { "type": "string", "description": "Output file path. Use .md to embed in a fenced block, or .mmd for raw source." },
"title": { "type": "string", "description": "Optional H1 heading to prepend when writing a .md file." },
"validate_only": { "type": "boolean", "description": "When true, validate the diagram without writing (default false)." },
"render": { "type": "boolean", "description": "When true, invoke mmdc to render an image alongside the file (default false)." }
},
"required": ["diagram", "path"]
}Output: The path written and any validation warnings or errors. In validate_only mode, reports issues without touching disk.
Approval: Subject to the active approval mode (creates/overwrites a file).
Automate a web browser using the agent-browser CLI. Translates typed parameters into CLI invocations and returns combined stdout+stderr output.
Requires: agent-browser installed globally — npm install -g agent-browser
Workflow: open URL → snapshot -i (get element refs) → use @eN refs to click/fill/select → re-snapshot to verify.
Approval policy:
snapshot— auto-approved (read-only DOM inspection)- All other actions — require approval
Schema
{
"type": "object",
"properties": {
"action": { "type": "string", "enum": ["open", "snapshot", "click", "fill", "select", "screenshot", "eval", "wait", "close"], "description": "Browser action to perform." },
"url": { "type": "string", "description": "Target URL. Required for 'open'." },
"ref": { "type": "string", "description": "Element reference from a previous snapshot, e.g. '@e1'. Required for click, fill, select." },
"text": { "type": "string", "description": "Input text for 'fill' and 'select'; JavaScript expression for 'eval'." },
"path": { "type": "string", "description": "Output file path for 'screenshot'." },
"session": { "type": "string", "description": "Named browser session for parallel automation (--session flag). Omit to use the default session." },
"timeout": { "type": "integer", "description": "Command timeout in seconds (--timeout flag). Default: 60.", "minimum": 1, "maximum": 300 },
"extra_args": { "type": "array", "items": { "type": "string" }, "description": "Additional CLI flags passed verbatim at the end (e.g. [\"--headed\", \"--load\", \"networkidle\"])." }
},
"required": ["action"]
}Actions:
| Action | Required params | Description |
|---|---|---|
open |
url |
Navigate to a URL in the browser |
snapshot |
— | Capture DOM snapshot with interactive element refs (@eN) |
click |
ref |
Click an element identified by a snapshot ref |
fill |
ref, text |
Type text into an input element |
select |
ref, text |
Choose an option in a select element |
screenshot |
path |
Save a screenshot to path |
eval |
text |
Execute a JavaScript expression and return the result |
wait |
— | Wait for the page to reach a stable state |
close |
— | Close the current browser page/session |
Not-installed fallback: When agent-browser is not on PATH the tool returns a ToolOutput::error with install instructions instead of propagating an infrastructure error.
Create or replace the agent's structured task list for the current session. Each call is a full replacement — the agent passes the complete desired state. The list is persisted to ~/.qcr/todos/<session-id>.json and rendered as a live checklist in the TUI.
Schema
{
"type": "object",
"properties": {
"todos": {
"type": "array",
"description": "The complete updated todo list (replaces the current list).",
"items": {
"type": "object",
"properties": {
"id": { "type": "string", "minLength": 1, "description": "Unique identifier for this item." },
"content": { "type": "string", "minLength": 1, "description": "Human-readable task description." },
"status": { "type": "string", "enum": ["pending", "in_progress", "completed"], "description": "Lifecycle state." }
},
"required": ["id", "content", "status"],
"additionalProperties": false
}
}
},
"required": ["todos"]
}Output: A confirmation string with a <system-reminder> block containing the serialized list so the agent can continue with up-to-date context. When todos is empty the list is cleared and a "cleared" message is returned.
TUI rendering: Each item is shown with a status indicator — [ ] pending, [→] in progress, [✓] completed. An empty list displays (todo list cleared).
Approval: Auto-approved — todo writes are agent-internal housekeeping and never modify project files.
Persistence: ~/.qcr/todos/<session-id>.json. Falls back to default.json when no session ID is available.
Creates a new directory and all necessary parent directories (equivalent to mkdir -p). Idempotent — succeeds silently if the directory already exists. Refuses to create directories inside protected system paths (/etc, /usr/bin, ~/.ssh, etc.).
Schema
{
"type": "object",
"properties": {
"path": { "type": "string", "description": "The directory path to create. Tilde (~) is expanded. All missing parent directories are created automatically. Must not be inside a protected system path." }
},
"required": ["path"]
}Output: "Created directory <path>" on success, or an error string.
Errors:
- Path is inside a protected system directory (hard refusal via
is_protected_for_write)
Note: Does not require approval — no content is written or destroyed. Safety guard still applies.
Copies a file or directory to a new destination. Directories are copied recursively. Parent directories of the destination are created automatically. Refuses to silently overwrite an existing destination. Refuses to copy into protected system directories (/etc, /usr/bin, ~/.ssh, etc.).
Schema
{
"type": "object",
"properties": {
"source_path": { "type": "string", "description": "The file or directory to copy. Tilde (~) is expanded." },
"destination_path": { "type": "string", "description": "The destination path. Must not already exist. Must not be inside a protected system path. Parent directories are created automatically." }
},
"required": ["source_path", "destination_path"]
}Output: "Copied <src> to <dst>" on success.
Errors:
- Source does not exist
- Destination already exists (will not overwrite — delete or move first)
- Destination is inside a protected system directory (hard refusal via
is_protected_for_write)
Note: Requires approval.
Moves or renames a file or directory. Uses an atomic rename(2) when source and destination share the same filesystem; falls back to copy-then-delete for cross-device moves. Parent directories of the destination are created automatically. Refuses to overwrite an existing destination. Refuses to move protected system paths as source, and refuses to move into protected system directories as destination.
Schema
{
"type": "object",
"properties": {
"source_path": { "type": "string", "description": "The file or directory to move or rename. Tilde (~) is expanded. Must not be a protected system path." },
"destination_path": { "type": "string", "description": "The destination path. Must not already exist. Must not be inside a protected system path. Parent directories are created automatically." }
},
"required": ["source_path", "destination_path"]
}Output: "Moved <src> to <dst>" (appends (cross-device) when a copy-then-delete fallback was used).
Errors:
- Source does not exist
- Source is a protected system path (hard refusal via
is_protected_for_delete) - Destination already exists
- Destination is inside a protected system directory (hard refusal via
is_protected_for_write)
Note: Requires approval. Both source and destination are safety-checked.
Safety-first file and directory deletion. Enforces a deny-list of protected system paths (/, /etc, /usr, ~, ~/.ssh, ~/.qcr, etc.), requires an explicit recursive flag for non-empty directories, and supports dry_run mode.
Schema
{
"type": "object",
"properties": {
"path": { "type": "string", "description": "The file or directory to delete. Tilde (~) is expanded." },
"recursive": { "type": "boolean", "description": "Must be true to delete a non-empty directory and all its contents. Default: false." },
"dry_run": { "type": "boolean", "description": "If true, report what would be deleted without making any changes. Default: false." }
},
"required": ["path"]
}Output examples:
- File delete:
"Deleted /path/to/file.txt" - Directory delete:
"Deleted directory /path/to/dir (42 items)" - Dry run file:
"[dry-run] Would delete file: /path/to/file.txt" - Dry run dir:
"[dry-run] Would delete directory: /path/to/dir (42 items)"
Errors:
- Path does not exist
- Non-empty directory without
recursive: true - Path matches the protected deny-list (hard refusal, cannot be overridden)
Protected paths (partial list): /, /etc, /usr, /bin, /sbin, /lib, /boot, /var, /tmp, /root, /System, /Library, /Applications, ~ (home directory), ~/.ssh, ~/.gnupg, ~/.config, ~/.qcr, ~/.bashrc, ~/.zshrc, ~/.gitconfig.
Note: Always requires approval. Use dry_run: true to preview before committing.
These tools manage session-scoped cron tasks. They are registered in TUI mode only (not headless/print) and share an Arc<Scheduler> with the TUI event loop. Set QCR_DISABLE_CRON=1 to disable.
See docs/scheduled-tasks.md for the full scheduling guide including /loop, jitter, expiry, and cron expression reference.
Schedule a new recurring or one-shot task using a standard 5-field cron expression. The task fires a prompt as a new agent turn at the scheduled time. Session-scoped -- tasks are discarded when the session ends. Recurring tasks auto-expire after 3 days. Maximum 50 tasks per session.
Schema
{
"type": "object",
"properties": {
"cron_expression": { "type": "string", "description": "Standard 5-field cron: minute hour day-of-month month day-of-week. Examples: '*/10 * * * *', '0 9 * * 1-5', '30 14 15 3 *'." },
"prompt": { "type": "string", "description": "The prompt text to execute when the task fires." },
"recurring": { "type": "boolean", "description": "Whether the task recurs (true, default) or fires once then deletes itself (false)." }
},
"required": ["cron_expression", "prompt"]
}Output: Confirmation with task ID (8-char hex), cron expression, prompt, active count, expiry date, and jitter offset.
Errors:
- Invalid cron expression (wrong field count, out-of-range values, bad syntax)
- Maximum 50 tasks reached
- Scheduler disabled (
QCR_DISABLE_CRON=1)
List all scheduled tasks in the current session. Returns each task's ID, cron expression, prompt, type (recurring or one-shot), creation time, expiry time, last fire time, and jitter offset.
Schema
{
"type": "object",
"properties": {},
"required": []
}Output: Formatted list of all active tasks, or "No scheduled tasks." if empty. When the scheduler is disabled, returns a message indicating that.
Delete a scheduled task by its 8-character hex ID. Use CronList to find task IDs.
Schema
{
"type": "object",
"properties": {
"task_id": { "type": "string", "description": "The 8-character hex ID of the task to delete (shown by CronList)." }
},
"required": ["task_id"]
}Output: Confirmation with deleted task details and remaining task count.
Errors:
- Task ID not found
- Scheduler disabled
These tools are available when QCR_EXPERIMENTAL_AGENT_TEAMS=1 is set. They provide team lifecycle management, task coordination, and inter-agent messaging. All team tools check the feature gate at call time and return an error if the feature is not enabled.
See docs/agent-teams.md for the full agent teams guide.
Spawn a new teammate agent as a child process and add it to the active team. The teammate starts with its own context window and runs the given prompt as its initial instruction. Requires approval.
Schema
{
"type": "object",
"properties": {
"name": { "type": "string", "description": "Display name for the teammate." },
"prompt": { "type": "string", "description": "Initial instruction prompt for the teammate." },
"model": { "type": "string", "description": "Model override (optional). Defaults to the session model." },
"plan_approval": { "type": "boolean", "description": "If true, teammate requests lead approval before executing plans." }
},
"required": ["name", "prompt"]
}Output: Confirmation with teammate name and assigned agent ID.
Errors:
- Feature not enabled
nameorpromptmissing or empty- Teammate with that name already exists in the team
Send a ShutdownRequest message to a named teammate and wait for acknowledgement. Updates the member status to Stopped after the handshake completes. Requires approval.
Schema
{
"type": "object",
"properties": {
"name": { "type": "string", "description": "Display name of the teammate to shut down." }
},
"required": ["name"]
}Output: Confirmation that the shutdown handshake completed.
Errors:
- Feature not enabled
- Teammate not found in team config
- Teammate already stopped
Remove the active team's config, mailbox directories, and task files after all members have stopped. Requires approval.
Schema
{
"type": "object",
"properties": {}
}Output: Confirmation that team directories were removed.
Errors:
- Feature not enabled
- One or more teammates still have
Runningstatus (ActiveTeammates)
Atomically claim a pending task from the shared task list. Uses an fs4 exclusive file lock so exactly one agent wins when multiple agents race for the same task.
Schema
{
"type": "object",
"properties": {
"task_id": { "type": "string", "description": "ID of the task to claim." }
},
"required": ["task_id"]
}Output: JSON record of the claimed task including id, title, status, assigned_to, claimed_at.
Errors:
- Feature not enabled
- Task not found
TaskAlreadyClaimed— another agent won the raceTaskBlocked— one or moredepends_ontasks are not yetCompleted
Mark a claimed task as completed and resolve any tasks that were blocked waiting on it.
Schema
{
"type": "object",
"properties": {
"task_id": { "type": "string", "description": "ID of the task to complete." }
},
"required": ["task_id"]
}Output: Confirmation with task ID and completed_at timestamp.
Errors:
- Feature not enabled
- Task not found
- Task is not in
InProgressstate (must be claimed first) - Caller is not the agent that claimed the task
List all tasks in the shared task list, optionally filtered by status.
Schema
{
"type": "object",
"properties": {
"filter": {
"type": "string",
"enum": ["all", "pending", "in_progress", "completed"],
"description": "Status filter. Defaults to 'all'."
}
}
}Output: JSON array of task records sorted by task ID.
Errors:
- Feature not enabled
- Invalid filter value
Send a direct message to a named teammate, or broadcast to all teammates when to is "broadcast" or kind is "broadcast".
Schema
{
"type": "object",
"properties": {
"to": { "type": "string", "description": "Recipient display name, agent ID, or 'broadcast'." },
"content": { "type": "string", "description": "Message body." },
"kind": { "type": "string", "enum": ["message", "broadcast"], "description": "Message kind. Defaults to 'message'." }
},
"required": ["to", "content"]
}Output: Confirmation with recipient name, or count of recipients for a broadcast.
Errors:
- Feature not enabled
toorcontentmissing or empty- Recipient not found in team config
- Cannot send to self
Send a message to all team members simultaneously. The sender is excluded from the recipient list.
Schema
{
"type": "object",
"properties": {
"content": { "type": "string", "description": "Message body to broadcast to all teammates." }
},
"required": ["content"]
}Output: Confirmation with count of recipients reached. If no other members exist, returns a zero-count success.
Errors:
- Feature not enabled
contentmissing or empty
List all members of the active team with their name, agent ID, role, and status. Read-only.
Schema
{
"type": "object",
"properties": {}
}Output: Plain-text table with columns: NAME, AGENT ID, ROLE, STATUS.
Errors:
- Feature not enabled
Present a structured multiple-choice question to the user and wait for their answer. Use when the agent needs clarification, wants the user to choose between approaches, or needs explicit confirmation before proceeding.
The TUI renders a numbered choice list and the user presses a key to select. The selected option text is returned to the LLM.
Schema
{
"type": "object",
"properties": {
"question": {
"type": "string",
"description": "The question to present. Be concise and specific."
},
"options": {
"type": "array",
"items": { "type": "string" },
"minItems": 2,
"maxItems": 8,
"description": "Answer choices. Each option should be distinct and actionable."
},
"multi_select": {
"type": "boolean",
"description": "If true, the user may select multiple options. Default: false.",
"default": false
}
},
"required": ["question", "options"]
}Output: The text of the selected option(s). Times out after 5 minutes if the user does not respond.
Read or update runtime configuration without editing files or restarting. Changes take effect immediately and are persisted to ~/.qcr/settings.json.
Schema
{
"type": "object",
"properties": {
"action": {
"type": "string",
"enum": ["get", "set", "list"],
"description": "'get' reads a key, 'set' writes a key, 'list' shows all keys."
},
"key": {
"type": "string",
"description": "Config key (dot-path). Required for get/set."
},
"value": {
"type": "string",
"description": "New value. Required for set."
}
},
"required": ["action"]
}Supported keys:
| Key | Description |
|---|---|
model.model_id |
Active model identifier (e.g. qwen3-coder, gpt-4o) |
model.provider |
Provider name (e.g. dashscope, openai, anthropic) |
model.max_tokens |
Max completion tokens (integer, or default to unset) |
model.temperature |
Sampling temperature (float 0-2, or default to unset) |
approval_mode |
always, never, or suggest |
max_turns |
Maximum agentic turns before auto-stop (integer) |
telemetry_enabled |
true or false |
auto_compress_threshold |
Compress history when message count exceeds this (0 = disabled) |
custom_instructions |
Extra instructions appended after system prompt |
Output: For get, the current value. For set, confirmation. For list, all supported keys with descriptions.
Submit a plan to the lead agent for approval and block until the lead responds. Only available when the teammate was started with QCR_PLAN_APPROVAL=1.
The tool sends a PlanApprovalRequest message to the lead's mailbox, then polls the teammate's own inbox for a PlanApprovalResponse (500 ms interval, 5-minute timeout).
Schema
{
"type": "object",
"properties": {
"plan": {
"type": "string",
"description": "The plan text to submit for approval."
}
},
"required": ["plan"]
}Output:
- On approval:
"Plan approved. You may now begin implementation." - On rejection:
"Plan rejected: {feedback}. Please revise and resubmit." - On timeout: error indicating the lead did not respond within 5 minutes
Errors:
- Feature not enabled (
QCR_EXPERIMENTAL_AGENT_TEAMSnot set) - Plan approval not enabled (
QCR_PLAN_APPROVALnot set for this teammate) planparameter missing or empty- Timeout waiting for lead response
| Tool | Read-only | Requires Approval | Safety Guard | Category |
|---|---|---|---|---|
| ReadFile | yes | no | -- | Filesystem |
| ListDirectory | yes | no | -- | Filesystem |
| Glob | yes | no | -- | Filesystem |
| Grep | yes | no | -- | Filesystem |
| WebFetch | yes | no | -- | Web |
| WebSearch | yes | no | -- | Web |
| Lsp | yes | no | -- | Editor |
| GetSkill | yes | no | -- | Skills |
| ToolSearch | yes | no | -- | Tools |
| Memory | no | no | -- | Knowledge |
| Now | yes | no | -- | Utility |
| Thinking | yes | no | -- | Utility |
| WriteFile | no | yes | -- | Filesystem |
| EditFile | no | yes | -- | Filesystem |
| UndoEdit | no | yes | -- | Filesystem |
| FormatFile | no | yes | -- | Filesystem |
| FindAndEdit | no | yes | -- | Filesystem |
| Mermaid | no | yes | -- | Filesystem |
| CreateDirectory | no | no | is_protected_for_write |
Filesystem |
| CopyPath | no | yes | is_protected_for_write (dest) |
Filesystem |
| MovePath | no | yes | is_protected_for_delete (src) + is_protected_for_write (dest) |
Filesystem |
| DeletePath | no | yes | is_protected_for_delete |
Filesystem |
| Browser | no | yes (snapshot: no) | -- | Browser |
| Shell | no | yes | -- | Execute |
| Task | no | no | -- | Sub-agent |
| TodoWrite | no | no | -- | Task tracking |
| Oracle | no | no | -- | Advisory |
| CronCreate | no | no | -- | Scheduling |
| CronList | yes | no | -- | Scheduling |
| CronDelete | no | no | -- | Scheduling |
| SpawnTeammate | no | yes | feature gate | Agent Teams |
| ShutdownTeammate | no | yes | feature gate | Agent Teams |
| CleanupTeam | no | yes | feature gate | Agent Teams |
| ClaimTask | no | no | feature gate | Agent Teams |
| CompleteTask | no | no | feature gate | Agent Teams |
| ReadTaskList | yes | no | feature gate | Agent Teams |
| SendMessage | no | no | feature gate | Agent Teams |
| Broadcast | no | no | feature gate | Agent Teams |
| ListTeammates | yes | no | feature gate | Agent Teams |
| SubmitPlan | no | no | feature gate + QCR_PLAN_APPROVAL |
Agent Teams |
| AskUserQuestion | yes | no | -- | User Interaction |
| Config | no | no | -- | Configuration |
Implement the Tool trait from qcr-core:
use async_trait::async_trait;
use qcr_core::tools::traits::{Tool, ToolOutput};
use qcr_core::error::ToolError;
pub struct MyTool;
#[async_trait]
impl Tool for MyTool {
fn name(&self) -> &str { "MyTool" }
fn description(&self) -> &str { "Does something useful." }
fn parameters_schema(&self) -> serde_json::Value {
serde_json::json!({
"type": "object",
"properties": {
"input": { "type": "string" }
},
"required": ["input"]
})
}
fn requires_approval(&self) -> bool { false }
fn is_read_only(&self) -> bool { true }
async fn execute(&self, params: serde_json::Value) -> Result<ToolOutput, ToolError> {
let input = params["input"].as_str()
.ok_or_else(|| ToolError::InvalidParams {
tool: "MyTool".into(),
message: "missing 'input'".into(),
})?;
Ok(ToolOutput::ok(format!("processed: {input}")))
}
}Register it in your agent setup:
registry.register(Arc::new(MyTool))?;