Skip to content

feat(sessions): add git context layer to system prompt #1520

Description

@petabridge-netclaw

feat(sessions): add git context layer to system prompt

Summary

Agents running inside Netclaw sessions frequently lose track of which git branch they're on, which commit they've diverged from, whether their working tree is dirty, and what remote they're working against. This leads to:

  • Committing to the wrong branch
  • Pushing to a stale upstream without rebasing
  • Trying to commit changes that were already committed elsewhere
  • Confusion about which repository they're working in

Issue #595 tracked session CWD in WorkingContext and emits [working-context] with the project_dir path. Issue #596 resolved path-taking tools against session CWD. Neither added git-specific context — the path to the repo is known, but its state (branch, commit, dirty flags) is not communicated to the agent.

This issue proposes a new GitContextLayerProvider that reads git state from the working directory's .git and injects it into every turn's system prompt as a [git-context] block.

Why this is needed

The [working-context] block now exposes:

[working-context]
project_dir: /home/netclaw/.netclaw/workspaces/netclaw
recent_files:
  - /path/to/file1.cs
[/working-context]

The agent sees the directory path but cannot determine:

  • Which branch HEAD points to
  • Whether the working tree is clean or dirty
  • What commit is checked out
  • What remote URLs are configured
  • Whether there are untracked files that might interfere with operations

This is especially problematic in multi-project sessions where the agent works across multiple repos, or when it spawns subagents that inherit the same project_dir but have no explicit git grounding.

Proposal

Add a new context layer provider that emits a [git-context] block on every turn:

[git-context]
repo: /home/netclaw/.netclaw/workspaces/netclaw
branch: feature/git-context-layer
remote: origin (https://github.com/netclaw-dev/netclaw.git)
head: a1b2c3d4 (HEAD)
dirty: false
untracked: 0
staged: 0
[/git-context]

Fields

Field Source Notes
repo git rev-parse --show-toplevel Canonical repo path
branch git branch --show-current Empty string if detached
remote git remote get-url origin (or first remote) Label + URL
head git rev-parse HEAD Short SHA + branch ref if available
dirty git status --porcelain --untracked-files=no true/false
untracked git status --porcelain -u minus dirty Count of untracked files
staged git diff --cached --name-only count Number of staged changes

Timing

ContextLayerTiming.EveryTurn — the git state changes during tool execution (the agent commits, edits files, pushes), so every turn needs a fresh reading. This matches the pattern of CurrentTimeContextLayer.

Implementation sketch

public sealed class GitContextLayerProvider : IContextLayerProvider
{
    public ContextLayerTiming Timing => ContextLayerTiming.EveryTurn;

    public string GetContextLayer(TrustAudience audience)
    {
        // 1. Check if projectDirectory (from WorkingContext) is a git repo
        // 2. If not a git repo, return string.Empty (layer is suppressed)
        // 3. If git repo, run git commands to collect state
        // 4. Format as [git-context] block
    }
}

The provider receives the projectDirectory from WorkingContext (which is already available on every turn) to avoid scanning parent directories for .git. If the project directory is not a git repo root, the provider returns string.Empty and the layer is suppressed entirely.

DI registration

Register alongside existing IContextLayerProvider implementations in Program.cs:

services.AddSingleton<GitContextLayerProvider>();
services.AddSingleton<IContextLayerProvider>(sp => sp.GetRequiredService<GitContextLayerProvider>());

This is the same pattern used by CurrentTimeContextLayer (see Program.cs line 834). The GitContextLayerProvider will automatically be included in the IReadOnlyList<IContextLayerProvider> that SessionMessageAssembler.BuildVolatileContextBlock() iterates.

Subagent handling

Subagents inherit project_dir from the parent session as read-only grounding. The git context layer will also be injected into subagent turns since the context layer iteration is per-turn, not per-session. This gives subagents explicit git state without needing to query it themselves.

Scope

  • In scope: Local git state (branch, commit, dirty/untracked/staged counts, remote URL)
  • Out of scope: GitHub PR/issue context (would require API calls and separate caching strategy)

Open questions

  1. What about non-git projects? The provider returns string.Empty when not in a git repo, so the block is suppressed. No impact on non-git workspaces.
  2. Performance? git status is typically <5ms on local repos. Even on larger repos, it's under 50ms in practice. This is well within the per-turn budget.
  3. Should dirty state be more detailed? The initial implementation uses boolean + counts. If agents need more granularity (e.g., which files are modified), we can extend the block later.
  4. Should we include tags/branches with local changes? Out of scope for v1, but could be a nice-to-have if agents need to know about local-only branches.

Testing

  • Unit tests in Netclaw.Actors.Tests for the context layer itself (happy path, detached HEAD, no git repo, no remotes)
  • Integration tests in SessionMessageAssemblerTests verifying the block appears in the assembled volatile context
  • Manual verification that agents correctly interpret and act on the git context

Metadata

Metadata

Assignees

No one assigned

    Labels

    context-pipelineLLM context assembly: prompt layers, dynamic injection, memory recall, temporal groundingenhancementNew feature or requestsessionsLLM session actor, turn lifecycle, pipelines

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions