Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .cargo/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ rustflags = ["-C", "target-feature=+crt-static"]
[alias]
workshop = "run -p build-workshop --"
xtask = "run -p build-xtask --"
# Outlines the workspace with the vibelight checkout next to this repo. Cargo
# builds it in release mode on first use and rebuilds it when its source
# changes. Run it from the repo root: the path is relative to the cwd.
vibelight = "run --release --manifest-path ../vibelight/Cargo.toml --"

# Clippy appends a docs-link line to every diagnostic; the lint name already
# identifies it.
Expand Down
10 changes: 10 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ harness-runner = { path = "crates/harness-internal/runner" }
harness-plugin-loader = { path = "crates/harness-internal/plugin-loader" }
plugin-user-input = { path = "crates/plugin-user-input" }
plugin-web = { path = "crates/plugin-web" }
plugin-std = { path = "crates/plugin-std" }
plugin-mcp = { path = "crates/plugin-mcp" }
gateway-routing = { path = "crates/gateway/routing" }
runvil-lua = { path = "crates/runvil-internal/lua" }
Expand Down
22 changes: 22 additions & 0 deletions crates/plugin-std/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
[package]
name = "plugin-std"
version.workspace = true
edition.workspace = true
license.workspace = true
repository.workspace = true
publish = false

[dependencies]
# The Plugin contract: `PackInfo`, the Pack trait, the context a
# call is lent, and the tool, error, and service vocabulary.
runvil-plugin.workspace = true
# Each tool's definition is embedded JSON, read into a spec at construct.
serde.workspace = true
serde_json.workspace = true

[dev-dependencies]
# `testing::TestCall`, which lends the tools a context without a Harness.
runvil-plugin = { workspace = true, features = ["test-support"] }

[lints]
workspace = true
13 changes: 13 additions & 0 deletions crates/plugin-std/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//! Includes the file-size check's source instead of depending on
//! `build-ceiling`, because Plugin crates may name no `build-*` crate.

#[expect(
unreachable_pub,
reason = "the check's public items are private to this build script"
)]
#[path = "../build-ceiling/src/lib.rs"]
mod build_ceiling;

fn main() -> Result<(), build_ceiling::Violations> {
build_ceiling::check()
}
53 changes: 53 additions & 0 deletions crates/plugin-std/schemas/EDITS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Definition edits

Each file in this directory is Cursor's definition of the tool named by its
`wire_name`, snapshot on 2026-10-27. Everything in a file is Cursor's text
except the edits below, each made because the line describes behavior this
Pack does not have.

## read.json

- `Reads a file from the local filesystem.` became `Reads a file from the
run's filesystem.`, because every read goes through the run's virtual
filesystem, not the real one.
- `Assume this tool is able to read all files on the machine.` became
`Assume this tool is able to read every file the run can see.`, for the
same reason.
- `If the User provides a path to a file` became `If the prompt provides a
path to a file`, because a Runvil run has a prompt rather than a chat
user.
- Removed: `Image Support`, `PDF Support`, and the paragraph explaining how
images and PDFs are returned. This build reads text only, so a model told
otherwise would send a PDF and get an error.

## write.json

- Cursor's `Write` description, which is about creating or overwriting a
file. Reworded to name `StrReplace` for targeted changes and to require a
read before overwriting, matching Cursor's own guidance. No behavior was
added; only the wording is ours.

## replace.json

- Cursor's `StrReplace` definition. The wording is ours; the parameters,
their types, and the `required` list are Cursor's.

## rm.json

- Verbatim. Cursor's `Delete` description and schema are kept exactly.

## glob.json

- The `target_directory` description's default, `Cursor workspace root`,
became `the run's filesystem root`.

## grep.json

