Skip to content

Latest commit

 

History

History
1317 lines (969 loc) · 43.3 KB

File metadata and controls

1317 lines (969 loc) · 43.3 KB

Built-in Tools

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.


Approval Modes

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.


Read-Only Tools

These tools never modify the filesystem or execute commands. They are always auto-approved regardless of approval mode.

ReadFile

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.


ListDirectory (Ls)

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/).


Glob

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.


Grep

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.


WebFetch

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.


WebSearch

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.


Lsp

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.


GetSkill

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):

  1. Project plugin skills.qcr/plugins/<plugin>/skills/<name>.md
  2. Global plugin skills~/.qcr/plugins/<plugin>/skills/<name>.md
  3. Project skills.qcr/skills/<name>.md
  4. 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:

  1. Each feature gets its own .kiro/specs/<feature-slug>/ spec (requirements → design → tasks → execution).
  2. After all tasks in a spec are done, GetSkill("e2e-fix") is called to verify the feature end-to-end.
  3. 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.


Memory

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.md relative to the current working directory
  • user~/.qcr/QCR.md in 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.



Now

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.


ToolSearch

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.


Thinking

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.


Write / Execute Tools

These tools can modify the filesystem or run commands. They are subject to the approval policy.

WriteFile

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.


EditFile

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_text to make it unique.
  • Set new_text to "" (empty string) to delete a block.
  • For multiline replacements, include the exact newline characters.

Shell

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.


Task (SubAgent)

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 agent is specified, the named plugin agent's system_prompt, tools, and max_turns are applied. See docs/plugins.md for how to define plugin agents.
  • If agent is specified but the name is not found in the plugin agent registry, an error is returned.

UndoEdit

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).


Oracle

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.


FormatFile

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).


FindAndEdit

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).


Mermaid

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).


Browser

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.


TodoWrite

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.


CreateDirectory

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.


CopyPath

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.


MovePath

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.


DeletePath

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.


Scheduling Tools

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.

CronCreate

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)

CronList

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.

CronDelete

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

Agent Team Tools

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.

SpawnTeammate

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
  • name or prompt missing or empty
  • Teammate with that name already exists in the team

ShutdownTeammate

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

CleanupTeam

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 Running status (ActiveTeammates)

ClaimTask

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 race
  • TaskBlocked — one or more depends_on tasks are not yet Completed

CompleteTask

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 InProgress state (must be claimed first)
  • Caller is not the agent that claimed the task

ReadTaskList

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

SendMessage

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
  • to or content missing or empty
  • Recipient not found in team config
  • Cannot send to self

Broadcast

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
  • content missing or empty

ListTeammates

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

AskUserQuestion

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.


Config

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.


SubmitPlan

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_TEAMS not set)
  • Plan approval not enabled (QCR_PLAN_APPROVAL not set for this teammate)
  • plan parameter missing or empty
  • Timeout waiting for lead response

Tool Summary Table

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

Adding a Custom Tool

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))?;