chore: Refactor git context gathering for session and init [LET-7906] (#1379)

This commit is contained in:
Devansh Jain
2026-03-13 14:44:45 -07:00
committed by GitHub
parent 423a4a8c92
commit 4bc3045f68
5 changed files with 163 additions and 103 deletions

View File

@@ -230,7 +230,7 @@ import { parsePatchOperations } from "./helpers/formatArgsDisplay";
import {
buildInitMessage,
fireAutoInit,
gatherGitContext,
gatherInitGitContext,
} from "./helpers/initCommand";
import {
getReflectionSettings,
@@ -9531,7 +9531,7 @@ export default function App({
true,
);
const gitContext = gatherGitContext();
const { context: gitContext } = gatherInitGitContext();
const memoryDir = settingsManager.isMemfsEnabled(agentId)
? getMemoryFilesystemRoot(agentId)
: undefined;

View File

@@ -0,0 +1,117 @@
import { execFileSync } from "node:child_process";
export interface GitContextSnapshot {
isGitRepo: boolean;
branch: string | null;
status: string | null;
recentCommits: string | null;
gitUser: string | null;
}
export interface GatherGitContextOptions {
cwd?: string;
recentCommitLimit?: number;
/**
* Git log format string passed to `git log --format=...`.
* If omitted, uses `git log --oneline`.
*/
recentCommitFormat?: string;
statusLineLimit?: number;
}
function runGit(args: string[], cwd: string): string | null {
try {
return execFileSync("git", args, {
cwd,
encoding: "utf-8",
stdio: ["ignore", "pipe", "ignore"],
}).trim();
} catch {
return null;
}
}
function truncateLines(value: string, maxLines: number): string {
const lines = value.split("\n");
if (lines.length <= maxLines) {
return value;
}
return (
lines.slice(0, maxLines).join("\n") +
`\n... and ${lines.length - maxLines} more changes`
);
}
function formatGitUser(
name: string | null,
email: string | null,
): string | null {
if (!name && !email) {
return null;
}
if (name && email) {
return `${name} <${email}>`;
}
return name || email;
}
export function gatherGitContextSnapshot(
options: GatherGitContextOptions = {},
): GitContextSnapshot {
const cwd = options.cwd ?? process.cwd();
const recentCommitLimit = options.recentCommitLimit ?? 3;
if (!runGit(["rev-parse", "--git-dir"], cwd)) {
return {
isGitRepo: false,
branch: null,
status: null,
recentCommits: null,
gitUser: null,
};
}
const branch = runGit(["branch", "--show-current"], cwd);
const fullStatus = runGit(["status", "--short"], cwd);
const status =
typeof fullStatus === "string" && options.statusLineLimit
? truncateLines(fullStatus, options.statusLineLimit)
: fullStatus;
const recentCommits = options.recentCommitFormat
? runGit(
[
"log",
`--format=${options.recentCommitFormat}`,
"-n",
String(recentCommitLimit),
],
cwd,
)
: runGit(["log", "--oneline", "-n", String(recentCommitLimit)], cwd);
const userConfig = runGit(
["config", "--get-regexp", "^user\\.(name|email)$"],
cwd,
);
let userName: string | null = null;
let userEmail: string | null = null;
if (userConfig) {
for (const line of userConfig.split("\n")) {
if (line.startsWith("user.name "))
userName = line.slice("user.name ".length);
else if (line.startsWith("user.email "))
userEmail = line.slice("user.email ".length);
}
}
const gitUser = formatGitUser(userName, userEmail);
return {
isGitRepo: true,
branch,
status,
recentCommits,
gitUser,
};
}

View File

@@ -14,6 +14,7 @@ import {
} from "../../agent/memoryFilesystem";
import { SYSTEM_REMINDER_CLOSE, SYSTEM_REMINDER_OPEN } from "../../constants";
import { settingsManager } from "../../settings-manager";
import { gatherGitContextSnapshot } from "./gitContext";
import { getSnapshot as getSubagentSnapshot } from "./subagentState";
// ── Guard ──────────────────────────────────────────────────
@@ -29,69 +30,38 @@ export function hasActiveInitSubagent(): boolean {
// ── Git context ────────────────────────────────────────────
export function gatherGitContext(): string {
export function gatherInitGitContext(): { context: string; identity: string } {
try {
const cwd = process.cwd();
const git = gatherGitContextSnapshot({
recentCommitLimit: 10,
});
if (!git.isGitRepo) {
return {
context: "(not a git repository)",
identity: "",
};
}
try {
execSync("git rev-parse --git-dir", { cwd, stdio: "pipe" });
const branch = execSync("git branch --show-current", {
cwd,
encoding: "utf-8",
}).trim();
const mainBranch = execSync(
"git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's@^refs/remotes/origin/@@' || echo 'main'",
{ cwd, encoding: "utf-8", shell: "/bin/bash" },
).trim();
const status = execSync("git status --short", {
cwd,
encoding: "utf-8",
}).trim();
const recentCommits = execSync(
"git log --oneline -10 2>/dev/null || echo 'No commits yet'",
{ cwd, encoding: "utf-8" },
).trim();
return `
- branch: ${branch}
- main: ${mainBranch}
- status: ${status || "(clean)"}
return {
context: `
- branch: ${git.branch ?? "(unknown)"}
- status: ${git.status || "(clean)"}
Recent commits:
${recentCommits}
`;
} catch {
return "(not a git repository)";
}
${git.recentCommits || "No commits yet"}
`,
identity: git.gitUser ?? "",
};
} catch {
// execSync import failed (shouldn't happen with static import, but be safe)
return "";
return {
context: "",
identity: "",
};
}
}
// ── Shallow init (background subagent) ───────────────────
/** Gather git identity for the local user. */
function gatherGitIdentity(): string {
const cwd = process.cwd();
try {
const userName = execSync("git config user.name 2>/dev/null || true", {
cwd,
encoding: "utf-8",
}).trim();
const userEmail = execSync("git config user.email 2>/dev/null || true", {
cwd,
encoding: "utf-8",
}).trim();
if (userName || userEmail) return `${userName} <${userEmail}>`;
return "";
} catch {
return "";
}
}
/** Read existing memory files from the local filesystem. */
function gatherExistingMemory(agentId: string): string {
const systemDir = getMemorySystemDir(agentId);
@@ -258,8 +228,7 @@ export async function fireAutoInit(
if (hasActiveInitSubagent()) return false;
if (!settingsManager.isMemfsEnabled(agentId)) return false;
const gitContext = gatherGitContext();
const gitIdentity = gatherGitIdentity();
const gitDetails = gatherInitGitContext();
const existingMemory = gatherExistingMemory(agentId);
const dirListing = gatherDirListing();
@@ -267,8 +236,8 @@ export async function fireAutoInit(
agentId,
workingDirectory: process.cwd(),
memoryDir: getMemoryFilesystemRoot(agentId),
gitContext,
gitIdentity,
gitContext: gitDetails.context,
gitIdentity: gitDetails.identity,
existingMemory,
dirListing,
});

View File

@@ -2,10 +2,10 @@
// Generates session context system reminder for the first message of each CLI session
// Contains device/environment information only. Agent metadata is in agentMetadata.ts.
import { execSync } from "node:child_process";
import { platform } from "node:os";
import { SYSTEM_REMINDER_CLOSE, SYSTEM_REMINDER_OPEN } from "../../constants";
import { getVersion } from "../../version";
import { gatherGitContextSnapshot } from "./gitContext";
/**
* Get the current local time in a human-readable format
@@ -40,17 +40,6 @@ export function getDeviceType(): string {
}
}
/**
* Safely execute a git command, returning null on failure
*/
function safeGitExec(command: string, cwd: string): string | null {
try {
return execSync(command, { cwd, encoding: "utf-8", stdio: "pipe" }).trim();
} catch {
return null;
}
}
/**
* Gather git information if in a git repository
* Returns truncated commits (3) and status (20 lines)
@@ -61,41 +50,25 @@ function getGitInfo(): {
branch?: string;
recentCommits?: string;
status?: string;
gitUser?: string;
} {
const cwd = process.cwd();
const git = gatherGitContextSnapshot({
recentCommitLimit: 3,
recentCommitFormat: "%h %s (%an)",
statusLineLimit: 20,
});
try {
// Check if we're in a git repo
execSync("git rev-parse --git-dir", { cwd, stdio: "pipe" });
// Get current branch (with fallback)
const branch = safeGitExec("git branch --show-current", cwd) ?? "(unknown)";
// Get recent commits (3 commits with author, with fallback)
const recentCommits =
safeGitExec('git log --format="%h %s (%an)" -3', cwd) ??
"(failed to get commits)";
// Get git status (truncate to 20 lines, with fallback)
const fullStatus =
safeGitExec("git status --short", cwd) ?? "(failed to get status)";
const statusLines = fullStatus.split("\n");
let status = fullStatus;
if (statusLines.length > 20) {
status =
statusLines.slice(0, 20).join("\n") +
`\n... and ${statusLines.length - 20} more files`;
}
return {
isGitRepo: true,
branch,
recentCommits,
status: status || "(clean working tree)",
};
} catch {
if (!git.isGitRepo) {
return { isGitRepo: false };
}
return {
isGitRepo: true,
branch: git.branch ?? "(unknown)",
recentCommits: git.recentCommits ?? "(failed to get commits)",
status: git.status || "(clean working tree)",
gitUser: git.gitUser ?? "(not configured)",
};
}
/**
@@ -146,6 +119,7 @@ The user has just initiated a new connection via the [Letta Code CLI client](htt
// Add git info if available
if (gitInfo.isGitRepo) {
context += `- **Git repository**: Yes (branch: ${gitInfo.branch})
- **Git user**: ${gitInfo.gitUser}
### Recent Commits
\`\`\`

View File

@@ -40,7 +40,7 @@ describe("init wiring", () => {
const helperSource = readSource("../../cli/helpers/initCommand.ts");
expect(helperSource).toContain("export function hasActiveInitSubagent(");
expect(helperSource).toContain("export function gatherGitContext()");
expect(helperSource).toContain("export function gatherInitGitContext()");
expect(helperSource).toContain("export function buildShallowInitPrompt(");
expect(helperSource).toContain("export function buildInitMessage(");
});