- Removed the sentence pointing at `rg`, since the tool searches the run's
filesystem itself.
- Removed the sentence pointing at the `Shell` tool for piping, because this
Pack offers no `Shell`.
- The `path` parameter's default, `Cursor workspace root`, became `the
run's filesystem root`.
- Every parameter, type, description, enum, and the `required` list is
otherwise Cursor's.
18 changes: 18 additions & 0 deletions crates/plugin-std/schemas/glob.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"wire_name": "Glob",
"description": "Finds files by name pattern and returns their paths. Use this to locate files when you know part of a name or an extension but not the directory, and prefer it over listing directories one at a time.",
"parameters": {
"type": "object",
"properties": {
"glob_pattern": {
"type": "string",
"description": "The glob pattern to match files against"
},
"target_directory": {
"type": "string",
"description": "The absolute path to the directory to search in. If not provided, the run's filesystem root is used."
}
},
"required": ["glob_pattern"]
}
}
67 changes: 67 additions & 0 deletions crates/plugin-std/schemas/grep.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
{
"wire_name": "Grep",
"description": "Searches file contents with a regular expression and reports the matches. Use it to find where a symbol, string, or pattern occurs, and read the matching file before changing it.",
"parameters": {
"type": "object",
"properties": {
"pattern": {
"type": "string",
"description": "The regular expression pattern to search for in file contents"
},
"path": {
"type": "string",
"description": "File or directory to search in. Defaults to the run's filesystem root."
},
"glob": {
"type": "string",
"description": "Glob pattern to filter files (e.g. \"*.js\", \"*.{ts,tsx}\")"
},
"type": {
"type": "string",
"description": "File type to search. Common types: js, py, rust, go, java, etc. More efficient than glob for standard file types."
},
"output_mode": {
"type": "string",
"enum": ["content", "files_with_matches", "count"],
"description": "Output mode: \"content\" shows matching lines (supports -A/-B/-C context, -n line numbers, head_limit), \"files_with_matches\" shows file paths (supports head_limit), \"count\" shows match counts (supports head_limit). Defaults to \"files_with_matches\"."
},
"-B": {
"type": "number",
"description": "Number of lines to show before each match. Requires output_mode: \"content\", ignored otherwise."
},
"-A": {
"type": "number",
"description": "Number of lines to show after each match. Requires output_mode: \"content\", ignored otherwise."
},
"-C": {
"type": "number",
"description": "Alias for context."
},
"context": {
"type": "number",
"description": "Number of lines to show before and after each match. Requires output_mode: \"content\", ignored otherwise."
},
"-n": {
"type": "boolean",
"description": "Show line numbers in output. Requires output_mode: \"content\", ignored otherwise. Defaults to true."
},
"-i": {
"type": "boolean",
"description": "Case insensitive search"
},
"multiline": {
"type": "boolean",
"description": "Enable multiline mode where . matches newlines and patterns can span lines. Default: false."
},
"head_limit": {
"type": "number",
"description": "Limit output to first N lines/entries. Works across all output modes: content (limits output lines), files_with_matches (limits file paths), count (limits count entries). Defaults to 250 when unspecified. Pass 0 for unlimited (use sparingly - large result sets waste context)."
},
"offset": {
"type": "number",
"description": "Skip first N lines/entries before applying head_limit. Works across all output modes. Defaults to 0."
}
},
"required": ["pattern"]
}
}
22 changes: 22 additions & 0 deletions crates/plugin-std/schemas/read.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"wire_name": "Read",
"description": "Reads a file from the run's filesystem. You can access any file directly by using this tool.\nAssume this tool is able to read every file the run can see. If the prompt provides a path to a file assume that path is valid. It is okay to read a file that does not exist; an error will be returned.\n\nUsage:\n- You can optionally specify a line offset and limit (especially handy for long files), but it's recommended to read the whole file by not providing these parameters\n- Any lines longer than 2000 characters will be truncated\n- Results are returned using cat -n format, with line numbers starting at 1\n- You have the capability to call multiple tools in a single response. It is always better to speculatively read multiple files as a batch that are potentially useful.\n- If you read a file that exists but has empty contents you will receive 'File is empty.'",
"parameters": {
"type": "object",
"properties": {
"path": {
"type": "string",
"description": "The absolute path of the file to read"
},
"offset": {
"type": "number",
"description": "The line number to start reading from. Only provide if the file is too large to read at once. Negative values count backwards from the end."
},
"limit": {
"type": "number",
"description": "The number of lines to read. Only provide if the file is too large to read at once."
}
},
"required": ["path"]
}
}
26 changes: 26 additions & 0 deletions crates/plugin-std/schemas/replace.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"wire_name": "StrReplace",
"description": "Replaces an exact string in a file with a new string. The old string must be unique in the file unless replace_all is true, so include enough surrounding context to identify the one occurrence you mean. Read the file first.",
"parameters": {
"type": "object",
"properties": {
"path": {
"type": "string",
"description": "The absolute path of the file to modify"
},
"old_string": {
"type": "string",
"description": "The text to replace"
},
"new_string": {
"type": "string",
"description": "The text to replace it with (must be different from old_string)"
},
"replace_all": {
"type": "boolean",
"description": "Replace all occurrences of old_string (default false)"
}
},
"required": ["path", "old_string", "new_string"]
}
}
14 changes: 14 additions & 0 deletions crates/plugin-std/schemas/rm.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"wire_name": "Delete",
"description": "Deletes a file at the specified path. The operation will fail gracefully if:\n - The file doesn't exist\n - The operation is rejected for security reasons\n - The file cannot be deleted",
"parameters": {
"type": "object",
"properties": {
"path": {
"type": "string",
"description": "The absolute path of the file to delete"
}
},
"required": ["path"]
}
}
18 changes: 18 additions & 0 deletions crates/plugin-std/schemas/write.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"wire_name": "Write",
"description": "Creates or overwrites a file at the specified path. Use this tool to create a new file or to replace the whole contents of an existing one. If the destination file already exists, you MUST first read it before writing, so a change you did not make is never overwritten. For a targeted change to an existing file, prefer StrReplace.",
"parameters": {
"type": "object",
"properties": {
"path": {
"type": "string",
"description": "The absolute path of the file to write"
},
"contents": {
"type": "string",
"description": "The contents to write to the file"
}
},
"required": ["path", "contents"]
}
}
Loading
Loading