Workspace tools
The workspace.* tools: read, search, change, commit and push a throwaway clone of a repository.
Generated from the built-in tools registry. Do not edit by hand.
A run whose task declares workspace.* tools gets a throwaway clone of the repository named in its repo input. The repository sets the clone up itself (its .os/workspace.yml, or an install the runner detects), and the clone is destroyed when the run ends. A task without workspace.* tools never gets one.
These tools work on that clone only. Issues and pull requests stay with the enterprise's own GitHub tools. What a task may change is its write policy: writeAllow, rewriteAllow, writeDeny and appendOnly on the task version.
| Tool | What it does | Effect |
|---|---|---|
workspace.read_file |
Read a file from the clone, whole or one numbered window of it. | read |
workspace.list_dir |
List a directory in the clone. | read |
workspace.write_file |
Write a whole file in the clone. | write |
workspace.delete_file |
Delete a file from the clone outright. | write |
workspace.edit |
Replace one exact piece of text in a file. | write |
workspace.search |
Search the clone for a string or pattern. | read |
workspace.run_command |
Run one allowlisted binary in the clone, with no shell. | write |
workspace.git_commit |
Commit the tracked changes and the files this run wrote. | write |
workspace.git_push |
Push the current branch, never the default one. | write |
workspace.read_file
Read a file from the clone, whole or one numbered window of it.
What the model reads:
Read a file from the run workspace. Pass path for the whole file, or add startLine (1-based) and limit to read one numbered window of a large file. Prefer a window over a scratch script.
| Field | Type | Required | Description |
|---|---|---|---|
path |
string | yes | |
startLine |
number | no | First line to read, 1-based. Omit to read the whole file. |
limit |
number | no | How many lines to read from startLine. Default 200. |
- Effect: read.
- Retry: Safe: repeating the call changes nothing.
- Called by: a run.
- Needs:
- A
repoinput on the run — a git remote address, or whatever short form the code host binding'sgit.repoTemplatecustom prop expands — which the runner clones. A code host binding may name the repository instead, in itsgit.repoprop. A task withoutworkspace.*tools never gets a clone.
- A
- Returns: The file as UTF-8 text, or the requested window of it with line numbers.
| Error | When | What happens |
|---|---|---|
exploration_budget_exceeded |
Too many reads back over ground already covered with no write, edit or commit in between, or more distinct files, folders and searches than the per-run budget. | Tool error |
Example:
{
"path": "src/index.ts"
}
Example:
{
"path": "src/index.ts",
"startLine": 200,
"limit": 80
}
workspace.list_dir
List a directory in the clone.
What the model reads:
List a directory in the run workspace. Pass path (default .).
| Field | Type | Required | Description |
|---|---|---|---|
path |
string | no |
- Effect: read.
- Retry: Safe: repeating the call changes nothing.
- Called by: a run.
- Needs:
- A
repoinput on the run — a git remote address, or whatever short form the code host binding'sgit.repoTemplatecustom prop expands — which the runner clones. A code host binding may name the repository instead, in itsgit.repoprop. A task withoutworkspace.*tools never gets a clone.
- A
- Returns: The directory listing.
| Error | When | What happens |
|---|---|---|
exploration_budget_exceeded |
Too many reads back over ground already covered with no write, edit or commit in between, or more distinct files, folders and searches than the per-run budget. | Tool error |
Example:
{
"path": "src"
}
workspace.write_file
Write a whole file in the clone.
What the model reads:
Write a file in the run workspace. Pass path and content.
| Field | Type | Required | Description |
|---|---|---|---|
path |
string | yes | |
content |
string | yes |
- Effect: write.
- Retry: Idempotent: a repeat of the same call acts once, or leaves the same state. Writing the same content again leaves the same file.
- Called by: a run.
- Needs:
- The task version's
workspace.writeAllow,rewriteAllow,writeDenyandappendOnlydecide which paths it may change. - A
repoinput on the run — a git remote address, or whatever short form the code host binding'sgit.repoTemplatecustom prop expands — which the runner clones. A code host binding may name the repository instead, in itsgit.repoprop. A task withoutworkspace.*tools never gets a clone.
- The task version's
- Returns:
{ ok: true }.
| Error | When | What happens |
|---|---|---|
workspace_write_not_allowed |
The path is outside the task's write globs, or matches writeDeny. |
Tool error |
workspace_append_only |
The path is add-only for this task, and the call would change or remove a line it had when the run started. | Tool error |
Example:
{
"path": "src/greeting.ts",
"content": "export const greeting = 'hello'\n"
}
workspace.delete_file
Delete a file from the clone outright.
What the model reads:
Delete a file in the run workspace outright, instead of overwriting it empty. Pass path.
| Field | Type | Required | Description |
|---|---|---|---|
path |
string | yes |
- Effect: write.
- Retry: Not retry-safe: a repeat acts again. A repeat finds nothing to delete and fails
delete_file_missing; the file stays deleted. - Called by: a run.
- Needs:
- The task version's
workspace.writeAllow,rewriteAllow,writeDenyandappendOnlydecide which paths it may change. - A
repoinput on the run — a git remote address, or whatever short form the code host binding'sgit.repoTemplatecustom prop expands — which the runner clones. A code host binding may name the repository instead, in itsgit.repoprop. A task withoutworkspace.*tools never gets a clone.
- The task version's
| Error | When | What happens |
|---|---|---|
delete_file_missing |
Nothing is at that path. | Tool error |
delete_file_is_directory |
The path is a directory. | Tool error |
workspace_write_not_allowed |
The path is outside the task's write globs, or matches writeDeny. |
Tool error |
workspace_append_only |
The path is add-only for this task, and the call would change or remove a line it had when the run started. | Tool error |
Example:
{
"path": "src/unused.ts"
}
workspace.edit
Replace one exact piece of text in a file.
What the model reads:
Replace one exact piece of text in a workspace file. Pass path, oldString and newString. oldString must appear exactly once unless you pass replaceAll. Use this to change a file, not write_file, which rewrites the whole file.
| Field | Type | Required | Description |
|---|---|---|---|
path |
string | yes | |
oldString |
string | yes | Exact text to replace, whitespace included. |
newString |
string | yes | Replacement text. Empty string deletes oldString. |
replaceAll |
boolean | no | Replace every occurrence instead of requiring one. |
- Effect: write.
- Retry: Not retry-safe: a repeat acts again. A repeat no longer finds oldString and fails
edit_no_match; the first edit stands. - Called by: a run.
- Needs:
- The task version's
workspace.writeAllow,rewriteAllow,writeDenyandappendOnlydecide which paths it may change. - A
repoinput on the run — a git remote address, or whatever short form the code host binding'sgit.repoTemplatecustom prop expands — which the runner clones. A code host binding may name the repository instead, in itsgit.repoprop. A task withoutworkspace.*tools never gets a clone.
- The task version's
| Error | When | What happens |
|---|---|---|
edit_old_string_required |
oldString is missing or empty. |
Tool error |
edit_no_match |
The file does not contain oldString exactly. |
Tool error |
edit_not_unique |
oldString appears more than once and replaceAll is not set. |
Tool error |
edit_file_missing |
Nothing is at that path. | Tool error |
workspace_write_not_allowed |
The path is outside the task's write globs, or matches writeDeny. |
Tool error |
workspace_append_only |
The path is add-only for this task, and the call would change or remove a line it had when the run started. | Tool error |
Example:
{
"path": "src/greeting.ts",
"oldString": "'hello'",
"newString": "'hello, world'"
}
workspace.search
Search the clone for a string or pattern.
What the model reads:
Search the run workspace for a string. Pass pattern, optionally globs (e.g. ["apps/api/**/*.ts"]), ignoreCase, regex, maxResults. Returns path, line and text per match. Use this instead of writing a script to grep.
| Field | Type | Required | Description |
|---|---|---|---|
pattern |
string | yes | |
globs |
array of string | no | Limit to these path globs. |
ignoreCase |
boolean | no | |
regex |
boolean | no | Treat pattern as a POSIX extended regexp. |
maxResults |
number | no | Default 100, ceiling 500. |
- Effect: read.
- Retry: Safe: repeating the call changes nothing.
- Called by: a run.
- Needs:
- A
repoinput on the run — a git remote address, or whatever short form the code host binding'sgit.repoTemplatecustom prop expands — which the runner clones. A code host binding may name the repository instead, in itsgit.repoprop. A task withoutworkspace.*tools never gets a clone.
- A
- Returns: Path, line and text for each match.
| Error | When | What happens |
|---|---|---|
search_pattern_required |
pattern is missing or empty. |
Tool error |
exploration_budget_exceeded |
Too many reads back over ground already covered with no write, edit or commit in between, or more distinct files, folders and searches than the per-run budget. | Tool error |
Example:
{
"pattern": "TODO",
"globs": [
"src/**/*.ts"
],
"maxResults": 20
}
workspace.run_command
Run one allowlisted binary in the clone, with no shell.
What the model reads:
Run an allowlisted binary in the run workspace. Pass argv as a JSON array of strings. Example: {"argv":["git","status"]}. No shell, no pipes, no cd. Allowed bins: git, pnpm, npm, node, mise.
| Field | Type | Required | Description |
|---|---|---|---|
argv |
array of string | yes | argv[0] is the binary. Example ["pnpm","test"] |
- Effect: write.
- Retry: Not retry-safe: a repeat acts again. The command runs again.
- Called by: a run.
- Needs:
- A
repoinput on the run — a git remote address, or whatever short form the code host binding'sgit.repoTemplatecustom prop expands — which the runner clones. A code host binding may name the repository instead, in itsgit.repoprop. A task withoutworkspace.*tools never gets a clone.
- A
- Returns:
{ code, stdout, stderr }.
| Error | When | What happens |
|---|---|---|
argv_required |
argv is missing or is not an array of strings. |
Tool error |
bin_not_allowlisted |
argv[0] is not one of git, pnpm, npm, node, mise, or is a path. |
Tool error |
git_push_via_run_command |
The command would push. Push with workspace.git_push. |
Tool error |
git_hooks_skip_refused |
The command would skip the repo's git hooks (--no-verify, core.hooksPath). |
Tool error |
direct_typecheck_refused |
The command runs a typechecker (tsc), a task-graph runner (turbo) or a package-manager typecheck script directly, around the repo's verification task. Verify with mise run os:red <issue>, which returns the typecheck's exit code and compiler output; mise run <task> is never refused. |
Tool error |
scratch_execution_refused |
The command would execute a file this run created outside the task's full-write paths, whether run directly or loaded by an inline script (node -e "require('./tmp.mjs')"), and whether named in full or the way Node resolves it (node tmp, require('./tmp'), a directory's index). Use workspace.read_file, workspace.search or mise run os:red <issue> instead. Files already in the clone, and files in the full-write paths, run as before. |
Tool error |
Example:
{
"argv": [
"git",
"status"
]
}
Example:
{
"argv": [
"npm",
"test"
]
}
workspace.git_commit
Commit the tracked changes and the files this run wrote.
What the model reads:
Commit in the run workspace. Pass message. Stages tracked changes and the files you wrote or edited; a new file only a command created may be left out, so `git add` it with workspace.run_command first if it belongs in the commit.
| Field | Type | Required | Description |
|---|---|---|---|
message |
string | yes |
- Effect: write.
- Retry: Not retry-safe: a repeat acts again. A repeat makes another commit, even an empty one.
- Called by: a run.
- Needs:
- The task version's
workspace.writeAllow,rewriteAllow,writeDenyandappendOnlydecide which paths it may change. - A
repoinput on the run — a git remote address, or whatever short form the code host binding'sgit.repoTemplatecustom prop expands — which the runner clones. A code host binding may name the repository instead, in itsgit.repoprop. A task withoutworkspace.*tools never gets a clone.
- The task version's
| Error | When | What happens |
|---|---|---|
workspace_commit_not_allowed |
The commit would include paths outside the task's write globs. Nothing is committed. | Tool error |
workspace_append_only |
The path is add-only for this task, and the call would change or remove a line it had when the run started. | Tool error |
git_commit_failed |
git refused the commit, for example a pre-commit hook failed. | Tool error |
Example:
{
"message": "Add the greeting"
}
workspace.git_push
Push the current branch, never the default one.
What the model reads:
Push the current non-default branch. No arguments.
No declared fields: the schema accepts any object.
- Effect: write.
- Retry: Idempotent: a repeat of the same call acts once, or leaves the same state. A repeat with nothing new to push changes nothing.
- Called by: a run.
- Needs:
- The task version's
workspace.writeAllow,rewriteAllow,writeDenyandappendOnlydecide which paths it may change. - A
repoinput on the run — a git remote address, or whatever short form the code host binding'sgit.repoTemplatecustom prop expands — which the runner clones. A code host binding may name the repository instead, in itsgit.repoprop. A task withoutworkspace.*tools never gets a clone. - A code host tool bound in the run's cascade whose
git.hostcustom prop matches the remote: the push authenticates with that binding's credential, and with no other.
- The task version's
| Error | When | What happens |
|---|---|---|
default_branch_refused |
The current branch is the default branch. | Tool error |
workspace_append_only |
The path is add-only for this task, and the call would change or remove a line it had when the run started. | Tool error |
git_push_failed |
git refused the push, for example the repo's pre-push hook failed. | Tool error |