Skip to content

feat(tools): add read-only "git" toolset for structured repository inspection#3637

Open
dwin-gharibi wants to merge 14 commits into
docker:mainfrom
dwin-gharibi:feat/git-tool
Open

feat(tools): add read-only "git" toolset for structured repository inspection#3637
dwin-gharibi wants to merge 14 commits into
docker:mainfrom
dwin-gharibi:feat/git-tool

Conversation

@dwin-gharibi

Copy link
Copy Markdown
Contributor

Closes #3636


Summary

Adds a new built-in toolset, git, giving an agent structured, read-only access to the working repository: status, log, branches, commit details, and blame. Implemented with go-git (already a direct dependency), so it needs no git binary and is fully unit-testable.

Tools: git_status, git_log, git_branches, git_show, git_blame.

Motivation

Coding agents constantly need git context. Today the only option is the shell tool with raw git, which forces the model to parse free-form porcelain, exposes the full writable shell surface, and depends on a git binary. A dedicated read-only toolset returns clean structured output, is safe by construction, and works without shell or the binary.

What changed

File Change
pkg/tools/builtin/git/git.go New. The toolset: 5 read-only tools over go-git.
pkg/tools/builtin/git/git_test.go New. Tests build throwaway repos with go-git and exercise every tool.
pkg/teamloader/toolsets/toolsets.go Register the git creator.
pkg/teamloader/toolsets/catalog.go Add the git catalog entry (required by the drift-guard test).
docs/configuration/tools/index.md Add git to the built-in toolsets table.
docs/tools/git/index.md New. Dedicated docs page.

Design

  • Read-only by design. No add/commit/checkout — write ops are stateful, overlap shell, and raise permission questions; deferred to keep v1 safe.
  • go-git, not the CLI — pure Go, no binary dependency, deterministically testable. The repo opens at the agent's WorkingDir with DetectDotGit.
  • The catalog drift-guard test forces the catalog entry: registering the type without documenting it fails the build.

Example output

$ git_status
On branch master
1 changed file(s) [XY = staged/worktree; M=modified A=added D=deleted R=renamed ?=untracked]:
   M main.go

$ git_log
b3951c61  2026-01-02  Alice  feat: print hi
aac3216e  2026-01-02  Alice  feat: initial commit

$ git_show
commit b3951c61b84a0c298552096542bbd3bd7f58840b
Author: Alice <alice@example.com>
Date:   2026-01-02 03:04:05 +0000

feat: print hi

Changed files:
  main.go (+1, -1)

$ git_blame main.go
aac3216e alice@example.com          1| package main
b3951c61 alice@example.com          3| func main() { println("hi") }

Testing

go test ./pkg/tools/builtin/git/ ./pkg/teamloader/toolsets/
  • git_status (changed + clean), git_log (order + limit), git_branches (current marked), git_show (metadata + stats), git_blame (authorship), plus not-a-repo and missing-path error paths, and the tool-count/interface check.
  • TestBuiltinToolsetsCatalogMatchesRegistry passes with git in both registry and catalog.

All pass; gofmt -l and go vet clean. (CI runs task lint / task test.)

