Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/nullclaw/nullclaw/llms.txt

Use this file to discover all available pages before exploring further.

Git Operations

The git_operations tool provides structured Git operations with comprehensive input sanitization and workspace scoping. Supported operations:
  • status — Show working tree status
  • diff — Show file differences
  • log — View commit history
  • branch — List branches
  • commit — Create commit
  • add — Stage files
  • checkout — Switch branches
  • stash — Stash changes
Security features:
  • Command injection prevention
  • Workspace path scoping with cwd override
  • Commit message truncation
  • Branch name validation

Parameters

operation
string
required
Git operation: status, diff, log, branch, commit, add, checkout, or stash
message
string
Commit message (required for commit operation)
paths
string
File paths to stage (required for add operation)
branch
string
Branch name (required for checkout operation)
files
string
Files to diff (defaults to . for all files)
cached
boolean
Show staged changes for diff operation (default: false)
limit
integer
Number of log entries (default: 10, max: 1000)
cwd
string
Repository directory (absolute path within allowed paths; defaults to workspace)

Configuration

const gt = try allocator.create(git.GitTool);
gt.* = .{
    .workspace_dir = "/path/to/workspace",
    .allowed_paths = &.{},  // Additional repo paths
};

Operations

status

Show working tree status in porcelain v2 format. Usage:
{
  "tool": "git_operations",
  "operation": "status"
}
Response:
# branch.oid 7a3b8c1d2e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b
# branch.head main
# branch.upstream origin/main
# branch.ab +0 -0
1 .M N... 100644 100644 100644 da39a3e... da39a3e... src/main.zig
? untracked.txt
Command:
git status --porcelain=2 --branch

diff

Show file differences. Usage (unstaged changes):
{
  "tool": "git_operations",
  "operation": "diff",
  "files": "src/main.zig"
}
Response:
diff --git a/src/main.zig b/src/main.zig
index 1234567..abcdefg 100644
--- a/src/main.zig
+++ b/src/main.zig
@@ -1,3 +1,4 @@
 const std = @import("std");
