Workspace
Execute shell commands against a git repository at any point in history with optional auto-commit.
Looking for a higher-level filesystem API?
coregit.fs.mount()wraps these endpoints withwriteFile/readFile/bash().exec()and three commit modes for agentic workflows. Use it instead of building your own queue on top of the raw/execendpoints.
Execute Command
Run a shell command in the context of a repository. The filesystem is backed by git objects — reads are lazy, writes are held in memory. Optionally commit changes back to git in a single atomic operation.
POST /v1/repos/:slug/exec
POST /v1/repos/:namespace/:slug/execPermission: Write access required.
{
"command": "echo 'hello' > greeting.txt && cat greeting.txt",
"branch": "main",
"cwd": "/",
"env": { "NODE_ENV": "production" },
"commit": true,
"commit_message": "Add greeting file",
"author": { "name": "Agent", "email": "agent@example.com" }
}Fields
| Field | Required | Description |
|---|---|---|
command | Yes | Shell command to execute (max 10,000 chars) |
branch | No | Target branch (default: "main"). Used as filesystem source and commit target |
ref | No | Load filesystem from this ref (commit SHA, branch, or tag). Overrides branch for reads |
cwd | No | Working directory (default: "/") |
env | No | Environment variables |
commit | No | Commit changed files after execution (default: false) |
commit_message | If commit=true | Commit message |
author | No | { name, email } for the commit author |
pre_apply_changes | No | Buffered file changes to layer onto the in-memory overlay before the command runs. See Pre-applied changes. |
When ref is provided, the filesystem is loaded from that specific commit. If you also want to commit changes, you must provide branch — it determines which branch receives the new commit.
Pre-applied changes
pre_apply_changes is the bridge between SDK-side write buffering (e.g. fs.mount() in manual / on-exec modes) and server-side shell execution. Each entry is applied to the workspace overlay before bash starts, so cat, grep, npm, and friends see the buffered writes. Land them in a commit by also setting commit: true; otherwise they exist only for the duration of this exec.
{
"command": "cat src/README.md",
"pre_apply_changes": [
{ "path": "src/README.md", "action": "create", "content": "hello world", "encoding": "utf-8" },
{ "path": "src/old.md", "action": "delete" },
{ "path": "src/a.md", "action": "rename", "new_path": "src/b.md" }
]
}| Field | Required | Description |
|---|---|---|
path | Yes | Path inside the repo (no leading slash). |
action | No | "create" (default), "delete", or "rename". |
content | If action="create" | File contents. |
encoding | No | "utf-8" (default) or "base64". |
new_path | If action="rename" | New path inside the repo. |
edit and lazy_edit actions are intentionally not supported here — those need server-side merge logic that lives in the Commits API. Validation caps: max 1000 entries per request, max 10 MB content per file.
Response 200
{
"stdout": "hello\n",
"stderr": "",
"exit_code": 0,
"changed_files": ["greeting.txt"],
"commit_sha": "a1b2c3...",
"execution_time_ms": 42
}| Field | Description |
|---|---|
stdout | Standard output |
stderr | Standard error |
exit_code | Process exit code |
changed_files | List of files created or modified |
commit_sha | Commit SHA (only when commit=true) |
execution_time_ms | Execution time in milliseconds |
Use Cases
Execute on a specific commit in history
Use ref to load the filesystem from any commit SHA, branch, or tag:
{
"command": "cat package.json | grep version",
"ref": "a1b2c3d4e5f6..."
}Execute on an old commit and commit the result to a branch
{
"command": "npm run fix-legacy",
"ref": "v1.0.0",
"branch": "hotfix-v1",
"commit": true,
"commit_message": "fix: backport security patch to v1",
"author": { "name": "Agent", "email": "agent@example.com" }
}Install dependencies and commit lockfile
{
"command": "npm install express",
"branch": "main",
"commit": true,
"commit_message": "chore: add express dependency",
"author": { "name": "Agent", "email": "agent@example.com" }
}Run a build without committing
{
"command": "npm run build",
"branch": "main",
"commit": false
}SDK Example
// Execute on current branch HEAD
const { data } = await git.workspace.exec("my-repo", {
command: "ls -la",
branch: "main",
});
console.log(data.stdout);
// Execute on a specific commit SHA
const { data } = await git.workspace.exec("my-repo", {
command: "cat package.json",
ref: "a1b2c3d4...",
});
// Execute on an old commit, commit result to a branch
const { data } = await git.workspace.exec("my-repo", {
command: "npm run migrate",
ref: "v1.0.0",
branch: "hotfix",
commit: true,
commit_message: "fix: apply migration",
author: { name: "Agent", email: "agent@example.com" },
});
console.log(data.commit_sha);With namespaces:
const { data } = await git.workspace.exec(
{ namespace: "alice", slug: "my-repo" },
{
command: "npm test",
ref: "feature-branch",
},
);Multi-Repo Execution
Execute a command across multiple repositories mounted at virtual paths (/{slug}/).
POST /v1/workspace/execPermission: Read or write access to all specified repos.
{
"repos": [
{ "slug": "frontend", "branch": "main" },
{ "slug": "backend", "branch": "main" },
{ "slug": "shared-lib", "namespace": "libs" }
],
"command": "grep -r 'API_VERSION' .",
"commit": false
}Fields
| Field | Required | Description |
|---|---|---|
repos | Yes | Array of repos to mount (max 10) |
repos[].slug | Yes | Repository slug |
repos[].branch | No | Branch name (default: repo's default branch) |
repos[].namespace | No | Namespace for scoped repos |
command | Yes | Shell command to execute (max 10,000 chars) |
cwd | No | Working directory |
env | No | Environment variables |
commit | No | Commit changes per repo (default: false) |
commit_message | If commit=true | Commit message |
author | No | { name, email } for commit author |
pre_apply_changes | No | Same as the single-repo variant — but paths are shaped /<slug>/<rest> so the multi-repo router dispatches each change to the right mount. |
Each repo is mounted at /{slug}/ in the virtual filesystem. The command runs across all of them.
{
"repos": [{ "slug": "frontend" }, { "slug": "backend" }],
"command": "cat /frontend/src/README.md",
"pre_apply_changes": [
{ "path": "/frontend/src/README.md", "action": "create", "content": "hi", "encoding": "utf-8" }
]
}Response 200
{
"stdout": "frontend/src/config.ts: API_VERSION: '2'\nbackend/src/config.ts: API_VERSION: '2'\n",
"stderr": "",
"exit_code": 0,
"changed_files": {},
"commits": {},
"repos_mounted": ["frontend", "backend", "shared-lib"],
"execution_time_ms": 1200
}When commit=true, changes are committed independently per repo:
{
"changed_files": {
"frontend": [{ "path": "src/version.ts", "action": "modify" }],
"backend": [{ "path": "src/version.ts", "action": "modify" }]
},
"commits": {
"frontend": "a1b2c3...",
"backend": "d4e5f6..."
}
}SDK Example
// Search across repos
const { data } = await git.workspace.multiExec({
repos: [
{ slug: "frontend" },
{ slug: "backend" },
],
command: "grep -r 'deprecated' .",
});
console.log(data.stdout);
// Modify files across repos and commit
const { data } = await git.workspace.multiExec({
repos: [
{ slug: "frontend", branch: "main" },
{ slug: "backend", branch: "main" },
],
command: "sed -i 's/v1/v2/g' */src/config.ts",
commit: true,
commit_message: "chore: bump API version to v2",
author: { name: "Agent", email: "agent@example.com" },
});
console.log(data.commits); // { frontend: "a1b2...", backend: "d4e5..." }
// Flush SDK-buffered writes alongside the shell command
const { data } = await git.workspace.multiExec({
repos: [{ slug: "frontend" }, { slug: "backend" }],
command: "cat /frontend/src/README.md",
pre_apply_changes: [
{
path: "/frontend/src/README.md",
action: "create",
content: "hello",
encoding: "utf-8",
},
],
commit: true,
commit_message: "docs: seed README",
author: { name: "Agent", email: "agent@example.com" },
});For most agent workflows you don't construct pre_apply_changes by hand — use fs.mount() and the manual / on-exec commit modes, which build the array for you.