[FEATURE]: Native git tool for repository context awareness
Feature hasn't been suggested before.
- [x] I have verified this feature I'm about to request hasn't been suggested before.
Describe the enhancement you want to request
I propose a feature to add a first-class native git tool (similar to grep, glob, lsp_diagnostics) that gives the LLM structured access to git repository state. This would allow the agent to understand what the user has been working on without relying on unstructured bash calls to git commands.
For context, I thought of this when working on a MR at work that did two separate but related changes. I originally sent a message with just the second set of changes I needed, but I needed to rewrite the prompt to include context about what I had already changed that necessitated the new changes. Ideally, the agent could've known "This user indicated they are working on a new MR, let me look at what changes they've already written.
Problem
When a user says something like:
- "I'm working on this PR, can you help me finish it?"
- "Continue where I left off"
- "Review what I've changed"
The LLM currently has no efficient way to understand the git context. The workarounds are:
- User manually explains — Friction, easy to forget details
- LLM calls bash with git commands — Returns unstructured text, wastes context tokens, requires multiple calls (
git status,git diff,git log, etc.) - User pastes diffs — Manual, breaks flow
A native tool would let the LLM proactively check "what has the user been working on" and respond with full context.
A git tool that returns structured, LLM-optimized output:
// Example tool signature
git({
scope: "pr" | "working" | "staged" | "commits",
base?: string, // Base branch for PR diff (default: main/master)
limit?: number, // Number of commits to return (for scope: "commits")
file?: string, // Get full diff for a specific file (useful if diff was truncated)
})
Example outputs:
// scope: "pr" — full diff against base branch
{
"branch": "feature/user-auth",
"base": "main",
"ahead_by": 3,
"behind_by": 0,
"files": [
{
"path": "src/auth.ts",
"status": "modified",
"insertions": 45,
"deletions": 3,
"diff": "@@ -12,6 +12,48 @@ import { verify } from 'jsonwebtoken';\n \n+export function validateJWT(token: string): boolean {\n+ try {\n+ const decoded = verify(token, process.env.JWT_SECRET);\n+ return !!decoded;\n+ } catch {\n+ return false;\n+ }\n+}\n ..."
},
{
"path": "src/middleware.ts",
"status": "modified",
"insertions": 12,
"deletions": 2,
"diff": "@@ -5,7 +5,17 @@ export const authMiddleware = (req, res, next) => {\n- // TODO: implement\n- next();\n+ const token = req.headers.authorization?.split(' ')[1];\n+ if (!token || !validateJWT(token)) {\n+ return res.status(401).json({ error: 'Unauthorized' });\n+ }\n+ next();\n };"
},
{
"path": "tests/auth.test.ts",
"status": "added",
"insertions": 88,
"deletions": 0,
"diff": "@@ -0,0 +1,88 @@\n+import { validateJWT } from '../src/auth';\n+\n+describe('validateJWT', () => {\n+ it('should return true for valid token', () => {\n ..."
}
]
}
// scope: "working" — unstaged and staged changes with diffs
{
"branch": "feature/user-auth",
"staged": [
{
"path": "src/auth.ts",
"status": "modified",
"diff": "@@ -50,6 +50,10 @@ export function validateJWT(token: string): boolean {\n+ // Add token expiry check\n+ if (decoded.exp < Date.now() / 1000) {\n+ return false;\n+ }\n ..."
}
],
"unstaged": [
{
"path": "src/middleware.ts",
"status": "modified",
"diff": "@@ -15,3 +15,7 @@ export const authMiddleware = ...\n+\n+export const optionalAuth = (req, res, next) => {\n+ // TODO: implement\n+};"
}
],
"untracked": ["scratch.ts"],
"conflicts": []
}
// scope: "commits" — recent commits with their diffs
{
"branch": "feature/user-auth",
"commits": [
{
"hash": "abc1234",
"message": "Add JWT validation",
"author": "[email protected]",
"date": "2025-01-09T10:00:00Z",
"files": [
{
"path": "src/auth.ts",
"status": "modified",
"diff": "@@ -12,6 +12,48 @@\n+export function validateJWT..."
}
]
},
{
"hash": "def5678",
"message": "Update middleware to use auth",
"author": "[email protected]",
"date": "2025-01-09T09:30:00Z",
"files": [
{
"path": "src/middleware.ts",
"status": "modified",
"diff": "@@ -5,7 +5,17 @@\n- // TODO: implement..."
}
]
}
]
}
Why a native tool instead of MCP or custom tool?
One of the reasons I prefer OpenCode is the native LSP support. Git is such a native part of everyone's editor(think to how much you inspect gitsigns while working), it should be native for LLMs.
Example interaction
User: "I'm working on this PR, help me finish it"
Agent: [calls git tool with scope="pr"]
Agent: "I see you're 3 commits into `feature/user-auth` based off `main`.
You've added JWT validation and updated the auth middleware.
The tests file exists but looks incomplete. Want me to finish the test coverage?"
Related issues
- #1924 — Git Branch-Aware Sessions (related: associating sessions with branches)
- #4152 — Integrate /undo with Git (related: git integration for change tracking)
Implementation notes
I'm happy to work on a PR for this, but I am more interested in opening the discussion. I have not contributed to OpenCode yet and am not very familiar with the way you guys design. Even the APIs I've described here are mostly just me trying to provide insight into how I'm thinking. I'm sure you guys will want to change the implementation details.
Let me know if this aligns with the project direction or if there are concerns about the approach.