+const config = @import("config.zig");
 
 pub fn main() !void {
Usage (staged changes):
{
  "tool": "git_operations",
  "operation": "diff",
  "cached": true
}
Command:
git diff --unified=3 --cached -- .

log

View commit history. Usage:
{
  "tool": "git_operations",
  "operation": "log",
  "limit": 5
}
Response:
7a3b8c1d|Alice|alice@example.com|2026-03-01 12:00:00 -0800|Add git operations tool
6f5e4d3c|Bob|bob@example.com|2026-03-01 11:30:00 -0800|Update documentation
5e4d3c2b|Alice|alice@example.com|2026-02-28 16:45:00 -0800|Fix memory leak in shell tool
4d3c2b1a|Bob|bob@example.com|2026-02-28 14:20:00 -0800|Add browser tool tests
3c2b1a9e|Alice|alice@example.com|2026-02-28 10:15:00 -0800|Initial commit
Format: hash|author|email|date|message Command:
git log -5 --pretty=format:%H|%an|%ae|%ad|%s --date=iso

branch

List branches with current branch indicator. Usage:
{
  "tool": "git_operations",
  "operation": "branch"
}
Response:
main|*
feature-browser|
fix-memory-leak|
Format: branch_name|* (asterisk indicates current branch) Command:
git branch --format=%(refname:short)|%(HEAD)

commit

Create a commit with staged changes. Usage:
{
  "tool": "git_operations",
  "operation": "commit",
  "message": "Add git operations tool with input sanitization"
}
Response:
Committed: Add git operations tool with input sanitization
Command:
git commit -m "Add git operations tool with input sanitization"
Commit message truncation:
  • Messages truncated to 2000 bytes
  • UTF-8 boundary-aware truncation
  • Prevents extremely long commit messages
Example: Long message
{
  "tool": "git_operations",
  "operation": "commit",
  "message": "[2000+ characters]"
}
Truncated at UTF-8 boundary near 2000 bytes.
Empty commit messages are rejected: “Commit message cannot be empty”

add

Stage files for commit. Usage:
{
  "tool": "git_operations",
  "operation": "add",
  "paths": "src/tools/git.zig"
}
Response:
Staged: src/tools/git.zig
Multiple files:
{
  "tool": "git_operations",
  "operation": "add",
  "paths": "src/*.zig"
}
Response:
Staged: src/*.zig
Command:
git add -- src/tools/git.zig
Note: -- separator prevents path injection

checkout

Switch to a different branch. Usage:
{
  "tool": "git_operations",
  "operation": "checkout",
  "branch": "feature-browser"
}
Response:
Switched to branch: feature-browser
Command:
git checkout feature-browser
Branch name validation:
Branch names are validated to prevent injection:
  • Blocked: ;, |, `, $()
  • Allowed: a-z, A-Z, 0-9, /, -, _, .
Example: Blocked injection
{
  "tool": "git_operations",
  "operation": "checkout",
  "branch": "main; rm -rf /"
}
Response:
Unsafe git arguments detected

stash

Stash uncommitted changes. Push/save stash:
{
  "tool": "git_operations",
  "operation": "stash",
  "action": "push"
}
Response:
Saved working directory and index state WIP on main: 7a3b8c1 Add git tool
Pop stash:
{
  "tool": "git_operations",
  "operation": "stash",
  "action": "pop"
}
Response:
On branch main
Changes not staged for commit:
  modified:   src/main.zig
Dropped refs/stash@{0} (7a3b8c1d)
List stashes:
{
  "tool": "git_operations",
  "operation": "stash",
  "action": "list"
}
Response:
stash@{0}: WIP on main: 7a3b8c1 Add git tool
stash@{1}: WIP on feature: 6f5e4d3 Update docs
Default action: push

Command Sanitization

All string arguments are sanitized before passing to Git to prevent command injection.

Blocked Patterns

Dangerous git options:
  • --exec=cmd — Execute arbitrary command
  • --upload-pack=cmd — Remote command execution
  • --receive-pack=cmd — Remote command execution
  • --pager=cmd — Pager command injection
  • --editor=cmd — Editor command injection
  • -c key=value — Config injection
  • --no-verify — Bypass hooks
Shell metacharacters:
  • ; — Command separator
  • | — Pipe
  • > < — Redirection
  • ` — Backtick execution
  • $() — Command substitution

Examples

Blocked: Config injection
{
  "tool": "git_operations",
  "operation": "commit",
  "message": "-c core.sshCommand=evil"
}
Response:
Unsafe git arguments detected
Blocked: Command substitution
{
  "tool": "git_operations",
  "operation": "add",
  "paths": "$(whoami)"
}
Response:
Unsafe git arguments detected
Allowed: Normal paths
{
  "tool": "git_operations",
  "operation": "add",
  "paths": "src/main.zig"
}
Allowed

Working Directory Control

Default: workspace_dir

By default, git operations run in workspace_dir:
{
  "tool": "git_operations",
  "operation": "status"
}
Runs git status in /workspace

Custom cwd (within workspace)

{
  "tool": "git_operations",
  "operation": "log",
  "cwd": "/workspace/subproject"
}
Runs git log in /workspace/subproject

Custom cwd (outside workspace)

Requires allowed_paths configuration:
gt.* = .{
    .workspace_dir = "/workspace",
    .allowed_paths = &.{
        "/data/repos",
        "/home/user/projects",
    },
};
{
  "tool": "git_operations",
  "operation": "status",
  "cwd": "/data/repos/project-a"
}
Allowed (in allowed_paths)
{
  "tool": "git_operations",
  "operation": "status",
  "cwd": "/tmp/repo"
}
Blocked:
cwd is outside allowed areas

cwd Validation Rules

  • cwd must be an absolute path
  • cwd must be within workspace_dir or allowed_paths
  • cwd is resolved with realpathAlloc() to prevent symlink escapes
  • Relative cwd is rejected: “cwd must be an absolute path”

Error Handling

Repository Not Found

{
  "tool": "git_operations",
  "operation": "status",
  "cwd": "/workspace/not-a-repo"
}
Response:
fatal: not a git repository (or any of the parent directories): .git

Nothing to Commit

{
  "tool": "git_operations",
  "operation": "commit",
  "message": "Empty commit"
}
Response:
On branch main
nothing to commit, working tree clean

Branch Not Found

{
  "tool": "git_operations",
  "operation": "checkout",
  "branch": "nonexistent-branch"
}
Response:
error: pathspec 'nonexistent-branch' did not match any file(s) known to git

No Files to Add

{
  "tool": "git_operations",
  "operation": "add",
  "paths": "nonexistent.txt"
}
Response:
fatal: pathspec 'nonexistent.txt' did not match any files

Use Cases

Check Status Before Commit

{
  "tool": "git_operations",
  "operation": "status"
}

Stage and Commit Changes

[
  {
    "tool": "git_operations",
    "operation": "add",
    "paths": "src/tools/git.zig"
  },
  {
    "tool": "git_operations",
    "operation": "commit",
    "message": "Add git operations tool"
  }
]

View Recent Commits

{
  "tool": "git_operations",
  "operation": "log",
  "limit": 10
}

Switch Branch

{
  "tool": "git_operations",
  "operation": "checkout",
  "branch": "feature-branch"
}

Stash Before Switching

[
  {
    "tool": "git_operations",
    "operation": "stash",
    "action": "push"
  },
  {
    "tool": "git_operations",
    "operation": "checkout",
    "branch": "main"
  }
]

Review Staged Changes

{
  "tool": "git_operations",
  "operation": "diff",
  "cached": true
}

Source

src/tools/git.zig:10-335

Testing

Run git tool tests:
zig build test --summary all
Tests cover:
  • All operations (status, diff, log, branch, commit, add, checkout, stash)
  • Command sanitization (injection prevention)
  • Working directory validation
  • Error cases
  • Commit message truncation
  • Branch name validation