Scope / non-goals

  • Read-only v1 — no write operations.
  • No working-tree line diff (go-git's worktree diff is limited); git_show covers commit-level diffs. Both are noted as possible future work.

@dwin-gharibi
dwin-gharibi requested a review from a team as a code owner July 14, 2026 07:19
@dwin-gharibi

Copy link
Copy Markdown
Contributor Author

Let's just continue. @aheritier @Sayt-0

@aheritier aheritier added area/docs Documentation changes area/tools For features/issues/fixes related to the usage of built-in and MCP tools kind/feat PR adds a new feature (maps to feat:). Use on PRs only. labels Jul 14, 2026

@Sayt-0 Sayt-0 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary

Useful addition: structured, read-only git context without the writable shell surface or a git binary, and the implementation follows the builtin-toolset patterns closely (creator + catalog + drift-guard, annotations, instructions, docs page, atomic conventional commits). Two blocking items prevent approval as-is; both are quick fixes. Inline comments follow.

Blocking

# Finding Where Impact
1 golangci-lint fails on the branch: 10x forbidigo (context.Background() in tests is banned, use t.Context()) and 1x modernize (strings.Cut). Note: the linter output shows only 3 of the 10 forbidigo hits because of max-same-issues: 3. pkg/tools/builtin/git/git_test.go, pkg/tools/builtin/git/git.go The ci / lint job will fail. The green checks currently on the PR are docs-only workflows; the Go CI has not run yet.
2 agent-schema.json is not updated: the Toolset.type enum does not include "git". agent-schema.json (Toolset.type enum, near the scheduler / rag entries) Editors validating configs against the schema flag type: git as invalid, and any future docs example using git fails pkg/config/schema_test.go. The catalog drift-guard was handled, but this second registration point was missed.

Non-blocking suggestions

  • git_status on a freshly initialized repository (unborn HEAD) prints On branch (detached HEAD), and git_log returns Error: reading log: reference not found. Friendlier handling ("no commits yet") would help agents on brand-new repos.
  • Docs: a short caveat on go-git performance would set expectations. Worktree().Status() rehashes the whole worktree (slow on large repos) and Blame cost grows with history x file size; maxBlameLines truncates output only after the full computation.
  • Docs: DetectDotGit walks up parent directories. When the working directory is not itself a repository but an ancestor is (for example a dotfiles-tracked home directory), git_show / git_blame expose the ancestor repository's full history and file contents. Worth an explicit note in the docs page, since filesystem confinement (allow/deny lists) does not apply here.
  • Test coverage gaps (fast follows): git_log path filter, git_show with an explicit and an invalid ref, git_blame with rev, the 400-line truncation path, and CreateToolSet.

Verdict

Request changes for items 1 and 2; everything else is optional. With those fixed, the toolset looks good to land as a read-only v1.

Comment thread pkg/tools/builtin/git/git_test.go Outdated
Comment thread pkg/tools/builtin/git/git.go Outdated
Comment thread pkg/tools/builtin/git/git.go Outdated
Comment thread pkg/tools/builtin/git/git.go Outdated
Comment thread pkg/tools/builtin/git/git.go
Comment thread docs/tools/git/index.md Outdated
Comment thread pkg/teamloader/toolsets/toolsets.go
@dwin-gharibi

Copy link
Copy Markdown
Contributor Author

@Sayt-0 Thank you for the review - the reproduction detail (including the max-same-issues: 3 note, which would have had me chasing 3 of 10) made this quick to act on. I have pushed an update addressing both blocking items and all four suggestions.

Blocking

  1. golangci-lint on the branch. Fixed both:

    • forbidigo: replaced all 10 context.Background() occurrences in git_test.go with t.Context(). As you predicted the linter only showed 3, so I fixed them from the file rather than the report - and the context import became orphaned as a result, which I removed.
    • modernize: firstLine now uses strings.Cut exactly as suggested.
  2. agent-schema.json not updated. Added "git" to the toolset type enum - and to the anyOf branch for types requiring no extra fields, which is the part I would otherwise have missed. fix(config): add scheduler toolset to agent-schema.json type enum #3670 showed that adding only to the primary enum still fails with Must validate at least one schema (anyOf), so both registration points are covered.

Suggestions (all taken)

  1. Unborn HEAD. A freshly initialized repo now reports On branch (no commits yet) from git_status, and git_log returns No commits yet. instead of Error: reading log: reference not found (checked via plumbing.ErrReferenceNotFound). Covered by TestGitUnbornHead.

  2. limit upper bound. Capped at 200 (maxLogLimit), mirroring the maxBlameLines approach, and the parameter description now says so. TestGitLogLimitIsCapped asserts limit: 1000000 returns exactly 200 lines.

  3. ro inconsistency. Dropped the variable and inlined tools.ToolAnnotations{ReadOnlyHint: true, Title: "Git Branches"} so all five tools are consistent and git_branches now has a Title.

  4. Docs caveats. Added both:

    • A warning that DetectDotGit walks up parent directories, so a non-repo working directory under a tracked ancestor (e.g. dotfiles in $HOME) resolves to that ancestor and exposes its history — and that the filesystem allow/deny lists do not apply. That one is worth having in writing; thank you for catching it.
    • A performance note that Status() rehashes the worktree and Blame scales with history × file size, with the explicit point that maxBlameLines truncates only after the computation.
  5. Test coverage gaps. Added tests for git_log path filter, git_show with an explicit ref and an invalid ref, git_blame with rev, the blame truncation path, and CreateToolSet (working-directory wiring).

  6. Docs weight. Moved to 125 to resolve the collision with docs/tools/mcp/index.md (130).

On start_line / end_line for blame: I have left that out of v1 as you suggested, and noted the 400-line cutoff in the docs. Happy to add the range parameters in this PR if you would rather have them now.

Do let me know if any change is required please.

@dwin-gharibi
dwin-gharibi requested a review from Sayt-0 July 16, 2026 16:38
@dwin-gharibi

Copy link
Copy Markdown
Contributor Author

Fixed malformed agent-schema.json @Sayt-0.

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

Labels

area/docs Documentation changes area/tools For features/issues/fixes related to the usage of built-in and MCP tools kind/feat PR adds a new feature (maps to feat:). Use on PRs only.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add a read-only "git" built-in toolset for structured repository inspection

3 participants