Skip to content

Add the std Pack with Cursor's six file tools - #178

Open
vinniefalco wants to merge 2 commits into
cppalliance:masterfrom
vinniefalco:master
Open

vinniefalco wants to merge 2 commits into
cppalliance:masterfrom
vinniefalco:master

Conversation

@vinniefalco

Copy link
Copy Markdown
Member

Summary

This adds plugin-std, the Pack that gives a Runvil agent the file tools every coding agent has, and it copies Cursor's surface rather than inventing one. A model already trained on Cursor's tools then needs no new instructions: it calls Read, Write, StrReplace, Delete, Glob, and Grep, with Cursor's parameter names and Cursor's own descriptions.

Every tool is a stub in this commit. A well-formed call answers a text saying the implementation is missing; a call whose arguments miss the schema fails with the parameter named. The stubs exist so the crate, the definitions, the ids, the wire names, and the Lua path are all proven before any file logic lands.

The series is two commits. 7357dd23e adds the vibelight cargo alias and came before this work; 08801150f is the Pack.

The Pack

  • crates/plugin-std installs runvil/std, so its Pack id is std and every tool id sits under it: std/read, std/write, std/replace, std/rm, std/glob, std/grep.
  • Lua calls a tool by its id, tools.call("std/read", { path = "/notes.md" }), and offers the Pack with tools.offer("std"). The model calls the same tool by its wire name, Read, which ToolInfo::wire_name sets. The two layers are deliberately separate: the Lua ids stay short and prefixed, and the wire names copy Cursor exactly.
  • Each tool's description and parameter schema are checked in under crates/plugin-std/schemas/ and embedded with include_str!. schemas/EDITS.md records every line changed from Cursor's text, as old line then new line, so the difference is auditable rather than folklore.
  • Four lines were edited, each because it describes behavior this build does not have. Read no longer promises images and PDFs, because it is text only. Grep no longer points at rg or at the Shell tool, because this Pack offers no shell. Two defaults that said "Cursor workspace root" now say the run's filesystem root.
  • The Pack takes no configuration but null or {} and needs no per-run service, matching plugin-user-input.
  • The crate reads no Host-wide service and names no async runtime. It performs no filesystem access at all in this commit.

Argument checking

  • src/check.rs validates a call against the stored schema, honoring type, properties, required, enum, and items. It runs before anything else, so a malformed call cannot reach an operation.
  • A refusal is an InvalidArguments ToolError that names the parameter and, where it helps, the value it received: std_Grep: pattern is required, and the call left it out.
  • A schema keyword the check does not understand is not a reason to refuse a call, and an undeclared parameter passes through, so the tool's own operation decides what it can do with the value. A schema with parameters that silently do nothing would be worse than a missing parameter.

The stub answer

  • A well-formed call returns ToolOutput::trusted, never a ToolError, with this text and the tool's wire name in both slots:

    The <name> tool is not implemented in this build. The implementation is missing and nothing in this session will change that. Calling <name> again is safe, but it will return this same result every time, whatever the arguments. Carry on without it.

  • It is a success on purpose. A valid call has to succeed for the success path and the failure path to both be exercised end to end, which they could not be if every call failed.

  • The text names the tool twice and says a repeat call is safe but identical, because a model that reads only "not implemented" may retry and conclude its arguments were wrong.

Wiring

  • crates/workshop/server/src/agents.rs installs the Pack in every agent run's Host, beside web, user-input, and mcp. The existing test that lists every installed Pack now expects std too.
  • crates/workshop/ui/test/docs-claims.mjs gains the crate to its Plugin doc list, so the wording scan reads its comments for the defined terms and the Pack-activation ban.
  • Cargo.lock gains the crate and its serde edge. No new outside dependency: serde and serde_json are already in the workspace.

The plan

vibe/2026-10-27-2-plugin-std-file-tools.md comes with the change and uses the same contract format as the other plans in vibe/. It records the shipped work as its first step, then specifies what is left: one schema correction, the six real operations over the call's VFS access, approval, and the shell.

What is deliberately not here

  • Shell. An in-process shell over the same filesystem is a separate Pack, so a run that wants one declares it.
  • Images and PDFs. Read is text only.
  • User approval for mutations. The VFS already answers an approval-required error under Ask mode; wiring it to an operator flow is later work.
  • ListDir. Cursor gates it behind enableDeprecatedLsTool, which defaults to false, and its replacement exists only as proto, so there is no model-facing definition worth copying.

Breaking changes

None. crates/runvil/public-api.txt is unchanged, because a Plugin crate is not part of the Engine's facade. The Engine, the Harness, and the gateway are untouched.

Test plan

cargo nextest run -p plugin-std passes, 32 tests. They cover each layer: the definitions parse and carry the expected wire names, the check accepts a matching call and refuses each kind of mismatch, the PackInfo and install behave as the Plugin contract requires, and every tool answers the stub text naming itself while each kind of bad call fails with the parameter named.

The full set ran over the whole change and passed: workspace nextest (5597 tests), workshop-server nextest (217 tests), both clippy runs with warnings denied, cargo fmt --all --check, the workspace rustdoc gate with warnings denied, cargo test -p build-xtask (264 tests), and the docs-claims scan. The pre-push hook then ran the docs-claims scan, the headless gateway check, workspace clippy, and cargo deny check again on the way out.

Nothing here touches the filesystem, so the VFS Mode policy gates nothing yet. Exercising Ask and Plan mode against these tools is part of the real-operation work, not this change.

Run vibelight from the repo root with cargo vibelight. The alias runs the sibling vibelight crate in release mode, so cargo builds it on first use and rebuilds it when its source changes.

- The path ../vibelight/Cargo.toml is relative to the cwd. The alias works only from the repo root, and only with vibelight checked out next to this repo.

- Nothing is tested. The alias was not run.
Every tool answers a stub that says the implementation is missing, and a call whose arguments miss the schema fails with the parameter named.

- `crates/plugin-std` offers `std/read`, `std/write`, `std/replace`, `std/rm`, `std/glob`, and `std/grep` under the wire names `Read`, `Write`, `StrReplace`, `Delete`, `Glob`, and `Grep`.
- Each tool's description and parameter schema are checked in under `schemas/` and embedded with `include_str!`, so the model-facing text is Cursor's own; `schemas/EDITS.md` records every line changed from it.
- The stub answer is a successful `ToolOutput::trusted` rather than a `ToolError`, so a well-formed call and a malformed one exercise different paths.
- `crates/workshop/server/src/agents.rs` installs the Pack in every agent run's Host beside `web`, `user-input`, and `mcp`.
- No operation touches the filesystem yet, so the VFS Mode policy gates nothing until the tools are real.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant